View Javadoc
1   /*
2    * Copyright 2021-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.platform.unix.openbsd;
6   
7   import static oshi.util.Memoizer.defaultExpiration;
8   import static oshi.util.Memoizer.memoize;
9   
10  import java.util.ArrayList;
11  import java.util.List;
12  import java.util.function.Supplier;
13  import java.util.regex.Matcher;
14  import java.util.regex.Pattern;
15  
16  import oshi.annotation.concurrent.ThreadSafe;
17  import oshi.driver.unix.openbsd.disk.Disklabel;
18  import oshi.hardware.HWDiskStore;
19  import oshi.hardware.HWPartition;
20  import oshi.hardware.common.AbstractHWDiskStore;
21  import oshi.util.ExecutingCommand;
22  import oshi.util.ParseUtil;
23  import oshi.util.platform.unix.openbsd.OpenBsdSysctlUtil;
24  import oshi.util.tuples.Quartet;
25  
26  /**
27   * OpenBSD hard disk implementation.
28   */
29  @ThreadSafe
30  public final class OpenBsdHWDiskStore extends AbstractHWDiskStore {
31  
32      private final Supplier<List<String>> iostat = memoize(OpenBsdHWDiskStore::querySystatIostat, defaultExpiration());
33  
34      private long reads = 0L;
35      private long readBytes = 0L;
36      private long writes = 0L;
37      private long writeBytes = 0L;
38      private long currentQueueLength = 0L;
39      private long transferTime = 0L;
40      private long timeStamp = 0L;
41      private List<HWPartition> partitionList;
42  
43      private OpenBsdHWDiskStore(String name, String model, String serial, long size) {
44          super(name, model, serial, size);
45      }
46  
47      /**
48       * Gets the disks on this machine.
49       *
50       * @return a list of {@link HWDiskStore} objects representing the disks
51       */
52      public static List<HWDiskStore> getDisks() {
53          List<HWDiskStore> diskList = new ArrayList<>();
54          List<String> dmesg = null; // Lazily fetch in loop if needed
55  
56          // Get list of disks from sysctl
57          // hw.disknames=sd0:2cf69345d371cd82,cd0:,sd1:
58          String[] devices = OpenBsdSysctlUtil.sysctl("hw.disknames", "").split(",");
59          OpenBsdHWDiskStore store;
60          String diskName;
61          for (String device : devices) {
62              diskName = device.split(":")[0];
63              // get partitions using disklabel command (requires root)
64              Quartet<String, String, Long, List<HWPartition>> diskdata = Disklabel.getDiskParams(diskName);
65              String model = diskdata.getA();
66              long size = diskdata.getC();
67              if (size <= 1) {
68                  if (dmesg == null) {
69                      dmesg = ExecutingCommand.runNative("dmesg");
70                  }
71                  Pattern diskAt = Pattern.compile(diskName + " at .*<(.+)>.*");
72                  Pattern diskMB = Pattern
73                          .compile(diskName + ":.* (\\d+)MB, (?:(\\d+) bytes\\/sector, )?(?:(\\d+) sectors).*");
74                  for (String line : dmesg) {
75                      Matcher m = diskAt.matcher(line);
76                      if (m.matches()) {
77                          model = m.group(1);
78                      }
79                      m = diskMB.matcher(line);
80                      if (m.matches()) {
81                          // Group 3 is sectors
82                          long sectors = ParseUtil.parseLongOrDefault(m.group(3), 0L);
83                          // Group 2 is optional capture of bytes per sector
84                          long bytesPerSector = ParseUtil.parseLongOrDefault(m.group(2), 0L);
85                          if (bytesPerSector == 0 && sectors > 0) {
86                              // if we don't have bytes per sector guess at it based on total size and number
87                              // of sectors
88                              // Group 1 is size in MB, which may round
89                              size = ParseUtil.parseLongOrDefault(m.group(1), 0L) << 20;
90                              // Estimate bytes per sector. Should be "near" a power of 2
91                              bytesPerSector = size / sectors;
92                              // Multiply by 1.5 and round down to nearest power of 2:
93                              bytesPerSector = Long.highestOneBit(bytesPerSector + bytesPerSector >> 1);
94                          }
95                          size = bytesPerSector * sectors;
96                          break;
97                      }
98                  }
99              }
100             store = new OpenBsdHWDiskStore(diskName, model, diskdata.getB(), size);
101             store.partitionList = diskdata.getD();
102             store.updateAttributes();
103 
104             diskList.add(store);
105         }
106         return diskList;
107     }
108 
109     @Override
110     public long getReads() {
111         return reads;
112     }
113 
114     @Override
115     public long getReadBytes() {
116         return readBytes;
117     }
118 
119     @Override
120     public long getWrites() {
121         return writes;
122     }
123 
124     @Override
125     public long getWriteBytes() {
126         return writeBytes;
127     }
128 
129     @Override
130     public long getCurrentQueueLength() {
131         return currentQueueLength;
132     }
133 
134     @Override
135     public long getTransferTime() {
136         return transferTime;
137     }
138 
139     @Override
140     public long getTimeStamp() {
141         return timeStamp;
142     }
143 
144     @Override
145     public List<HWPartition> getPartitions() {
146         return this.partitionList;
147     }
148 
149     @Override
150     public boolean updateAttributes() {
151         /*-
152         └─ $ ▶ systat -b iostat
153                 0 users Load 2.04 4.02 3.96                          thinkpad.local 00:14:35
154                 DEVICE          READ    WRITE     RTPS    WTPS     SEC            STATS
155                 sd0           49937M   25774M  1326555 1695370   945.9
156                 cd0                0        0        0       0     0.0
157                 sd1          1573888      204       29       0     0.1
158                 Totals        49939M   25774M  1326585 1695371   946.0
159                                                                                126568 total pages
160                                                                                126568 dma pages
161                                                                                   100 dirty pages
162                                                                                    14 delwri bufs
163                                                                                     0 busymap bufs
164                                                                                  6553 avail kvaslots
165                                                                                  6553 kvaslots
166                                                                                     0 pending writes
167                                                                                    12 pending reads
168                                                                                     0 cache hits
169                                                                                     0 high flips
170                                                                                     0 high flops
171                                                                                     0 dma flips
172         */
173         long now = System.currentTimeMillis();
174         boolean diskFound = false;
175         for (String line : iostat.get()) {
176             String[] split = ParseUtil.whitespaces.split(line);
177             if (split.length < 7 && split[0].equals(getName())) {
178                 diskFound = true;
179                 this.readBytes = ParseUtil.parseMultipliedToLongs(split[1]);
180                 this.writeBytes = ParseUtil.parseMultipliedToLongs(split[2]);
181                 this.reads = (long) ParseUtil.parseDoubleOrDefault(split[3], 0d);
182                 this.writes = (long) ParseUtil.parseDoubleOrDefault(split[4], 0d);
183                 // In seconds, multiply for ms
184                 this.transferTime = (long) (ParseUtil.parseDoubleOrDefault(split[5], 0d) * 1000);
185                 this.timeStamp = now;
186             }
187         }
188         return diskFound;
189     }
190 
191     private static List<String> querySystatIostat() {
192         return ExecutingCommand.runNative("systat -ab iostat");
193     }
194 }