1
2
3
4
5 package oshi.software.os.mac;
6
7 import static oshi.software.os.OSService.State.RUNNING;
8 import static oshi.software.os.OSService.State.STOPPED;
9
10 import java.io.File;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Comparator;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Locale;
17 import java.util.Properties;
18 import java.util.Set;
19 import java.util.stream.Collectors;
20
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import com.sun.jna.platform.mac.SystemB;
25
26 import oshi.annotation.concurrent.ThreadSafe;
27 import oshi.driver.mac.Who;
28 import oshi.driver.mac.WindowInfo;
29 import oshi.jna.Struct.CloseableProcTaskInfo;
30 import oshi.jna.Struct.CloseableTimeval;
31 import oshi.software.common.AbstractOperatingSystem;
32 import oshi.software.os.FileSystem;
33 import oshi.software.os.InternetProtocolStats;
34 import oshi.software.os.NetworkParams;
35 import oshi.software.os.OSDesktopWindow;
36 import oshi.software.os.OSProcess;
37 import oshi.software.os.OSProcess.State;
38 import oshi.software.os.OSService;
39 import oshi.software.os.OSSession;
40 import oshi.software.os.OSThread;
41 import oshi.util.ExecutingCommand;
42 import oshi.util.FileUtil;
43 import oshi.util.ParseUtil;
44 import oshi.util.Util;
45 import oshi.util.platform.mac.SysctlUtil;
46 import oshi.util.tuples.Pair;
47
48
49
50
51
52 @ThreadSafe
53 public class MacOperatingSystem extends AbstractOperatingSystem {
54
55 private static final Logger LOG = LoggerFactory.getLogger(MacOperatingSystem.class);
56
57 public static final String MACOS_VERSIONS_PROPERTIES = "oshi.macos.versions.properties";
58
59 private static final String SYSTEM_LIBRARY_LAUNCH_AGENTS = "/System/Library/LaunchAgents";
60 private static final String SYSTEM_LIBRARY_LAUNCH_DAEMONS = "/System/Library/LaunchDaemons";
61
62 private int maxProc = 1024;
63
64 private final String osXVersion;
65 private final int major;
66 private final int minor;
67
68 private static final long BOOTTIME;
69 static {
70 try (CloseableTimeval tv = new CloseableTimeval()) {
71 if (!SysctlUtil.sysctl("kern.boottime", tv) || tv.tv_sec.longValue() == 0L) {
72
73
74 BOOTTIME = ParseUtil.parseLongOrDefault(
75 ExecutingCommand.getFirstAnswer("sysctl -n kern.boottime").split(",")[0].replaceAll("\\D", ""),
76 System.currentTimeMillis() / 1000);
77 } else {
78
79
80
81 BOOTTIME = tv.tv_sec.longValue();
82 }
83 }
84 }
85
86 public MacOperatingSystem() {
87 String version = System.getProperty("os.version");
88 int verMajor = ParseUtil.getFirstIntValue(version);
89 int verMinor = ParseUtil.getNthIntValue(version, 2);
90
91 if (verMajor == 10 && verMinor > 15) {
92 String swVers = ExecutingCommand.getFirstAnswer("sw_vers -productVersion");
93 if (!swVers.isEmpty()) {
94 version = swVers;
95 }
96 verMajor = ParseUtil.getFirstIntValue(version);
97 verMinor = ParseUtil.getNthIntValue(version, 2);
98 }
99 this.osXVersion = version;
100 this.major = verMajor;
101 this.minor = verMinor;
102
103 this.maxProc = SysctlUtil.sysctl("kern.maxproc", 0x1000);
104 }
105
106 @Override
107 public String queryManufacturer() {
108 return "Apple";
109 }
110
111 @Override
112 public Pair<String, OSVersionInfo> queryFamilyVersionInfo() {
113 String family = this.major > 10 || (this.major == 10 && this.minor >= 12) ? "macOS"
114 : System.getProperty("os.name");
115 String codeName = parseCodeName();
116 String buildNumber = SysctlUtil.sysctl("kern.osversion", "");
117 return new Pair<>(family, new OSVersionInfo(this.osXVersion, codeName, buildNumber));
118 }
119
120 private String parseCodeName() {
121 Properties verProps = FileUtil.readPropertiesFromFilename(MACOS_VERSIONS_PROPERTIES);
122 String codeName = null;
123 if (this.major > 10) {
124 codeName = verProps.getProperty(Integer.toString(this.major));
125 } else if (this.major == 10) {
126 codeName = verProps.getProperty(this.major + "." + this.minor);
127 }
128 if (Util.isBlank(codeName)) {
129 LOG.warn("Unable to parse version {}.{} to a codename.", this.major, this.minor);
130 }
131 return codeName;
132 }
133
134 @Override
135 protected int queryBitness(int jvmBitness) {
136 if (jvmBitness == 64 || (this.major == 10 && this.minor > 6)) {
137 return 64;
138 }
139 return ParseUtil.parseIntOrDefault(ExecutingCommand.getFirstAnswer("getconf LONG_BIT"), 32);
140 }
141
142 @Override
143 public FileSystem getFileSystem() {
144 return new MacFileSystem();
145 }
146
147 @Override
148 public InternetProtocolStats getInternetProtocolStats() {
149 return new MacInternetProtocolStats(isElevated());
150 }
151
152 @Override
153 public List<OSSession> getSessions() {
154 return USE_WHO_COMMAND ? super.getSessions() : Who.queryUtxent();
155 }
156
157 @Override
158 public List<OSProcess> queryAllProcesses() {
159 List<OSProcess> procs = new ArrayList<>();
160 int[] pids = new int[this.maxProc];
161 Arrays.fill(pids, -1);
162 int numberOfProcesses = SystemB.INSTANCE.proc_listpids(SystemB.PROC_ALL_PIDS, 0, pids,
163 pids.length * SystemB.INT_SIZE) / SystemB.INT_SIZE;
164 for (int i = 0; i < numberOfProcesses; i++) {
165 if (pids[i] >= 0) {
166 OSProcess proc = getProcess(pids[i]);
167 if (proc != null) {
168 procs.add(proc);
169 }
170 }
171 }
172 return procs;
173 }
174
175 @Override
176 public OSProcess getProcess(int pid) {
177 OSProcess proc = new MacOSProcess(pid, this.major, this.minor, this);
178 return proc.getState().equals(State.INVALID) ? null : proc;
179 }
180
181 @Override
182 public List<OSProcess> queryChildProcesses(int parentPid) {
183 List<OSProcess> allProcs = queryAllProcesses();
184 Set<Integer> descendantPids = getChildrenOrDescendants(allProcs, parentPid, false);
185 return allProcs.stream().filter(p -> descendantPids.contains(p.getProcessID())).collect(Collectors.toList());
186 }
187
188 @Override
189 public List<OSProcess> queryDescendantProcesses(int parentPid) {
190 List<OSProcess> allProcs = queryAllProcesses();
191 Set<Integer> descendantPids = getChildrenOrDescendants(allProcs, parentPid, true);
192 return allProcs.stream().filter(p -> descendantPids.contains(p.getProcessID())).collect(Collectors.toList());
193 }
194
195 @Override
196 public int getProcessId() {
197 return SystemB.INSTANCE.getpid();
198 }
199
200 @Override
201 public int getProcessCount() {
202 return SystemB.INSTANCE.proc_listpids(SystemB.PROC_ALL_PIDS, 0, null, 0) / SystemB.INT_SIZE;
203 }
204
205 @Override
206 public int getThreadId() {
207 OSThread thread = getCurrentThread();
208 if (thread == null) {
209 return 0;
210 }
211 return thread.getThreadId();
212 }
213
214 @Override
215 public OSThread getCurrentThread() {
216
217 return getCurrentProcess().getThreadDetails().stream().sorted(Comparator.comparingLong(OSThread::getStartTime))
218 .findFirst().orElse(new MacOSThread(getProcessId()));
219 }
220
221 @Override
222 public int getThreadCount() {
223
224
225 int[] pids = new int[getProcessCount() + 10];
226 int numberOfProcesses = SystemB.INSTANCE.proc_listpids(SystemB.PROC_ALL_PIDS, 0, pids, pids.length)
227 / SystemB.INT_SIZE;
228 int numberOfThreads = 0;
229 try (CloseableProcTaskInfo taskInfo = new CloseableProcTaskInfo()) {
230 for (int i = 0; i < numberOfProcesses; i++) {
231 int exit = SystemB.INSTANCE.proc_pidinfo(pids[i], SystemB.PROC_PIDTASKINFO, 0, taskInfo,
232 taskInfo.size());
233 if (exit != -1) {
234 numberOfThreads += taskInfo.pti_threadnum;
235 }
236 }
237 }
238 return numberOfThreads;
239 }
240
241 @Override
242 public long getSystemUptime() {
243 return System.currentTimeMillis() / 1000 - BOOTTIME;
244 }
245
246 @Override
247 public long getSystemBootTime() {
248 return BOOTTIME;
249 }
250
251 @Override
252 public NetworkParams getNetworkParams() {
253 return new MacNetworkParams();
254 }
255
256 @Override
257 public List<OSService> getServices() {
258
259 List<OSService> services = new ArrayList<>();
260 Set<String> running = new HashSet<>();
261 for (OSProcess p : getChildProcesses(1, ProcessFiltering.ALL_PROCESSES, ProcessSorting.PID_ASC, 0)) {
262 OSService s = new OSService(p.getName(), p.getProcessID(), RUNNING);
263 services.add(s);
264 running.add(p.getName());
265 }
266
267 ArrayList<File> files = new ArrayList<>();
268 File dir = new File(SYSTEM_LIBRARY_LAUNCH_AGENTS);
269 if (dir.exists() && dir.isDirectory()) {
270 files.addAll(Arrays.asList(dir.listFiles((f, name) -> name.toLowerCase(Locale.ROOT).endsWith(".plist"))));
271 } else {
272 LOG.error("Directory: /System/Library/LaunchAgents does not exist");
273 }
274 dir = new File(SYSTEM_LIBRARY_LAUNCH_DAEMONS);
275 if (dir.exists() && dir.isDirectory()) {
276 files.addAll(Arrays.asList(dir.listFiles((f, name) -> name.toLowerCase(Locale.ROOT).endsWith(".plist"))));
277 } else {
278 LOG.error("Directory: /System/Library/LaunchDaemons does not exist");
279 }
280 for (File f : files) {
281
282 String name = f.getName().substring(0, f.getName().length() - 6);
283 int index = name.lastIndexOf('.');
284 String shortName = (index < 0 || index > name.length() - 2) ? name : name.substring(index + 1);
285 if (!running.contains(name) && !running.contains(shortName)) {
286 OSService s = new OSService(name, 0, STOPPED);
287 services.add(s);
288 }
289 }
290 return services;
291 }
292
293 @Override
294 public List<OSDesktopWindow> getDesktopWindows(boolean visibleOnly) {
295 return WindowInfo.queryDesktopWindows(visibleOnly);
296 }
297 }