View Javadoc
1   /*
2    * Copyright 2021-2023 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.software.os.unix.openbsd;
6   
7   import static oshi.jna.platform.unix.OpenBsdLibc.CTL_KERN;
8   import static oshi.jna.platform.unix.OpenBsdLibc.KERN_OSRELEASE;
9   import static oshi.jna.platform.unix.OpenBsdLibc.KERN_OSTYPE;
10  import static oshi.jna.platform.unix.OpenBsdLibc.KERN_VERSION;
11  import static oshi.software.os.OSService.State.RUNNING;
12  import static oshi.software.os.OSService.State.STOPPED;
13  
14  import java.io.File;
15  import java.util.ArrayList;
16  import java.util.Arrays;
17  import java.util.HashSet;
18  import java.util.List;
19  import java.util.Locale;
20  import java.util.Map;
21  import java.util.Set;
22  import java.util.stream.Collectors;
23  
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  import oshi.annotation.concurrent.ThreadSafe;
28  import oshi.jna.platform.unix.OpenBsdLibc;
29  import oshi.software.common.AbstractOperatingSystem;
30  import oshi.software.os.FileSystem;
31  import oshi.software.os.InternetProtocolStats;
32  import oshi.software.os.NetworkParams;
33  import oshi.software.os.OSProcess;
34  import oshi.software.os.OSService;
35  import oshi.software.os.OSThread;
36  import oshi.util.ExecutingCommand;
37  import oshi.util.ParseUtil;
38  import oshi.util.platform.unix.openbsd.OpenBsdSysctlUtil;
39  import oshi.util.tuples.Pair;
40  
41  /**
42   * OpenBsd is a free and open-source Unix-like operating system descended from the Berkeley Software Distribution (BSD),
43   * which was based on Research Unix.
44   */
45  @ThreadSafe
46  public class OpenBsdOperatingSystem extends AbstractOperatingSystem {
47  
48      private static final Logger LOG = LoggerFactory.getLogger(OpenBsdOperatingSystem.class);
49  
50      private static final long BOOTTIME = querySystemBootTime();
51  
52      /*
53       * Package-private for use by OpenBsdOSProcess
54       */
55      enum PsKeywords {
56          STATE, PID, PPID, USER, UID, GROUP, GID, PRI, VSZ, RSS, ETIME, CPUTIME, COMM, MAJFLT, MINFLT, NVCSW, NIVCSW,
57          ARGS; // ARGS must always be last
58      }
59  
60      static final String PS_COMMAND_ARGS = Arrays.stream(PsKeywords.values()).map(Enum::name)
61              .map(name -> name.toLowerCase(Locale.ROOT)).collect(Collectors.joining(","));
62  
63      @Override
64      public String queryManufacturer() {
65          return "Unix/BSD";
66      }
67  
68      @Override
69      public Pair<String, OSVersionInfo> queryFamilyVersionInfo() {
70          int[] mib = new int[2];
71          mib[0] = CTL_KERN;
72          mib[1] = KERN_OSTYPE;
73          String family = OpenBsdSysctlUtil.sysctl(mib, "OpenBSD");
74          mib[1] = KERN_OSRELEASE;
75          String version = OpenBsdSysctlUtil.sysctl(mib, "");
76          mib[1] = KERN_VERSION;
77          String versionInfo = OpenBsdSysctlUtil.sysctl(mib, "");
78          String buildNumber = versionInfo.split(":")[0].replace(family, "").replace(version, "").trim();
79  
80          return new Pair<>(family, new OSVersionInfo(version, null, buildNumber));
81      }
82  
83      @Override
84      protected int queryBitness(int jvmBitness) {
85          if (jvmBitness < 64 && ExecutingCommand.getFirstAnswer("uname -m").indexOf("64") == -1) {
86              return jvmBitness;
87          }
88          return 64;
89      }
90  
91      @Override
92      public FileSystem getFileSystem() {
93          return new OpenBsdFileSystem();
94      }
95  
96      @Override
97      public InternetProtocolStats getInternetProtocolStats() {
98          return new OpenBsdInternetProtocolStats();
99      }
100 
101     @Override
102     public List<OSProcess> queryAllProcesses() {
103         return getProcessListFromPS(-1);
104     }
105 
106     @Override
107     public List<OSProcess> queryChildProcesses(int parentPid) {
108         List<OSProcess> allProcs = queryAllProcesses();
109         Set<Integer> descendantPids = getChildrenOrDescendants(allProcs, parentPid, false);
110         return allProcs.stream().filter(p -> descendantPids.contains(p.getProcessID())).collect(Collectors.toList());
111     }
112 
113     @Override
114     public List<OSProcess> queryDescendantProcesses(int parentPid) {
115         List<OSProcess> allProcs = queryAllProcesses();
116         Set<Integer> descendantPids = getChildrenOrDescendants(allProcs, parentPid, true);
117         return allProcs.stream().filter(p -> descendantPids.contains(p.getProcessID())).collect(Collectors.toList());
118     }
119 
120     @Override
121     public OSProcess getProcess(int pid) {
122         List<OSProcess> procs = getProcessListFromPS(pid);
123         if (procs.isEmpty()) {
124             return null;
125         }
126         return procs.get(0);
127     }
128 
129     private List<OSProcess> getProcessListFromPS(int pid) {
130         List<OSProcess> procs = new ArrayList<>();
131         // https://man.openbsd.org/ps#KEYWORDS
132         // missing are threadCount and kernelTime which is included in cputime
133         String psCommand = "ps -awwxo " + PS_COMMAND_ARGS;
134         if (pid >= 0) {
135             psCommand += " -p " + pid;
136         }
137         List<String> procList = ExecutingCommand.runNative(psCommand);
138         if (procList.isEmpty() || procList.size() < 2) {
139             return procs;
140         }
141 
142         // remove header row
143         procList.remove(0);
144         // Fill list
145         for (String proc : procList) {
146             Map<PsKeywords, String> psMap = ParseUtil.stringToEnumMap(PsKeywords.class, proc.trim(), ' ');
147             // Check if last (thus all) value populated
148             if (psMap.containsKey(PsKeywords.ARGS)) {
149                 procs.add(new OpenBsdOSProcess(
150                         pid < 0 ? ParseUtil.parseIntOrDefault(psMap.get(PsKeywords.PID), 0) : pid, psMap, this));
151             }
152         }
153         return procs;
154     }
155 
156     @Override
157     public int getProcessId() {
158         return OpenBsdLibc.INSTANCE.getpid();
159     }
160 
161     @Override
162     public int getProcessCount() {
163         List<String> procList = ExecutingCommand.runNative("ps -axo pid");
164         if (!procList.isEmpty()) {
165             // Subtract 1 for header
166             return procList.size() - 1;
167         }
168         return 0;
169     }
170 
171     @Override
172     public int getThreadId() {
173         return OpenBsdLibc.INSTANCE.getthrid();
174     }
175 
176     @Override
177     public OSThread getCurrentThread() {
178         OSProcess proc = getCurrentProcess();
179         final int tid = getThreadId();
180         return proc.getThreadDetails().stream().filter(t -> t.getThreadId() == tid).findFirst()
181                 .orElse(new OpenBsdOSThread(proc.getProcessID(), tid));
182     }
183 
184     @Override
185     public int getThreadCount() {
186         // -H "Also display information about kernel visible threads"
187         // -k "Also display information about kernel threads"
188         // column TID holds thread ID
189         List<String> threadList = ExecutingCommand.runNative("ps -axHo tid");
190         if (!threadList.isEmpty()) {
191             // Subtract 1 for header
192             return threadList.size() - 1;
193         }
194         return 0;
195     }
196 
197     @Override
198     public long getSystemUptime() {
199         return System.currentTimeMillis() / 1000 - BOOTTIME;
200     }
201 
202     @Override
203     public long getSystemBootTime() {
204         return BOOTTIME;
205     }
206 
207     private static long querySystemBootTime() {
208         // Boot time will be the first consecutive string of digits.
209         return ParseUtil.parseLongOrDefault(
210                 ExecutingCommand.getFirstAnswer("sysctl -n kern.boottime").split(",")[0].replaceAll("\\D", ""),
211                 System.currentTimeMillis() / 1000);
212     }
213 
214     @Override
215     public NetworkParams getNetworkParams() {
216         return new OpenBsdNetworkParams();
217     }
218 
219     @Override
220     public List<OSService> getServices() {
221         // Get running services
222         List<OSService> services = new ArrayList<>();
223         Set<String> running = new HashSet<>();
224         for (OSProcess p : getChildProcesses(1, ProcessFiltering.ALL_PROCESSES, ProcessSorting.PID_ASC, 0)) {
225             OSService s = new OSService(p.getName(), p.getProcessID(), RUNNING);
226             services.add(s);
227             running.add(p.getName());
228         }
229         // Get Directories for stopped services
230         File dir = new File("/etc/rc.d");
231         File[] listFiles;
232         if (dir.exists() && dir.isDirectory() && (listFiles = dir.listFiles()) != null) {
233             for (File f : listFiles) {
234                 String name = f.getName();
235                 if (!running.contains(name)) {
236                     OSService s = new OSService(name, 0, STOPPED);
237                     services.add(s);
238                 }
239             }
240         } else {
241             LOG.error("Directory: /etc/rc.d does not exist");
242         }
243         return services;
244     }
245 }