1 /*
2 * Copyright 2020-2022 The OSHI Project Contributors
3 * SPDX-License-Identifier: MIT
4 */
5 package oshi.driver.unix.aix;
6
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.Comparator;
10 import java.util.HashMap;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.concurrent.ConcurrentHashMap;
15 import java.util.stream.Collectors;
16
17 import oshi.annotation.concurrent.ThreadSafe;
18 import oshi.hardware.HWPartition;
19 import oshi.util.ExecutingCommand;
20 import oshi.util.ParseUtil;
21 import oshi.util.tuples.Pair;
22
23 /**
24 * Utility to query lspv
25 */
26 @ThreadSafe
27 public final class Lspv {
28
29 /**
30 * The lspv command incurs a lot of disk reads. Since partitions shouldn't change during operation, cache the result
31 * here.
32 */
33 private static final Map<String, List<HWPartition>> PARTITION_CACHE = new ConcurrentHashMap<>();
34
35 private Lspv() {
36 }
37
38 /**
39 * Query {@code lspv} to get partition info, or return a cached value.
40 *
41 * @param device The disk to get the volumes from.
42 * @param majMinMap A map of device name to a pair with major and minor numbers.
43 *
44 * @return A list of logical volumes (partitions) on this device.
45 */
46 public static List<HWPartition> queryLogicalVolumes(String device, Map<String, Pair<Integer, Integer>> majMinMap) {
47 return PARTITION_CACHE.computeIfAbsent(device,
48 d -> Collections.unmodifiableList(computeLogicalVolumes(d, majMinMap).stream()
49 .sorted(Comparator.comparing(HWPartition::getMinor).thenComparing(HWPartition::getName))
50 .collect(Collectors.toList())));
51 }
52
53 private static List<HWPartition> computeLogicalVolumes(String device,
54 Map<String, Pair<Integer, Integer>> majMinMap) {
55 List<HWPartition> partitions = new ArrayList<>();
56 /*-
57 $ lspv -L hdisk0
58 PHYSICAL VOLUME: hdisk0 VOLUME GROUP: rootvg
59 PV IDENTIFIER: 000acfde95524f85 VG IDENTIFIER 000acfde00004c000000000395525276
60 PV STATE: active
61 STALE PARTITIONS: 0 ALLOCATABLE: yes
62 PP SIZE: 128 megabyte(s) LOGICAL VOLUMES: 12
63 TOTAL PPs: 271 (34688 megabytes) VG DESCRIPTORS: 2
64 FREE PPs: 227 (29056 megabytes) HOT SPARE: no
65 USED PPs: 44 (5632 megabytes) MAX REQUEST: 256 kilobytes
66 FREE DISTRIBUTION: 54..46..19..54..54
67 USED DISTRIBUTION: 01..08..35..00..00
68 */
69 String stateMarker = "PV STATE:";
70 String sizeMarker = "PP SIZE:";
71 long ppSize = 0L; // All physical partitions are the same size
72 for (String s : ExecutingCommand.runNative("lspv -L " + device)) {
73 if (s.startsWith(stateMarker)) {
74 if (!s.contains("active")) {
75 return partitions;
76 }
77 } else if (s.contains(sizeMarker)) {
78 ppSize = ParseUtil.getFirstIntValue(s);
79 }
80 }
81 if (ppSize == 0L) {
82 return partitions;
83 }
84 // Convert to megabytes
85 ppSize <<= 20;
86 /*-
87 $ lspv -p hdisk0
88 hdisk0:
89 PP RANGE STATE REGION LV NAME TYPE MOUNT POINT
90 1-1 used outer edge hd5 boot N/A
91 2-55 free outer edge
92 56-59 used outer middle hd6 paging N/A
93 60-61 used outer middle livedump jfs2 /var/adm/ras/livedump
94 62-62 used outer middle loglv01 jfslog N/A
95 63-63 used outer middle lv01 jfs N/A
96 64-109 free outer middle
97 110-110 used center hd8 jfs2log N/A
98 111-112 used center hd4 jfs2 /
99 113-128 used center hd2 jfs2 /usr
100 129-131 used center hd9var jfs2 /var
101 132-132 used center hd3 jfs2 /tmp
102 133-133 used center hd9var jfs2 /var
103 134-136 used center hd10opt jfs2 /opt
104 137-137 used center hd11admin jfs2 /admin
105 138-140 used center hd2 jfs2 /usr
106 141-141 used center hd3 jfs2 /tmp
107 142-142 used center hd4 jfs2 /
108 143-143 used center hd9var jfs2 /var
109 144-144 used center hd2 jfs2 /usr
110 145-163 free center
111 164-217 free inner middle
112 218-271 free inner edge
113 */
114 Map<String, String> mountMap = new HashMap<>();
115 Map<String, String> typeMap = new HashMap<>();
116 Map<String, Integer> ppMap = new HashMap<>();
117 for (String s : ExecutingCommand.runNative("lspv -p " + device)) {
118 String[] split = ParseUtil.whitespaces.split(s.trim());
119 if (split.length >= 6 && "used".equals(split[1])) {
120 // Region may have two words, so count from end
121 String name = split[split.length - 3];
122 mountMap.put(name, split[split.length - 1]);
123 typeMap.put(name, split[split.length - 2]);
124 int ppCount = 1 + ParseUtil.getNthIntValue(split[0], 2) - ParseUtil.getNthIntValue(split[0], 1);
125 ppMap.put(name, ppCount + ppMap.getOrDefault(name, 0));
126 }
127 }
128 for (Entry<String, String> entry : mountMap.entrySet()) {
129 String mount = "N/A".equals(entry.getValue()) ? "" : entry.getValue();
130 // All maps should have same keys
131 String name = entry.getKey();
132 String type = typeMap.get(name);
133 long size = ppSize * ppMap.get(name);
134 Pair<Integer, Integer> majMin = majMinMap.get(name);
135 int major = majMin == null ? ParseUtil.getFirstIntValue(name) : majMin.getA();
136 int minor = majMin == null ? ParseUtil.getFirstIntValue(name) : majMin.getB();
137 partitions.add(new HWPartition(name, name, type, "", size, major, minor, mount));
138 }
139 return partitions;
140 }
141 }