1
2
3
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.ArrayList;
11 import java.util.Arrays;
12 import java.util.Collections;
13 import java.util.Comparator;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.stream.Collectors;
18
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 import com.sun.jna.platform.linux.Udev;
23 import com.sun.jna.platform.linux.Udev.UdevContext;
24 import com.sun.jna.platform.linux.Udev.UdevDevice;
25 import com.sun.jna.platform.linux.Udev.UdevEnumerate;
26 import com.sun.jna.platform.linux.Udev.UdevListEntry;
27
28 import oshi.annotation.concurrent.ThreadSafe;
29 import oshi.hardware.HWDiskStore;
30 import oshi.hardware.HWPartition;
31 import oshi.hardware.common.AbstractHWDiskStore;
32 import oshi.util.Constants;
33 import oshi.util.FileUtil;
34 import oshi.util.ParseUtil;
35 import oshi.util.platform.linux.DevPath;
36 import oshi.util.platform.linux.ProcPath;
37
38
39
40
41 @ThreadSafe
42 public final class LinuxHWDiskStore extends AbstractHWDiskStore {
43
44 private static final Logger LOG = LoggerFactory.getLogger(LinuxHWDiskStore.class);
45
46 private static final String BLOCK = "block";
47 private static final String DISK = "disk";
48 private static final String PARTITION = "partition";
49
50 private static final String STAT = "stat";
51 private static final String SIZE = "size";
52 private static final String MINOR = "MINOR";
53 private static final String MAJOR = "MAJOR";
54
55 private static final String ID_FS_TYPE = "ID_FS_TYPE";
56 private static final String ID_FS_UUID = "ID_FS_UUID";
57 private static final String ID_MODEL = "ID_MODEL";
58 private static final String ID_SERIAL_SHORT = "ID_SERIAL_SHORT";
59
60 private static final String DM_UUID = "DM_UUID";
61 private static final String DM_VG_NAME = "DM_VG_NAME";
62 private static final String DM_LV_NAME = "DM_LV_NAME";
63 private static final String LOGICAL_VOLUME_GROUP = "Logical Volume Group";
64
65 private static final int SECTORSIZE = 512;
66
67
68 private static final int[] UDEV_STAT_ORDERS = new int[UdevStat.values().length];
69 static {
70 for (UdevStat stat : UdevStat.values()) {
71 UDEV_STAT_ORDERS[stat.ordinal()] = stat.getOrder();
72 }
73 }
74
75
76
77 private static final int UDEV_STAT_LENGTH;
78 static {
79 String stat = FileUtil.getStringFromFile(ProcPath.DISKSTATS);
80 int statLength = 11;
81 if (!stat.isEmpty()) {
82 statLength = ParseUtil.countStringToLongArray(stat, ' ');
83 }
84 UDEV_STAT_LENGTH = statLength;
85 }
86
87 private long reads = 0L;
88 private long readBytes = 0L;
89 private long writes = 0L;
90 private long writeBytes = 0L;
91 private long currentQueueLength = 0L;
92 private long transferTime = 0L;
93 private long timeStamp = 0L;
94 private List<HWPartition> partitionList = new ArrayList<>();
95
96 private LinuxHWDiskStore(String name, String model, String serial, long size) {
97 super(name, model, serial, size);
98 }
99
100 @Override
101 public long getReads() {
102 return reads;
103 }
104
105 @Override
106 public long getReadBytes() {
107 return readBytes;
108 }
109
110 @Override
111 public long getWrites() {
112 return writes;
113 }
114
115 @Override
116 public long getWriteBytes() {
117 return writeBytes;
118 }
119
120 @Override
121 public long getCurrentQueueLength() {
122 return currentQueueLength;
123 }
124
125 @Override
126 public long getTransferTime() {
127 return transferTime;
128 }
129
130 @Override
131 public long getTimeStamp() {
132 return timeStamp;
133 }
134
135 @Override
136 public List<HWPartition> getPartitions() {
137 return this.partitionList;
138 }
139
140
141
142
143
144
145 public static List<HWDiskStore> getDisks() {
146 return getDisks(null);
147 }
148
149 private static List<HWDiskStore> getDisks(LinuxHWDiskStore storeToUpdate) {
150 if (!HAS_UDEV) {
151 LOG.warn("Disk Store information requires libudev, which is not present.");
152 return Collections.emptyList();
153 }
154 LinuxHWDiskStore store = null;
155 List<HWDiskStore> result = new ArrayList<>();
156
157 Map<String, String> mountsMap = readMountsMap();
158
159 UdevContext udev = Udev.INSTANCE.udev_new();
160 try {
161 UdevEnumerate enumerate = udev.enumerateNew();
162 try {
163 enumerate.addMatchSubsystem(BLOCK);
164 enumerate.scanDevices();
165 for (UdevListEntry entry = enumerate.getListEntry(); entry != null; entry = entry.getNext()) {
166 String syspath = entry.getName();
167 UdevDevice device = udev.deviceNewFromSyspath(syspath);
168 if (device != null) {
169 try {
170
171 String devnode = device.getDevnode();
172
173 if (devnode != null && !devnode.startsWith(DevPath.LOOP)
174 && !devnode.startsWith(DevPath.RAM)) {
175 if (DISK.equals(device.getDevtype())) {
176
177 String devModel = device.getPropertyValue(ID_MODEL);
178 String devSerial = device.getPropertyValue(ID_SERIAL_SHORT);
179 long devSize = ParseUtil.parseLongOrDefault(device.getSysattrValue(SIZE), 0L)
180 * SECTORSIZE;
181 if (devnode.startsWith(DevPath.DM)) {
182 devModel = LOGICAL_VOLUME_GROUP;
183 devSerial = device.getPropertyValue(DM_UUID);
184 store = new LinuxHWDiskStore(devnode, devModel,
185 devSerial == null ? Constants.UNKNOWN : devSerial, devSize);
186 String vgName = device.getPropertyValue(DM_VG_NAME);
187 String lvName = device.getPropertyValue(DM_LV_NAME);
188 store.partitionList.add(new HWPartition(
189 getPartitionNameForDmDevice(vgName, lvName), device.getSysname(),
190 device.getPropertyValue(ID_FS_TYPE) == null ? PARTITION
191 : device.getPropertyValue(ID_FS_TYPE),
192 device.getPropertyValue(ID_FS_UUID) == null ? ""
193 : device.getPropertyValue(ID_FS_UUID),
194 ParseUtil.parseLongOrDefault(device.getSysattrValue(SIZE), 0L)
195 * SECTORSIZE,
196 ParseUtil.parseIntOrDefault(device.getPropertyValue(MAJOR), 0),
197 ParseUtil.parseIntOrDefault(device.getPropertyValue(MINOR), 0),
198 getMountPointOfDmDevice(vgName, lvName)));
199 } else {
200 store = new LinuxHWDiskStore(devnode,
201 devModel == null ? Constants.UNKNOWN : devModel,
202 devSerial == null ? Constants.UNKNOWN : devSerial, devSize);
203 }
204 if (storeToUpdate == null) {
205
206 computeDiskStats(store, device.getSysattrValue(STAT));
207 result.add(store);
208 } else if (store.getName().equals(storeToUpdate.getName())
209 && store.getModel().equals(storeToUpdate.getModel())
210 && store.getSerial().equals(storeToUpdate.getSerial())
211 && store.getSize() == storeToUpdate.getSize()) {
212
213
214
215 computeDiskStats(storeToUpdate, device.getSysattrValue(STAT));
216 result.add(storeToUpdate);
217 break;
218 }
219 } else if (storeToUpdate == null && store != null
220 && PARTITION.equals(device.getDevtype())) {
221
222
223 UdevDevice parent = device.getParentWithSubsystemDevtype(BLOCK, DISK);
224 if (parent != null && store.getName().equals(parent.getDevnode())) {
225
226
227 String name = device.getDevnode();
228 store.partitionList.add(new HWPartition(name, device.getSysname(),
229 device.getPropertyValue(ID_FS_TYPE) == null ? PARTITION
230 : device.getPropertyValue(ID_FS_TYPE),
231 device.getPropertyValue(ID_FS_UUID) == null ? ""
232 : device.getPropertyValue(ID_FS_UUID),
233 ParseUtil.parseLongOrDefault(device.getSysattrValue(SIZE), 0L)
234 * SECTORSIZE,
235 ParseUtil.parseIntOrDefault(device.getPropertyValue(MAJOR), 0),
236 ParseUtil.parseIntOrDefault(device.getPropertyValue(MINOR), 0),
237 mountsMap.getOrDefault(name,
238 getDependentNamesFromHoldersDirectory(device.getSysname()))));
239 }
240 }
241 }
242 } finally {
243 device.unref();
244 }
245 }
246 }
247 } finally {
248 enumerate.unref();
249 }
250 } finally {
251 udev.unref();
252 }
253
254 for (HWDiskStore hwds : result) {
255 ((LinuxHWDiskStore) hwds).partitionList = Collections.unmodifiableList(hwds.getPartitions().stream()
256 .sorted(Comparator.comparing(HWPartition::getName)).collect(Collectors.toList()));
257 }
258 return result;
259 }
260
261 @Override
262 public boolean updateAttributes() {
263
264
265 return !getDisks(this).isEmpty();
266 }
267
268 private static Map<String, String> readMountsMap() {
269 Map<String, String> mountsMap = new HashMap<>();
270 List<String> mounts = FileUtil.readFile(ProcPath.MOUNTS);
271 for (String mount : mounts) {
272 String[] split = ParseUtil.whitespaces.split(mount);
273 if (split.length < 2 || !split[0].startsWith(DevPath.DEV)) {
274 continue;
275 }
276 mountsMap.put(split[0], split[1]);
277 }
278 return mountsMap;
279 }
280
281 private static void computeDiskStats(LinuxHWDiskStore store, String devstat) {
282 long[] devstatArray = ParseUtil.parseStringToLongArray(devstat, UDEV_STAT_ORDERS, UDEV_STAT_LENGTH, ' ');
283 store.timeStamp = System.currentTimeMillis();
284
285
286 store.reads = devstatArray[UdevStat.READS.ordinal()];
287 store.readBytes = devstatArray[UdevStat.READ_BYTES.ordinal()] * SECTORSIZE;
288 store.writes = devstatArray[UdevStat.WRITES.ordinal()];
289 store.writeBytes = devstatArray[UdevStat.WRITE_BYTES.ordinal()] * SECTORSIZE;
290 store.currentQueueLength = devstatArray[UdevStat.QUEUE_LENGTH.ordinal()];
291 store.transferTime = devstatArray[UdevStat.ACTIVE_MS.ordinal()];
292 }
293
294 private static String getPartitionNameForDmDevice(String vgName, String lvName) {
295 return new StringBuilder().append(DevPath.DEV).append(vgName).append('/').append(lvName).toString();
296 }
297
298 private static String getMountPointOfDmDevice(String vgName, String lvName) {
299 return new StringBuilder().append(DevPath.MAPPER).append(vgName).append('-').append(lvName).toString();
300 }
301
302 private static String getDependentNamesFromHoldersDirectory(String sysPath) {
303 File holdersDir = new File(sysPath + "/holders");
304 File[] holders = holdersDir.listFiles();
305 if (holders != null) {
306 return Arrays.stream(holders).map(File::getName).collect(Collectors.joining(" "));
307 }
308 return "";
309 }
310
311
312 enum UdevStat {
313
314
315 READS(0), READ_BYTES(2), WRITES(4), WRITE_BYTES(6), QUEUE_LENGTH(8), ACTIVE_MS(9);
316
317 private int order;
318
319 public int getOrder() {
320 return this.order;
321 }
322
323 UdevStat(int order) {
324 this.order = order;
325 }
326 }
327 }