View Javadoc
1   /*
2    * Copyright 2020-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.driver.unix.solaris.disk;
6   
7   import java.util.HashMap;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import oshi.annotation.concurrent.ThreadSafe;
13  import oshi.util.ExecutingCommand;
14  import oshi.util.ParseUtil;
15  import oshi.util.tuples.Quintet;
16  
17  /**
18   * Utility to query iostat
19   */
20  @ThreadSafe
21  public final class Iostat {
22  
23      // Note uppercase E
24      private static final String IOSTAT_ER_DETAIL = "iostat -Er";
25  
26      // Note lowercase e
27      private static final String IOSTAT_ER = "iostat -er";
28      // Sample output:
29      // errors
30      // device,s/w,h/w,trn,tot
31      // cmdk0,0,0,0,0
32      // sd0,0,0,0
33  
34      // Note lowercase e
35      private static final String IOSTAT_ERN = "iostat -ern";
36      // Sample output:
37      // errors
38      // s/w,h/w,trn,tot,device
39      // 0,0,0,0,c1d0
40      // 0,0,0,0,c1t1d0
41  
42      private static final String DEVICE_HEADER = "device";
43  
44      private Iostat() {
45      }
46  
47      /**
48       * Query iostat to map partitions to mount points
49       *
50       * @return A map with partitions as the key and mount points as the value
51       */
52      public static Map<String, String> queryPartitionToMountMap() {
53          // Create map to correlate disk name with block device mount point for
54          // later use in partition info
55          Map<String, String> deviceMap = new HashMap<>();
56  
57          // First, run iostat -er to enumerate disks by name.
58          List<String> mountNames = ExecutingCommand.runNative(IOSTAT_ER);
59          // Also run iostat -ern to get the same list by mount point.
60          List<String> mountPoints = ExecutingCommand.runNative(IOSTAT_ERN);
61  
62          String disk;
63          for (int i = 0; i < mountNames.size() && i < mountPoints.size(); i++) {
64              // Map disk
65              disk = mountNames.get(i);
66              String[] diskSplit = disk.split(",");
67              if (diskSplit.length >= 5 && !DEVICE_HEADER.equals(diskSplit[0])) {
68                  String mount = mountPoints.get(i);
69                  String[] mountSplit = mount.split(",");
70                  if (mountSplit.length >= 5 && !DEVICE_HEADER.equals(mountSplit[4])) {
71                      deviceMap.put(diskSplit[0], mountSplit[4]);
72                  }
73              }
74          }
75          return deviceMap;
76      }
77  
78      /**
79       * Query iostat to map detailed drive information
80       *
81       * @param diskSet A set of valid disk names; others will be ignored
82       * @return A map with disk name as the key and a quintet of model, vendor, product, serial, size as the value
83       */
84      public static Map<String, Quintet<String, String, String, String, Long>> queryDeviceStrings(Set<String> diskSet) {
85          Map<String, Quintet<String, String, String, String, Long>> deviceParamMap = new HashMap<>();
86          // Run iostat -Er to get model, etc.
87          List<String> iostat = ExecutingCommand.runNative(IOSTAT_ER_DETAIL);
88          // We'll use Model if available, otherwise Vendor+Product
89          String diskName = null;
90          String model = "";
91          String vendor = "";
92          String product = "";
93          String serial = "";
94          long size = 0;
95          for (String line : iostat) {
96              // The -r switch enables comma delimited for easy parsing!
97              // No guarantees on which line the results appear so we'll nest
98              // a loop iterating on the comma splits
99              String[] split = line.split(",");
100             for (String keyValue : split) {
101                 keyValue = keyValue.trim();
102                 // If entry is tne name of a disk, this is beginning of new
103                 // output for that disk.
104                 if (diskSet.contains(keyValue)) {
105                     // First, if we have existing output from previous,
106                     // update
107                     if (diskName != null) {
108                         deviceParamMap.put(diskName, new Quintet<>(model, vendor, product, serial, size));
109                     }
110                     // Reset values for next iteration
111                     diskName = keyValue;
112                     model = "";
113                     vendor = "";
114                     product = "";
115                     serial = "";
116                     size = 0L;
117                     continue;
118                 }
119                 // Otherwise update variables
120                 if (keyValue.startsWith("Model:")) {
121                     model = keyValue.replace("Model:", "").trim();
122                 } else if (keyValue.startsWith("Serial No:")) {
123                     serial = keyValue.replace("Serial No:", "").trim();
124                 } else if (keyValue.startsWith("Vendor:")) {
125                     vendor = keyValue.replace("Vendor:", "").trim();
126                 } else if (keyValue.startsWith("Product:")) {
127                     product = keyValue.replace("Product:", "").trim();
128                 } else if (keyValue.startsWith("Size:")) {
129                     // Size: 1.23GB <1227563008 bytes>
130                     String[] bytes = keyValue.split("<");
131                     if (bytes.length > 1) {
132                         bytes = ParseUtil.whitespaces.split(bytes[1]);
133                         size = ParseUtil.parseLongOrDefault(bytes[0], 0L);
134                     }
135                 }
136             }
137             // At end of output update last entry
138             if (diskName != null) {
139                 deviceParamMap.put(diskName, new Quintet<>(model, vendor, product, serial, size));
140             }
141         }
142         return deviceParamMap;
143     }
144 }