View Javadoc
1   /*
2    * Copyright 2020-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.platform.unix.solaris;
6   
7   import static oshi.software.os.unix.solaris.SolarisOperatingSystem.HAS_KSTAT2;
8   
9   import java.util.ArrayList;
10  import java.util.Collections;
11  import java.util.Comparator;
12  import java.util.List;
13  import java.util.Map;
14  import java.util.Map.Entry;
15  import java.util.stream.Collectors;
16  
17  import com.sun.jna.platform.unix.solaris.LibKstat.Kstat;
18  import com.sun.jna.platform.unix.solaris.LibKstat.KstatIO;
19  
20  import oshi.annotation.concurrent.ThreadSafe;
21  import oshi.driver.unix.solaris.disk.Iostat;
22  import oshi.driver.unix.solaris.disk.Lshal;
23  import oshi.driver.unix.solaris.disk.Prtvtoc;
24  import oshi.hardware.HWDiskStore;
25  import oshi.hardware.HWPartition;
26  import oshi.hardware.common.AbstractHWDiskStore;
27  import oshi.util.platform.unix.solaris.KstatUtil;
28  import oshi.util.platform.unix.solaris.KstatUtil.KstatChain;
29  import oshi.util.tuples.Quintet;
30  
31  /**
32   * Solaris hard disk implementation.
33   */
34  @ThreadSafe
35  public final class SolarisHWDiskStore extends AbstractHWDiskStore {
36  
37      private long reads = 0L;
38      private long readBytes = 0L;
39      private long writes = 0L;
40      private long writeBytes = 0L;
41      private long currentQueueLength = 0L;
42      private long transferTime = 0L;
43      private long timeStamp = 0L;
44      private List<HWPartition> partitionList;
45  
46      private SolarisHWDiskStore(String name, String model, String serial, long size) {
47          super(name, model, serial, size);
48      }
49  
50      @Override
51      public long getReads() {
52          return reads;
53      }
54  
55      @Override
56      public long getReadBytes() {
57          return readBytes;
58      }
59  
60      @Override
61      public long getWrites() {
62          return writes;
63      }
64  
65      @Override
66      public long getWriteBytes() {
67          return writeBytes;
68      }
69  
70      @Override
71      public long getCurrentQueueLength() {
72          return currentQueueLength;
73      }
74  
75      @Override
76      public long getTransferTime() {
77          return transferTime;
78      }
79  
80      @Override
81      public long getTimeStamp() {
82          return timeStamp;
83      }
84  
85      @Override
86      public List<HWPartition> getPartitions() {
87          return this.partitionList;
88      }
89  
90      @Override
91      public boolean updateAttributes() {
92          this.timeStamp = System.currentTimeMillis();
93          if (HAS_KSTAT2) {
94              // Use Kstat2 implementation
95              return updateAttributes2();
96          }
97          try (KstatChain kc = KstatUtil.openChain()) {
98              Kstat ksp = kc.lookup(null, 0, getName());
99              if (ksp != null && kc.read(ksp)) {
100                 KstatIO data = new KstatIO(ksp.ks_data);
101                 this.reads = data.reads;
102                 this.writes = data.writes;
103                 this.readBytes = data.nread;
104                 this.writeBytes = data.nwritten;
105                 this.currentQueueLength = (long) data.wcnt + data.rcnt;
106                 // rtime and snaptime are nanoseconds, convert to millis
107                 this.transferTime = data.rtime / 1_000_000L;
108                 this.timeStamp = ksp.ks_snaptime / 1_000_000L;
109                 return true;
110             }
111         }
112         return false;
113     }
114 
115     private boolean updateAttributes2() {
116         String fullName = getName();
117         String alpha = fullName;
118         String numeric = "";
119         for (int c = 0; c < fullName.length(); c++) {
120             if (fullName.charAt(c) >= '0' && fullName.charAt(c) <= '9') {
121                 alpha = fullName.substring(0, c);
122                 numeric = fullName.substring(c);
123                 break;
124             }
125         }
126         // Try device style notation
127         Object[] results = KstatUtil.queryKstat2("kstat:/disk/" + alpha + "/" + getName() + "/0", "reads", "writes",
128                 "nread", "nwritten", "wcnt", "rcnt", "rtime", "snaptime");
129         // If failure try io notation
130         if (results[results.length - 1] == null) {
131             results = KstatUtil.queryKstat2("kstat:/disk/" + alpha + "/" + numeric + "/io", "reads", "writes", "nread",
132                     "nwritten", "wcnt", "rcnt", "rtime", "snaptime");
133         }
134         if (results[results.length - 1] == null) {
135             return false;
136         }
137         this.reads = results[0] == null ? 0L : (long) results[0];
138         this.writes = results[1] == null ? 0L : (long) results[1];
139         this.readBytes = results[2] == null ? 0L : (long) results[2];
140         this.writeBytes = results[3] == null ? 0L : (long) results[3];
141         this.currentQueueLength = results[4] == null ? 0L : (long) results[4];
142         this.currentQueueLength += results[5] == null ? 0L : (long) results[5];
143         // rtime and snaptime are nanoseconds, convert to millis
144         this.transferTime = results[6] == null ? 0L : (long) results[6] / 1_000_000L;
145         this.timeStamp = (long) results[7] / 1_000_000L;
146         return true;
147     }
148 
149     /**
150      * Gets the disks on this machine
151      *
152      * @return a list of {@link HWDiskStore} objects representing the disks
153      */
154     public static List<HWDiskStore> getDisks() {
155         // Create map to correlate disk name with block device mount point for
156         // later use in partition info
157         Map<String, String> deviceMap = Iostat.queryPartitionToMountMap();
158 
159         // Create map to correlate disk name with block device mount point for
160         // later use in partition info. Run lshal, if available, to get block device
161         // major (we'll use partition # for minor)
162         Map<String, Integer> majorMap = Lshal.queryDiskToMajorMap();
163 
164         // Create map of model, vendor, product, serial, size
165         // We'll use Model if available, otherwise Vendor+Product
166         Map<String, Quintet<String, String, String, String, Long>> deviceStringMap = Iostat
167                 .queryDeviceStrings(deviceMap.keySet());
168 
169         List<HWDiskStore> storeList = new ArrayList<>();
170         for (Entry<String, Quintet<String, String, String, String, Long>> entry : deviceStringMap.entrySet()) {
171             String storeName = entry.getKey();
172             Quintet<String, String, String, String, Long> val = entry.getValue();
173             storeList.add(createStore(storeName, val.getA(), val.getB(), val.getC(), val.getD(), val.getE(),
174                     deviceMap.getOrDefault(storeName, ""), majorMap.getOrDefault(storeName, 0)));
175         }
176 
177         return storeList;
178     }
179 
180     private static SolarisHWDiskStore createStore(String diskName, String model, String vendor, String product,
181             String serial, long size, String mount, int major) {
182         SolarisHWDiskStore store = new SolarisHWDiskStore(diskName,
183                 model.isEmpty() ? (vendor + " " + product).trim() : model, serial, size);
184         store.partitionList = Collections.unmodifiableList(Prtvtoc.queryPartitions(mount, major).stream()
185                 .sorted(Comparator.comparing(HWPartition::getName)).collect(Collectors.toList()));
186         store.updateAttributes();
187         return store;
188     }
189 }