1
2
3
4
5 package oshi.software.os.windows;
6
7 import static oshi.software.os.OSService.State.OTHER;
8 import static oshi.software.os.OSService.State.RUNNING;
9 import static oshi.software.os.OSService.State.STOPPED;
10 import static oshi.software.os.OperatingSystem.ProcessFiltering.VALID_PROCESS;
11 import static oshi.util.Memoizer.defaultExpiration;
12 import static oshi.util.Memoizer.memoize;
13
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.concurrent.TimeUnit;
24 import java.util.function.Supplier;
25 import java.util.stream.Collectors;
26
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import com.sun.jna.Native;
31 import com.sun.jna.platform.win32.Advapi32;
32 import com.sun.jna.platform.win32.Advapi32Util;
33 import com.sun.jna.platform.win32.Advapi32Util.EventLogIterator;
34 import com.sun.jna.platform.win32.Advapi32Util.EventLogRecord;
35 import com.sun.jna.platform.win32.Kernel32;
36 import com.sun.jna.platform.win32.Psapi;
37 import com.sun.jna.platform.win32.Tlhelp32;
38 import com.sun.jna.platform.win32.VersionHelpers;
39 import com.sun.jna.platform.win32.W32ServiceManager;
40 import com.sun.jna.platform.win32.Win32Exception;
41 import com.sun.jna.platform.win32.WinDef.DWORD;
42 import com.sun.jna.platform.win32.WinError;
43 import com.sun.jna.platform.win32.WinNT;
44 import com.sun.jna.platform.win32.WinNT.HANDLE;
45 import com.sun.jna.platform.win32.WinNT.LUID;
46 import com.sun.jna.platform.win32.Winsvc;
47 import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
48
49 import oshi.annotation.concurrent.ThreadSafe;
50 import oshi.driver.windows.EnumWindows;
51 import oshi.driver.windows.registry.HkeyUserData;
52 import oshi.driver.windows.registry.NetSessionData;
53 import oshi.driver.windows.registry.ProcessPerformanceData;
54 import oshi.driver.windows.registry.ProcessWtsData;
55 import oshi.driver.windows.registry.ProcessWtsData.WtsInfo;
56 import oshi.driver.windows.registry.SessionWtsData;
57 import oshi.driver.windows.registry.ThreadPerformanceData;
58 import oshi.driver.windows.wmi.Win32OperatingSystem;
59 import oshi.driver.windows.wmi.Win32OperatingSystem.OSVersionProperty;
60 import oshi.driver.windows.wmi.Win32Processor;
61 import oshi.driver.windows.wmi.Win32Processor.BitnessProperty;
62 import oshi.jna.ByRef.CloseableHANDLEByReference;
63 import oshi.jna.ByRef.CloseableIntByReference;
64 import oshi.jna.ByRef.CloseablePROCESSENTRY32ByReference;
65 import oshi.jna.Struct.CloseablePerformanceInformation;
66 import oshi.jna.Struct.CloseableSystemInfo;
67 import oshi.software.common.AbstractOperatingSystem;
68 import oshi.software.os.FileSystem;
69 import oshi.software.os.InternetProtocolStats;
70 import oshi.software.os.NetworkParams;
71 import oshi.software.os.OSDesktopWindow;
72 import oshi.software.os.OSProcess;
73 import oshi.software.os.OSService;
74 import oshi.software.os.OSService.State;
75 import oshi.software.os.OSSession;
76 import oshi.software.os.OSThread;
77 import oshi.util.Constants;
78 import oshi.util.GlobalConfig;
79 import oshi.util.platform.windows.WmiUtil;
80 import oshi.util.tuples.Pair;
81
82
83
84
85
86 @ThreadSafe
87 public class WindowsOperatingSystem extends AbstractOperatingSystem {
88
89 private static final Logger LOG = LoggerFactory.getLogger(WindowsOperatingSystem.class);
90
91 private static final boolean USE_PROCSTATE_SUSPENDED = GlobalConfig
92 .get(GlobalConfig.OSHI_OS_WINDOWS_PROCSTATE_SUSPENDED, false);
93
94 private static final boolean IS_VISTA_OR_GREATER = VersionHelpers.IsWindowsVistaOrGreater();
95
96
97
98
99 private static Supplier<String> systemLog = memoize(WindowsOperatingSystem::querySystemLog,
100 TimeUnit.HOURS.toNanos(1));
101
102 private static final long BOOTTIME = querySystemBootTime();
103
104 static {
105 enableDebugPrivilege();
106 }
107
108
109
110
111 private static final boolean X86 = isCurrentX86();
112 private static final boolean WOW = isCurrentWow();
113
114
115
116
117 private Supplier<Map<Integer, ProcessPerformanceData.PerfCounterBlock>> processMapFromRegistry = memoize(
118 WindowsOperatingSystem::queryProcessMapFromRegistry, defaultExpiration());
119 private Supplier<Map<Integer, ProcessPerformanceData.PerfCounterBlock>> processMapFromPerfCounters = memoize(
120 WindowsOperatingSystem::queryProcessMapFromPerfCounters, defaultExpiration());
121
122
123
124
125 private Supplier<Map<Integer, ThreadPerformanceData.PerfCounterBlock>> threadMapFromRegistry = memoize(
126 WindowsOperatingSystem::queryThreadMapFromRegistry, defaultExpiration());
127 private Supplier<Map<Integer, ThreadPerformanceData.PerfCounterBlock>> threadMapFromPerfCounters = memoize(
128 WindowsOperatingSystem::queryThreadMapFromPerfCounters, defaultExpiration());
129
130 @Override
131 public String queryManufacturer() {
132 return "Microsoft";
133 }
134
135 @Override
136 public Pair<String, OSVersionInfo> queryFamilyVersionInfo() {
137 String version = System.getProperty("os.name");
138 if (version.startsWith("Windows ")) {
139 version = version.substring(8);
140 }
141
142 String sp = null;
143 int suiteMask = 0;
144 String buildNumber = "";
145 WmiResult<OSVersionProperty> versionInfo = Win32OperatingSystem.queryOsVersion();
146 if (versionInfo.getResultCount() > 0) {
147 sp = WmiUtil.getString(versionInfo, OSVersionProperty.CSDVERSION, 0);
148 if (!sp.isEmpty() && !Constants.UNKNOWN.equals(sp)) {
149 version = version + " " + sp.replace("Service Pack ", "SP");
150 }
151 suiteMask = WmiUtil.getUint32(versionInfo, OSVersionProperty.SUITEMASK, 0);
152 buildNumber = WmiUtil.getString(versionInfo, OSVersionProperty.BUILDNUMBER, 0);
153 }
154 String codeName = parseCodeName(suiteMask);
155
156 if ("10".equals(version) && buildNumber.compareTo("22000") >= 0) {
157 version = "11";
158 } else if ("Server 2019".equals(version) && buildNumber.compareTo("20347") > 0) {
159 version = "Server 2022";
160 }
161 return new Pair<>("Windows", new OSVersionInfo(version, codeName, buildNumber));
162 }
163
164
165
166
167
168
169
170
171 private static String parseCodeName(int suiteMask) {
172 List<String> suites = new ArrayList<>();
173 if ((suiteMask & 0x00000002) != 0) {
174 suites.add("Enterprise");
175 }
176 if ((suiteMask & 0x00000004) != 0) {
177 suites.add("BackOffice");
178 }
179 if ((suiteMask & 0x00000008) != 0) {
180 suites.add("Communications Server");
181 }
182 if ((suiteMask & 0x00000080) != 0) {
183 suites.add("Datacenter");
184 }
185 if ((suiteMask & 0x00000200) != 0) {
186 suites.add("Home");
187 }
188 if ((suiteMask & 0x00000400) != 0) {
189 suites.add("Web Server");
190 }
191 if ((suiteMask & 0x00002000) != 0) {
192 suites.add("Storage Server");
193 }
194 if ((suiteMask & 0x00004000) != 0) {
195 suites.add("Compute Cluster");
196 }
197 if ((suiteMask & 0x00008000) != 0) {
198 suites.add("Home Server");
199 }
200 return String.join(",", suites);
201 }
202
203 @Override
204 protected int queryBitness(int jvmBitness) {
205 if (jvmBitness < 64 && System.getenv("ProgramFiles(x86)") != null && IS_VISTA_OR_GREATER) {
206 WmiResult<BitnessProperty> bitnessMap = Win32Processor.queryBitness();
207 if (bitnessMap.getResultCount() > 0) {
208 return WmiUtil.getUint16(bitnessMap, BitnessProperty.ADDRESSWIDTH, 0);
209 }
210 }
211 return jvmBitness;
212 }
213
214 @Override
215 public boolean isElevated() {
216 return Advapi32Util.isCurrentProcessElevated();
217 }
218
219 @Override
220 public FileSystem getFileSystem() {
221 return new WindowsFileSystem();
222 }
223
224 @Override
225 public InternetProtocolStats getInternetProtocolStats() {
226 return new WindowsInternetProtocolStats();
227 }
228
229 @Override
230 public List<OSSession> getSessions() {
231 List<OSSession> whoList = HkeyUserData.queryUserSessions();
232 whoList.addAll(SessionWtsData.queryUserSessions());
233 whoList.addAll(NetSessionData.queryUserSessions());
234 return whoList;
235 }
236
237 @Override
238 public List<OSProcess> getProcesses(Collection<Integer> pids) {
239 return processMapToList(pids);
240 }
241
242 @Override
243 public List<OSProcess> queryAllProcesses() {
244 return processMapToList(null);
245 }
246
247 @Override
248 public List<OSProcess> queryChildProcesses(int parentPid) {
249 Set<Integer> descendantPids = getChildrenOrDescendants(getParentPidsFromSnapshot(), parentPid, false);
250 return processMapToList(descendantPids);
251 }
252
253 @Override
254 public List<OSProcess> queryDescendantProcesses(int parentPid) {
255 Set<Integer> descendantPids = getChildrenOrDescendants(getParentPidsFromSnapshot(), parentPid, true);
256 return processMapToList(descendantPids);
257 }
258
259 private static Map<Integer, Integer> getParentPidsFromSnapshot() {
260 Map<Integer, Integer> parentPidMap = new HashMap<>();
261
262 try (CloseablePROCESSENTRY32ByReference processEntry = new CloseablePROCESSENTRY32ByReference()) {
263 WinNT.HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS,
264 new DWORD(0));
265 try {
266 while (Kernel32.INSTANCE.Process32Next(snapshot, processEntry)) {
267 parentPidMap.put(processEntry.th32ProcessID.intValue(),
268 processEntry.th32ParentProcessID.intValue());
269 }
270 } finally {
271 Kernel32.INSTANCE.CloseHandle(snapshot);
272 }
273 }
274 return parentPidMap;
275 }
276
277 @Override
278 public OSProcess getProcess(int pid) {
279 List<OSProcess> procList = processMapToList(Arrays.asList(pid));
280 return procList.isEmpty() ? null : procList.get(0);
281 }
282
283 private List<OSProcess> processMapToList(Collection<Integer> pids) {
284
285 Map<Integer, ProcessPerformanceData.PerfCounterBlock> processMap = processMapFromRegistry.get();
286
287 if (processMap == null || processMap.isEmpty()) {
288 processMap = (pids == null) ? processMapFromPerfCounters.get()
289 : ProcessPerformanceData.buildProcessMapFromPerfCounters(pids);
290 }
291 Map<Integer, ThreadPerformanceData.PerfCounterBlock> threadMap = null;
292 if (USE_PROCSTATE_SUSPENDED) {
293
294 threadMap = threadMapFromRegistry.get();
295
296 if (threadMap == null || threadMap.isEmpty()) {
297 threadMap = (pids == null) ? threadMapFromPerfCounters.get()
298 : ThreadPerformanceData.buildThreadMapFromPerfCounters(pids);
299 }
300 }
301
302 Map<Integer, WtsInfo> processWtsMap = ProcessWtsData.queryProcessWtsMap(pids);
303
304 Set<Integer> mapKeys = new HashSet<>(processWtsMap.keySet());
305 mapKeys.retainAll(processMap.keySet());
306
307 final Map<Integer, ProcessPerformanceData.PerfCounterBlock> finalProcessMap = processMap;
308 final Map<Integer, ThreadPerformanceData.PerfCounterBlock> finalThreadMap = threadMap;
309 return mapKeys.stream().parallel()
310 .map(pid -> new WindowsOSProcess(pid, this, finalProcessMap, processWtsMap, finalThreadMap))
311 .filter(VALID_PROCESS).collect(Collectors.toList());
312 }
313
314 private static Map<Integer, ProcessPerformanceData.PerfCounterBlock> queryProcessMapFromRegistry() {
315 return ProcessPerformanceData.buildProcessMapFromRegistry(null);
316 }
317
318 private static Map<Integer, ProcessPerformanceData.PerfCounterBlock> queryProcessMapFromPerfCounters() {
319 return ProcessPerformanceData.buildProcessMapFromPerfCounters(null);
320 }
321
322 private static Map<Integer, ThreadPerformanceData.PerfCounterBlock> queryThreadMapFromRegistry() {
323 return ThreadPerformanceData.buildThreadMapFromRegistry(null);
324 }
325
326 private static Map<Integer, ThreadPerformanceData.PerfCounterBlock> queryThreadMapFromPerfCounters() {
327 return ThreadPerformanceData.buildThreadMapFromPerfCounters(null);
328 }
329
330 @Override
331 public int getProcessId() {
332 return Kernel32.INSTANCE.GetCurrentProcessId();
333 }
334
335 @Override
336 public int getProcessCount() {
337 try (CloseablePerformanceInformation perfInfo = new CloseablePerformanceInformation()) {
338 if (!Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size())) {
339 LOG.error("Failed to get Performance Info. Error code: {}", Kernel32.INSTANCE.GetLastError());
340 return 0;
341 }
342 return perfInfo.ProcessCount.intValue();
343 }
344 }
345
346 @Override
347 public int getThreadId() {
348 return Kernel32.INSTANCE.GetCurrentThreadId();
349 }
350
351 @Override
352 public OSThread getCurrentThread() {
353 OSProcess proc = getCurrentProcess();
354 final int tid = getThreadId();
355 return proc.getThreadDetails().stream().filter(t -> t.getThreadId() == tid).findFirst()
356 .orElse(new WindowsOSThread(proc.getProcessID(), tid, null, null));
357 }
358
359 @Override
360 public int getThreadCount() {
361 try (CloseablePerformanceInformation perfInfo = new CloseablePerformanceInformation()) {
362 if (!Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size())) {
363 LOG.error("Failed to get Performance Info. Error code: {}", Kernel32.INSTANCE.GetLastError());
364 return 0;
365 }
366 return perfInfo.ThreadCount.intValue();
367 }
368 }
369
370 @Override
371 public long getSystemUptime() {
372 return querySystemUptime();
373 }
374
375 private static long querySystemUptime() {
376
377
378 if (IS_VISTA_OR_GREATER) {
379 return Kernel32.INSTANCE.GetTickCount64() / 1000L;
380 } else {
381
382 return Kernel32.INSTANCE.GetTickCount() / 1000L;
383 }
384 }
385
386 @Override
387 public long getSystemBootTime() {
388 return BOOTTIME;
389 }
390
391 private static long querySystemBootTime() {
392 String eventLog = systemLog.get();
393 if (eventLog != null) {
394 try {
395 EventLogIterator iter = new EventLogIterator(null, eventLog, WinNT.EVENTLOG_BACKWARDS_READ);
396
397
398
399 long event6005Time = 0L;
400 while (iter.hasNext()) {
401 EventLogRecord logRecord = iter.next();
402 if (logRecord.getStatusCode() == 12) {
403
404
405 return logRecord.getRecord().TimeGenerated.longValue();
406 } else if (logRecord.getStatusCode() == 6005) {
407
408
409 if (event6005Time > 0) {
410 return event6005Time;
411 }
412
413 event6005Time = logRecord.getRecord().TimeGenerated.longValue();
414 }
415 }
416
417 if (event6005Time > 0) {
418 return event6005Time;
419 }
420 } catch (Win32Exception e) {
421 LOG.warn("Can't open event log \"{}\".", eventLog);
422 }
423 }
424
425
426 return System.currentTimeMillis() / 1000L - querySystemUptime();
427 }
428
429 @Override
430 public NetworkParams getNetworkParams() {
431 return new WindowsNetworkParams();
432 }
433
434
435
436
437
438
439
440 private static boolean enableDebugPrivilege() {
441 try (CloseableHANDLEByReference hToken = new CloseableHANDLEByReference()) {
442 boolean success = Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(),
443 WinNT.TOKEN_QUERY | WinNT.TOKEN_ADJUST_PRIVILEGES, hToken);
444 if (!success) {
445 LOG.error("OpenProcessToken failed. Error: {}", Native.getLastError());
446 return false;
447 }
448 try {
449 LUID luid = new LUID();
450 success = Advapi32.INSTANCE.LookupPrivilegeValue(null, WinNT.SE_DEBUG_NAME, luid);
451 if (!success) {
452 LOG.error("LookupPrivilegeValue failed. Error: {}", Native.getLastError());
453 return false;
454 }
455 WinNT.TOKEN_PRIVILEGES tkp = new WinNT.TOKEN_PRIVILEGES(1);
456 tkp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(luid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
457 success = Advapi32.INSTANCE.AdjustTokenPrivileges(hToken.getValue(), false, tkp, 0, null, null);
458 int err = Native.getLastError();
459 if (!success) {
460 LOG.error("AdjustTokenPrivileges failed. Error: {}", err);
461 return false;
462 } else if (err == WinError.ERROR_NOT_ALL_ASSIGNED) {
463 LOG.debug("Debug privileges not enabled.");
464 return false;
465 }
466 } finally {
467 Kernel32.INSTANCE.CloseHandle(hToken.getValue());
468 }
469 }
470 return true;
471 }
472
473 @Override
474 public List<OSService> getServices() {
475 try (W32ServiceManager sm = new W32ServiceManager()) {
476 sm.open(Winsvc.SC_MANAGER_ENUMERATE_SERVICE);
477 Winsvc.ENUM_SERVICE_STATUS_PROCESS[] services = sm.enumServicesStatusExProcess(WinNT.SERVICE_WIN32,
478 Winsvc.SERVICE_STATE_ALL, null);
479 List<OSService> svcArray = new ArrayList<>();
480 for (Winsvc.ENUM_SERVICE_STATUS_PROCESS service : services) {
481 State state;
482 switch (service.ServiceStatusProcess.dwCurrentState) {
483 case 1:
484 state = STOPPED;
485 break;
486 case 4:
487 state = RUNNING;
488 break;
489 default:
490 state = OTHER;
491 break;
492 }
493 svcArray.add(new OSService(service.lpDisplayName, service.ServiceStatusProcess.dwProcessId, state));
494 }
495 return svcArray;
496 } catch (com.sun.jna.platform.win32.Win32Exception ex) {
497 LOG.error("Win32Exception: {}", ex.getMessage());
498 return Collections.emptyList();
499 }
500 }
501
502 private static String querySystemLog() {
503 String systemLog = GlobalConfig.get(GlobalConfig.OSHI_OS_WINDOWS_EVENTLOG, "System");
504 if (systemLog.isEmpty()) {
505
506 return null;
507 }
508
509 HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, systemLog);
510 if (h == null) {
511 LOG.warn("Unable to open configured system Event log \"{}\". Calculating boot time from uptime.",
512 systemLog);
513 return null;
514 }
515 return systemLog;
516 }
517
518 @Override
519 public List<OSDesktopWindow> getDesktopWindows(boolean visibleOnly) {
520 return EnumWindows.queryDesktopWindows(visibleOnly);
521 }
522
523
524
525
526
527
528
529
530
531
532 static boolean isX86() {
533 return X86;
534 }
535
536 private static boolean isCurrentX86() {
537 try (CloseableSystemInfo sysinfo = new CloseableSystemInfo()) {
538 Kernel32.INSTANCE.GetNativeSystemInfo(sysinfo);
539 return (0 == sysinfo.processorArchitecture.pi.wProcessorArchitecture.intValue());
540 }
541 }
542
543
544
545
546
547
548 static boolean isWow() {
549 return WOW;
550 }
551
552
553
554
555
556
557
558 static boolean isWow(HANDLE h) {
559 if (X86) {
560 return true;
561 }
562 try (CloseableIntByReference isWow = new CloseableIntByReference()) {
563 Kernel32.INSTANCE.IsWow64Process(h, isWow);
564 return isWow.getValue() != 0;
565 }
566 }
567
568 private static boolean isCurrentWow() {
569 if (X86) {
570 return true;
571 }
572 HANDLE h = Kernel32.INSTANCE.GetCurrentProcess();
573 return (h == null) ? false : isWow(h);
574 }
575 }