View Javadoc
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 }