1 /*
2 * Copyright 2021-2022 The OSHI Project Contributors
3 * SPDX-License-Identifier: MIT
4 */
5 package oshi.driver.unix.openbsd.disk;
6
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import oshi.annotation.concurrent.ThreadSafe;
11 import oshi.hardware.HWPartition;
12 import oshi.util.Constants;
13 import oshi.util.ExecutingCommand;
14 import oshi.util.ParseUtil;
15 import oshi.util.tuples.Pair;
16 import oshi.util.tuples.Quartet;
17
18 /**
19 * Utility class parsing partition information from disklabel command
20 */
21 @ThreadSafe
22 public final class Disklabel {
23
24 private Disklabel() {
25 }
26
27 /**
28 * Gets disk and partition information
29 *
30 * @param diskName The disk to fetch partition information from
31 * @return A quartet containing the disk's name/label, DUID, size, and a list of partitions
32 */
33 public static Quartet<String, String, Long, List<HWPartition>> getDiskParams(String diskName) {
34 // disklabel (requires root) supports 15 configurable partitions, `a' through
35 // `p', excluding `c'.
36 // The `c' partition describes the entire physical disk.
37 // By convention, the `a' partition of the boot disk is the root
38 // partition, and the `b' partition of the boot disk is the swap partition,
39 // and the 'i' partition is usually the boot record
40
41 // Create a list for all the other partitions
42 List<HWPartition> partitions = new ArrayList<>();
43 // Save some values to return to the caller to populate HWDiskStore values
44 String totalMarker = "total sectors:";
45 long totalSectors = 1L;
46 String bpsMarker = "bytes/sector:";
47 int bytesPerSector = 1;
48 String labelMarker = "label:";
49 String label = "";
50 String duidMarker = "duid:";
51 String duid = "";
52 for (String line : ExecutingCommand.runNative("disklabel -n " + diskName)) {
53 // Check for values in the header we need for the HWDiskstore
54 // # /dev/rsd1c:
55 // type: SCSI
56 // disk: SCSI disk
57 // label: Storage Device
58 // duid: 0000000000000000
59 // flags:
60 // bytes/sector: 512
61 // sectors/track: 63
62 // tracks/cylinder: 255
63 // sectors/cylinder: 16065
64 // cylinders: 976
65 // total sectors: 15693824
66 // boundstart: 0
67 // boundend: 15693824
68 // drivedata: 0
69 if (line.contains(totalMarker)) {
70 totalSectors = ParseUtil.getFirstIntValue(line);
71 } else if (line.contains(bpsMarker)) {
72 bytesPerSector = ParseUtil.getFirstIntValue(line);
73 } else if (line.contains(labelMarker)) {
74 label = line.split(labelMarker)[1].trim();
75 } else if (line.contains(duidMarker)) {
76 duid = line.split(duidMarker)[1].trim();
77 }
78 /*-
79 16 partitions:
80 # size offset fstype [fsize bsize cpg]
81 a: 2097152 1024 4.2BSD 2048 16384 12958 # /
82 b: 17023368 2098176 swap # none
83 c: 500118192 0 unused
84 d: 8388576 19121568 4.2BSD 2048 16384 12958 # /tmp
85 e: 41386752 27510144 4.2BSD 2048 16384 12958 # /var
86 f: 4194304 68896896 4.2BSD 2048 16384 12958 # /usr
87 g: 2097152 73091200 4.2BSD 2048 16384 12958 # /usr/X11R6
88 h: 20971520 75188352 4.2BSD 2048 16384 12958 # /usr/local
89 i: 960 64 MSDOS
90 j: 4194304 96159872 4.2BSD 2048 16384 12958 # /usr/src
91 k: 12582912 100354176 4.2BSD 2048 16384 12958 # /usr/obj
92 l: 387166336 112937088 4.2BSD 4096 32768 26062 # /home
93 Note size is in sectors
94 */
95 if (line.trim().indexOf(':') == 1) {
96 // partition table values have a single letter followed by a colon
97 String[] split = ParseUtil.whitespaces.split(line.trim(), 9);
98 String name = split[0].substring(0, 1);
99 // get major and minor from stat
100 Pair<Integer, Integer> majorMinor = getMajorMinor(diskName, name);
101 // add partitions
102 if (split.length > 4) {
103 partitions.add(new HWPartition(diskName + name, name, split[3], duid + "." + name,
104 ParseUtil.parseLongOrDefault(split[1], 0L) * bytesPerSector, majorMinor.getA(),
105 majorMinor.getB(), split.length > 5 ? split[split.length - 1] : ""));
106 }
107 }
108 }
109 if (partitions.isEmpty()) {
110 return getDiskParamsNoRoot(diskName);
111 }
112 return new Quartet<>(label, duid, totalSectors * bytesPerSector, partitions);
113 }
114
115 private static Quartet<String, String, Long, List<HWPartition>> getDiskParamsNoRoot(String diskName) {
116 List<HWPartition> partitions = new ArrayList<>();
117 for (String line : ExecutingCommand.runNative("df")) {
118 if (line.startsWith("/dev/" + diskName)) {
119 String[] split = ParseUtil.whitespaces.split(line);
120 String name = split[0].substring(5 + diskName.length());
121 Pair<Integer, Integer> majorMinor = getMajorMinor(diskName, name);
122 if (split.length > 5) {
123 long partSize = ParseUtil.parseLongOrDefault(split[1], 1L) * 512L;
124 partitions.add(new HWPartition(split[0], split[0].substring(5), Constants.UNKNOWN,
125 Constants.UNKNOWN, partSize, majorMinor.getA(), majorMinor.getB(), split[5]));
126 }
127 }
128 }
129 return new Quartet<>(Constants.UNKNOWN, Constants.UNKNOWN, 0L, partitions);
130 }
131
132 private static Pair<Integer, Integer> getMajorMinor(String diskName, String name) {
133 int major = 0;
134 int minor = 0;
135 String majorMinor = ExecutingCommand.getFirstAnswer("stat -f %Hr,%Lr /dev/" + diskName + name);
136 int comma = majorMinor.indexOf(',');
137 if (comma > 0 && comma < majorMinor.length()) {
138 major = ParseUtil.parseIntOrDefault(majorMinor.substring(0, comma), 0);
139 minor = ParseUtil.parseIntOrDefault(majorMinor.substring(comma + 1), 0);
140 }
141 return new Pair<>(major, minor);
142 }
143 }