1
2
3
4
5 package oshi.software.os.windows;
6
7 import static oshi.software.os.OSProcess.State.INVALID;
8 import static oshi.software.os.OSProcess.State.RUNNING;
9 import static oshi.software.os.OSProcess.State.SUSPENDED;
10 import static oshi.util.Memoizer.memoize;
11
12 import java.io.File;
13 import java.util.Arrays;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.function.Supplier;
19 import java.util.stream.Collectors;
20
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import com.sun.jna.Memory;
25 import com.sun.jna.Pointer;
26 import com.sun.jna.platform.win32.Advapi32;
27 import com.sun.jna.platform.win32.Advapi32Util;
28 import com.sun.jna.platform.win32.Advapi32Util.Account;
29 import com.sun.jna.platform.win32.Kernel32;
30 import com.sun.jna.platform.win32.Kernel32Util;
31 import com.sun.jna.platform.win32.Shell32Util;
32 import com.sun.jna.platform.win32.VersionHelpers;
33 import com.sun.jna.platform.win32.Win32Exception;
34 import com.sun.jna.platform.win32.WinError;
35 import com.sun.jna.platform.win32.WinNT;
36 import com.sun.jna.platform.win32.WinNT.HANDLE;
37 import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
38
39 import oshi.annotation.concurrent.ThreadSafe;
40 import oshi.driver.windows.registry.ProcessPerformanceData;
41 import oshi.driver.windows.registry.ProcessWtsData;
42 import oshi.driver.windows.registry.ProcessWtsData.WtsInfo;
43 import oshi.driver.windows.registry.ThreadPerformanceData;
44 import oshi.driver.windows.wmi.Win32Process;
45 import oshi.driver.windows.wmi.Win32Process.CommandLineProperty;
46 import oshi.driver.windows.wmi.Win32ProcessCached;
47 import oshi.jna.ByRef.CloseableHANDLEByReference;
48 import oshi.jna.ByRef.CloseableIntByReference;
49 import oshi.jna.ByRef.CloseableULONGptrByReference;
50 import oshi.jna.platform.windows.NtDll;
51 import oshi.jna.platform.windows.NtDll.UNICODE_STRING;
52 import oshi.software.common.AbstractOSProcess;
53 import oshi.software.os.OSThread;
54 import oshi.util.Constants;
55 import oshi.util.GlobalConfig;
56 import oshi.util.ParseUtil;
57 import oshi.util.platform.windows.WmiUtil;
58 import oshi.util.tuples.Pair;
59 import oshi.util.tuples.Triplet;
60
61
62
63
64 @ThreadSafe
65 public class WindowsOSProcess extends AbstractOSProcess {
66
67 private static final Logger LOG = LoggerFactory.getLogger(WindowsOSProcess.class);
68
69 private static final boolean USE_BATCH_COMMANDLINE = GlobalConfig
70 .get(GlobalConfig.OSHI_OS_WINDOWS_COMMANDLINE_BATCH, false);
71
72 private static final boolean USE_PROCSTATE_SUSPENDED = GlobalConfig
73 .get(GlobalConfig.OSHI_OS_WINDOWS_PROCSTATE_SUSPENDED, false);
74
75 private static final boolean IS_VISTA_OR_GREATER = VersionHelpers.IsWindowsVistaOrGreater();
76 private static final boolean IS_WINDOWS7_OR_GREATER = VersionHelpers.IsWindows7OrGreater();
77
78
79 private final WindowsOperatingSystem os;
80
81 private Supplier<Pair<String, String>> userInfo = memoize(this::queryUserInfo);
82 private Supplier<Pair<String, String>> groupInfo = memoize(this::queryGroupInfo);
83 private Supplier<String> currentWorkingDirectory = memoize(this::queryCwd);
84 private Supplier<String> commandLine = memoize(this::queryCommandLine);
85 private Supplier<List<String>> args = memoize(this::queryArguments);
86 private Supplier<Triplet<String, String, Map<String, String>>> cwdCmdEnv = memoize(
87 this::queryCwdCommandlineEnvironment);
88 private Map<Integer, ThreadPerformanceData.PerfCounterBlock> tcb;
89
90 private String name;
91 private String path;
92 private State state = INVALID;
93 private int parentProcessID;
94 private int threadCount;
95 private int priority;
96 private long virtualSize;
97 private long residentSetSize;
98 private long kernelTime;
99 private long userTime;
100 private long startTime;
101 private long upTime;
102 private long bytesRead;
103 private long bytesWritten;
104 private long openFiles;
105 private int bitness;
106 private long pageFaults;
107
108 public WindowsOSProcess(int pid, WindowsOperatingSystem os,
109 Map<Integer, ProcessPerformanceData.PerfCounterBlock> processMap, Map<Integer, WtsInfo> processWtsMap,
110 Map<Integer, ThreadPerformanceData.PerfCounterBlock> threadMap) {
111 super(pid);
112
113 this.os = os;
114
115 this.bitness = os.getBitness();
116
117 this.tcb = threadMap;
118 updateAttributes(processMap.get(pid), processWtsMap.get(pid));
119 }
120
121 @Override
122 public String getName() {
123 return this.name;
124 }
125
126 @Override
127 public String getPath() {
128 return this.path;
129 }
130
131 @Override
132 public String getCommandLine() {
133 return this.commandLine.get();
134 }
135
136 @Override
137 public List<String> getArguments() {
138 return args.get();
139 }
140
141 @Override
142 public Map<String, String> getEnvironmentVariables() {
143 return cwdCmdEnv.get().getC();
144 }
145
146 @Override
147 public String getCurrentWorkingDirectory() {
148 return currentWorkingDirectory.get();
149 }
150
151 @Override
152 public String getUser() {
153 return userInfo.get().getA();
154 }
155
156 @Override
157 public String getUserID() {
158 return userInfo.get().getB();
159 }
160
161 @Override
162 public String getGroup() {
163 return groupInfo.get().getA();
164 }
165
166 @Override
167 public String getGroupID() {
168 return groupInfo.get().getB();
169 }
170
171 @Override
172 public State getState() {
173 return this.state;
174 }
175
176 @Override
177 public int getParentProcessID() {
178 return this.parentProcessID;
179 }
180
181 @Override
182 public int getThreadCount() {
183 return this.threadCount;
184 }
185
186 @Override
187 public int getPriority() {
188 return this.priority;
189 }
190
191 @Override
192 public long getVirtualSize() {
193 return this.virtualSize;
194 }
195
196 @Override
197 public long getResidentSetSize() {
198 return this.residentSetSize;
199 }
200
201 @Override
202 public long getKernelTime() {
203 return this.kernelTime;
204 }
205
206 @Override
207 public long getUserTime() {
208 return this.userTime;
209 }
210
211 @Override
212 public long getUpTime() {
213 return this.upTime;
214 }
215
216 @Override
217 public long getStartTime() {
218 return this.startTime;
219 }
220
221 @Override
222 public long getBytesRead() {
223 return this.bytesRead;
224 }
225
226 @Override
227 public long getBytesWritten() {
228 return this.bytesWritten;
229 }
230
231 @Override
232 public long getOpenFiles() {
233 return this.openFiles;
234 }
235
236 @Override
237 public long getSoftOpenFileLimit() {
238 return WindowsFileSystem.MAX_WINDOWS_HANDLES;
239 }
240
241 @Override
242 public long getHardOpenFileLimit() {
243 return WindowsFileSystem.MAX_WINDOWS_HANDLES;
244 }
245
246 @Override
247 public int getBitness() {
248 return this.bitness;
249 }
250
251 @Override
252 public long getAffinityMask() {
253 final HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, getProcessID());
254 if (pHandle != null) {
255 try (CloseableULONGptrByReference processAffinity = new CloseableULONGptrByReference();
256 CloseableULONGptrByReference systemAffinity = new CloseableULONGptrByReference()) {
257 if (Kernel32.INSTANCE.GetProcessAffinityMask(pHandle, processAffinity, systemAffinity)) {
258 return Pointer.nativeValue(processAffinity.getValue().toPointer());
259 }
260 } finally {
261 Kernel32.INSTANCE.CloseHandle(pHandle);
262 }
263 }
264 return 0L;
265 }
266
267 @Override
268 public long getMinorFaults() {
269 return this.pageFaults;
270 }
271
272 @Override
273 public List<OSThread> getThreadDetails() {
274 Map<Integer, ThreadPerformanceData.PerfCounterBlock> threads = tcb == null
275 ? queryMatchingThreads(Collections.singleton(this.getProcessID()))
276 : tcb;
277 return threads.entrySet().stream().parallel()
278 .filter(entry -> entry.getValue().getOwningProcessID() == this.getProcessID())
279 .map(entry -> new WindowsOSThread(getProcessID(), entry.getKey(), this.name, entry.getValue()))
280 .collect(Collectors.toList());
281 }
282
283 @Override
284 public boolean updateAttributes() {
285 Set<Integer> pids = Collections.singleton(this.getProcessID());
286
287 Map<Integer, ProcessPerformanceData.PerfCounterBlock> pcb = ProcessPerformanceData
288 .buildProcessMapFromRegistry(null);
289
290 if (pcb == null) {
291 pcb = ProcessPerformanceData.buildProcessMapFromPerfCounters(pids);
292 }
293 if (USE_PROCSTATE_SUSPENDED) {
294 this.tcb = queryMatchingThreads(pids);
295 }
296 Map<Integer, WtsInfo> wts = ProcessWtsData.queryProcessWtsMap(pids);
297 return updateAttributes(pcb.get(this.getProcessID()), wts.get(this.getProcessID()));
298 }
299
300 private boolean updateAttributes(ProcessPerformanceData.PerfCounterBlock pcb, WtsInfo wts) {
301 this.name = pcb.getName();
302 this.path = wts.getPath();
303 this.parentProcessID = pcb.getParentProcessID();
304 this.threadCount = wts.getThreadCount();
305 this.priority = pcb.getPriority();
306 this.virtualSize = wts.getVirtualSize();
307 this.residentSetSize = pcb.getResidentSetSize();
308 this.kernelTime = wts.getKernelTime();
309 this.userTime = wts.getUserTime();
310 this.startTime = pcb.getStartTime();
311 this.upTime = pcb.getUpTime();
312 this.bytesRead = pcb.getBytesRead();
313 this.bytesWritten = pcb.getBytesWritten();
314 this.openFiles = wts.getOpenFiles();
315 this.pageFaults = pcb.getPageFaults();
316
317
318
319
320 this.state = RUNNING;
321 if (this.tcb != null) {
322
323 int pid = this.getProcessID();
324
325 for (ThreadPerformanceData.PerfCounterBlock tpd : this.tcb.values()) {
326 if (tpd.getOwningProcessID() == pid) {
327 if (tpd.getThreadWaitReason() == 5) {
328 this.state = SUSPENDED;
329 } else {
330 this.state = RUNNING;
331 break;
332 }
333 }
334 }
335 }
336
337
338
339 final HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, getProcessID());
340 if (pHandle != null) {
341 try {
342
343 if (IS_VISTA_OR_GREATER && this.bitness == 64) {
344 try (CloseableIntByReference wow64 = new CloseableIntByReference()) {
345 if (Kernel32.INSTANCE.IsWow64Process(pHandle, wow64) && wow64.getValue() > 0) {
346 this.bitness = 32;
347 }
348 }
349 }
350 try {
351 if (IS_WINDOWS7_OR_GREATER) {
352 this.path = Kernel32Util.QueryFullProcessImageName(pHandle, 0);
353 }
354 } catch (Win32Exception e) {
355 this.state = INVALID;
356 }
357 } finally {
358 Kernel32.INSTANCE.CloseHandle(pHandle);
359 }
360 }
361
362 return !this.state.equals(INVALID);
363 }
364
365 private Map<Integer, ThreadPerformanceData.PerfCounterBlock> queryMatchingThreads(Set<Integer> pids) {
366
367 Map<Integer, ThreadPerformanceData.PerfCounterBlock> threads = ThreadPerformanceData
368 .buildThreadMapFromRegistry(pids);
369
370 if (threads == null) {
371 threads = ThreadPerformanceData.buildThreadMapFromPerfCounters(pids, this.getName(), -1);
372 }
373 return threads;
374 }
375
376 private String queryCommandLine() {
377
378 if (!cwdCmdEnv.get().getB().isEmpty()) {
379 return cwdCmdEnv.get().getB();
380 }
381
382 if (USE_BATCH_COMMANDLINE) {
383 return Win32ProcessCached.getInstance().getCommandLine(getProcessID(), getStartTime());
384 }
385
386 WmiResult<CommandLineProperty> commandLineProcs = Win32Process
387 .queryCommandLines(Collections.singleton(getProcessID()));
388 if (commandLineProcs.getResultCount() > 0) {
389 return WmiUtil.getString(commandLineProcs, CommandLineProperty.COMMANDLINE, 0);
390 }
391 return "";
392 }
393
394 private List<String> queryArguments() {
395 String cl = getCommandLine();
396 if (!cl.isEmpty()) {
397 return Arrays.asList(Shell32Util.CommandLineToArgv(cl));
398 }
399 return Collections.emptyList();
400 }
401
402 private String queryCwd() {
403
404 if (!cwdCmdEnv.get().getA().isEmpty()) {
405 return cwdCmdEnv.get().getA();
406 }
407
408 if (getProcessID() == this.os.getProcessId()) {
409 String cwd = new File(".").getAbsolutePath();
410
411 if (!cwd.isEmpty()) {
412 return cwd.substring(0, cwd.length() - 1);
413 }
414 }
415 return "";
416 }
417
418 private Pair<String, String> queryUserInfo() {
419 Pair<String, String> pair = null;
420 final HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, getProcessID());
421 if (pHandle != null) {
422 try (CloseableHANDLEByReference phToken = new CloseableHANDLEByReference()) {
423 try {
424 if (Advapi32.INSTANCE.OpenProcessToken(pHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY,
425 phToken)) {
426 Account account = Advapi32Util.getTokenAccount(phToken.getValue());
427 pair = new Pair<>(account.name, account.sidString);
428 } else {
429 int error = Kernel32.INSTANCE.GetLastError();
430
431 if (error != WinError.ERROR_ACCESS_DENIED) {
432 LOG.error("Failed to get process token for process {}: {}", getProcessID(),
433 Kernel32.INSTANCE.GetLastError());
434 }
435 }
436 } catch (Win32Exception e) {
437 LOG.warn("Failed to query user info for process {} ({}): {}", getProcessID(), getName(),
438 e.getMessage());
439 } finally {
440 final HANDLE token = phToken.getValue();
441 if (token != null) {
442 Kernel32.INSTANCE.CloseHandle(token);
443 }
444 Kernel32.INSTANCE.CloseHandle(pHandle);
445 }
446 }
447 }
448 if (pair == null) {
449 return new Pair<>(Constants.UNKNOWN, Constants.UNKNOWN);
450 }
451 return pair;
452 }
453
454 private Pair<String, String> queryGroupInfo() {
455 Pair<String, String> pair = null;
456 final HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, getProcessID());
457 if (pHandle != null) {
458 try (CloseableHANDLEByReference phToken = new CloseableHANDLEByReference()) {
459 if (Advapi32.INSTANCE.OpenProcessToken(pHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken)) {
460 Account account = Advapi32Util.getTokenPrimaryGroup(phToken.getValue());
461 pair = new Pair<>(account.name, account.sidString);
462 } else {
463 int error = Kernel32.INSTANCE.GetLastError();
464
465 if (error != WinError.ERROR_ACCESS_DENIED) {
466 LOG.error("Failed to get process token for process {}: {}", getProcessID(),
467 Kernel32.INSTANCE.GetLastError());
468 }
469 }
470 final HANDLE token = phToken.getValue();
471 if (token != null) {
472 Kernel32.INSTANCE.CloseHandle(token);
473 }
474 Kernel32.INSTANCE.CloseHandle(pHandle);
475 }
476 }
477 if (pair == null) {
478 return new Pair<>(Constants.UNKNOWN, Constants.UNKNOWN);
479 }
480 return pair;
481 }
482
483 private Triplet<String, String, Map<String, String>> queryCwdCommandlineEnvironment() {
484
485 HANDLE h = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ, false,
486 getProcessID());
487 if (h != null) {
488 try {
489
490 if (WindowsOperatingSystem.isX86() == WindowsOperatingSystem.isWow(h)) {
491 try (CloseableIntByReference nRead = new CloseableIntByReference()) {
492
493 NtDll.PROCESS_BASIC_INFORMATION pbi = new NtDll.PROCESS_BASIC_INFORMATION();
494 int ret = NtDll.INSTANCE.NtQueryInformationProcess(h, NtDll.PROCESS_BASIC_INFORMATION,
495 pbi.getPointer(), pbi.size(), nRead);
496 if (ret != 0) {
497 return defaultCwdCommandlineEnvironment();
498 }
499 pbi.read();
500
501
502 NtDll.PEB peb = new NtDll.PEB();
503 Kernel32.INSTANCE.ReadProcessMemory(h, pbi.PebBaseAddress, peb.getPointer(), peb.size(), nRead);
504 if (nRead.getValue() == 0) {
505 return defaultCwdCommandlineEnvironment();
506 }
507 peb.read();
508
509
510 NtDll.RTL_USER_PROCESS_PARAMETERS upp = new NtDll.RTL_USER_PROCESS_PARAMETERS();
511 Kernel32.INSTANCE.ReadProcessMemory(h, peb.ProcessParameters, upp.getPointer(), upp.size(),
512 nRead);
513 if (nRead.getValue() == 0) {
514 return defaultCwdCommandlineEnvironment();
515 }
516 upp.read();
517
518
519 String cwd = readUnicodeString(h, upp.CurrentDirectory.DosPath);
520 String cl = readUnicodeString(h, upp.CommandLine);
521
522
523 int envSize = upp.EnvironmentSize.intValue();
524 if (envSize > 0) {
525 try (Memory buffer = new Memory(envSize)) {
526 Kernel32.INSTANCE.ReadProcessMemory(h, upp.Environment, buffer, envSize, nRead);
527 if (nRead.getValue() > 0) {
528 char[] env = buffer.getCharArray(0, envSize / 2);
529 Map<String, String> envMap = ParseUtil.parseCharArrayToStringMap(env);
530
531 envMap.remove("");
532 return new Triplet<>(cwd, cl, Collections.unmodifiableMap(envMap));
533 }
534 }
535 }
536 return new Triplet<>(cwd, cl, Collections.emptyMap());
537 }
538 }
539 } finally {
540 Kernel32.INSTANCE.CloseHandle(h);
541 }
542 }
543 return defaultCwdCommandlineEnvironment();
544 }
545
546 private static Triplet<String, String, Map<String, String>> defaultCwdCommandlineEnvironment() {
547 return new Triplet<>("", "", Collections.emptyMap());
548 }
549
550 private static String readUnicodeString(HANDLE h, UNICODE_STRING s) {
551 if (s.Length > 0) {
552
553 try (Memory m = new Memory(s.Length + 2L); CloseableIntByReference nRead = new CloseableIntByReference()) {
554 m.clear();
555 Kernel32.INSTANCE.ReadProcessMemory(h, s.Buffer, m, s.Length, nRead);
556 if (nRead.getValue() > 0) {
557 return m.getWideString(0);
558 }
559 }
560 }
561 return "";
562 }
563 }