View Javadoc
1   /*
2    * Copyright 2021-2024 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.platform.linux;
6   
7   import static oshi.software.os.linux.LinuxOperatingSystem.HAS_UDEV;
8   
9   import java.io.File;
10  import java.util.Collections;
11  import java.util.HashMap;
12  import java.util.HashSet;
13  import java.util.List;
14  import java.util.Map;
15  import java.util.Set;
16  import java.util.stream.Collectors;
17  
18  import org.slf4j.Logger;
19  import org.slf4j.LoggerFactory;
20  
21  import com.sun.jna.platform.linux.Udev;
22  
23  import oshi.hardware.LogicalVolumeGroup;
24  import oshi.hardware.common.AbstractLogicalVolumeGroup;
25  import oshi.util.ExecutingCommand;
26  import oshi.util.ParseUtil;
27  import oshi.util.Util;
28  import oshi.util.platform.linux.DevPath;
29  
30  final class LinuxLogicalVolumeGroup extends AbstractLogicalVolumeGroup {
31  
32      private static final Logger LOG = LoggerFactory.getLogger(LinuxLogicalVolumeGroup.class);
33  
34      private static final String BLOCK = "block";
35      private static final String DM_UUID = "DM_UUID";
36      private static final String DM_VG_NAME = "DM_VG_NAME";
37      private static final String DM_LV_NAME = "DM_LV_NAME";
38  
39      LinuxLogicalVolumeGroup(String name, Map<String, Set<String>> lvMap, Set<String> pvSet) {
40          super(name, lvMap, pvSet);
41      }
42  
43      static List<LogicalVolumeGroup> getLogicalVolumeGroups() {
44          if (!HAS_UDEV) {
45              LOG.warn("Logical Volume Group information requires libudev, which is not present.");
46              return Collections.emptyList();
47          }
48          Map<String, Map<String, Set<String>>> logicalVolumesMap = new HashMap<>();
49          Map<String, Set<String>> physicalVolumesMap = new HashMap<>();
50  
51          // Populate pv map from pvs command
52          // This requires elevated permissions and may fail
53          for (String s : ExecutingCommand.runNative("pvs -o vg_name,pv_name")) {
54              String[] split = ParseUtil.whitespaces.split(s.trim());
55              if (split.length == 2 && split[1].startsWith(DevPath.DEV)) {
56                  physicalVolumesMap.computeIfAbsent(split[0], k -> new HashSet<>()).add(split[1]);
57              }
58          }
59  
60          // Populate lv map from udev
61          Udev.UdevContext udev = Udev.INSTANCE.udev_new();
62          try {
63              Udev.UdevEnumerate enumerate = udev.enumerateNew();
64              try {
65                  enumerate.addMatchSubsystem(BLOCK);
66                  enumerate.scanDevices();
67                  for (Udev.UdevListEntry entry = enumerate.getListEntry(); entry != null; entry = entry.getNext()) {
68                      String syspath = entry.getName();
69                      Udev.UdevDevice device = udev.deviceNewFromSyspath(syspath);
70                      if (device != null) {
71                          try {
72                              String devnode = device.getDevnode();
73                              if (devnode != null && devnode.startsWith(DevPath.DM)) {
74                                  String uuid = device.getPropertyValue(DM_UUID);
75                                  if (uuid != null && uuid.startsWith("LVM-")) {
76                                      String vgName = device.getPropertyValue(DM_VG_NAME);
77                                      String lvName = device.getPropertyValue(DM_LV_NAME);
78                                      if (!Util.isBlank(vgName) && !Util.isBlank(lvName)) {
79                                          logicalVolumesMap.computeIfAbsent(vgName, k -> new HashMap<>());
80                                          Map<String, Set<String>> lvMapForGroup = logicalVolumesMap.get(vgName);
81                                          // Backup to add to pv set if pvs command failed
82                                          physicalVolumesMap.computeIfAbsent(vgName, k -> new HashSet<>());
83                                          Set<String> pvSetForGroup = physicalVolumesMap.get(vgName);
84  
85                                          File slavesDir = new File(syspath + "/slaves");
86                                          File[] slaves = slavesDir.listFiles();
87                                          if (slaves != null) {
88                                              for (File f : slaves) {
89                                                  String pvName = f.getName();
90                                                  lvMapForGroup.computeIfAbsent(lvName, k -> new HashSet<>())
91                                                          .add(DevPath.DEV + pvName);
92                                                  // Backup to add to pv set if pvs command failed
93                                                  // Added /dev/ to remove duplicates like /dev/sda1 and sda1
94                                                  pvSetForGroup.add(DevPath.DEV + pvName);
95                                              }
96                                          }
97                                      }
98                                  }
99                              }
100                         } finally {
101                             device.unref();
102                         }
103                     }
104                 }
105             } finally {
106                 enumerate.unref();
107             }
108         } finally {
109             udev.unref();
110         }
111         return logicalVolumesMap.entrySet().stream()
112                 .map(e -> new LinuxLogicalVolumeGroup(e.getKey(), e.getValue(), physicalVolumesMap.get(e.getKey())))
113                 .collect(Collectors.toList());
114     }
115 }