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 ? ThreadPerformanceData.buildThreadMapFromPerfCounters(Collections.singleton(this.getProcessID()),
276 this.getName(), -1)
277 : tcb;
278 return threads.entrySet().stream().parallel()
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 = ThreadPerformanceData.buildThreadMapFromRegistry(null);
295
296 if (this.tcb == null) {
297 this.tcb = ThreadPerformanceData.buildThreadMapFromPerfCounters(null);
298 }
299 }
300 Map<Integer, WtsInfo> wts = ProcessWtsData.queryProcessWtsMap(pids);
301 return updateAttributes(pcb.get(this.getProcessID()), wts.get(this.getProcessID()));
302 }
303
304 private boolean updateAttributes(ProcessPerformanceData.PerfCounterBlock pcb, WtsInfo wts) {
305 this.name = pcb.getName();
306 this.path = wts.getPath();
307 this.parentProcessID = pcb.getParentProcessID();
308 this.threadCount = wts.getThreadCount();
309 this.priority = pcb.getPriority();
310 this.virtualSize = wts.getVirtualSize();
311 this.residentSetSize = pcb.getResidentSetSize();
312 this.kernelTime = wts.getKernelTime();
313 this.userTime = wts.getUserTime();
314 this.startTime = pcb.getStartTime();
315 this.upTime = pcb.getUpTime();
316 this.bytesRead = pcb.getBytesRead();
317 this.bytesWritten = pcb.getBytesWritten();
318 this.openFiles = wts.getOpenFiles();
319 this.pageFaults = pcb.getPageFaults();
320
321
322
323
324 this.state = RUNNING;
325 if (this.tcb != null) {
326
327 int pid = this.getProcessID();
328
329 for (ThreadPerformanceData.PerfCounterBlock tpd : this.tcb.values()) {
330 if (tpd.getOwningProcessID() == pid) {
331 if (tpd.getThreadWaitReason() == 5) {
332 this.state = SUSPENDED;
333 } else {
334 this.state = RUNNING;
335 break;
336 }
337 }
338 }
339 }
340
341
342
343 final HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, getProcessID());
344 if (pHandle != null) {
345 try {
346
347 if (IS_VISTA_OR_GREATER && this.bitness == 64) {
348 try (CloseableIntByReference wow64 = new CloseableIntByReference()) {
349 if (Kernel32.INSTANCE.IsWow64Process(pHandle, wow64) && wow64.getValue() > 0) {
350 this.bitness = 32;
351 }
352 }
353 }
354 try {
355 if (IS_WINDOWS7_OR_GREATER) {
356 this.path = Kernel32Util.QueryFullProcessImageName(pHandle, 0);
357 }
358 } catch (Win32Exception e) {
359 this.state = INVALID;
360 }
361 } finally {
362 Kernel32.INSTANCE.CloseHandle(pHandle);
363 }
364 }
365
366 return !this.state.equals(INVALID);
367 }
368
369 private String queryCommandLine() {
370
371 if (!cwdCmdEnv.get().getB().isEmpty()) {
372 return cwdCmdEnv.get().getB();
373 }
374
375 if (USE_BATCH_COMMANDLINE) {
376 return Win32ProcessCached.getInstance().getCommandLine(getProcessID(), getStartTime());
377 }
378
379 WmiResult<CommandLineProperty> commandLineProcs = Win32Process
380 .queryCommandLines(Collections.singleton(getProcessID()));
381 if (commandLineProcs.getResultCount() > 0) {
382 return WmiUtil.getString(commandLineProcs, CommandLineProperty.COMMANDLINE, 0);
383 }
384 return "";
385 }
386
387 private List<String> queryArguments() {
388 String cl = getCommandLine();
389 if (!cl.isEmpty()) {
390 return Arrays.asList(Shell32Util.CommandLineToArgv(cl));
391 }
392 return Collections.emptyList();
393 }
394
395 private String queryCwd() {
396
397 if (!cwdCmdEnv.get().getA().isEmpty()) {
398 return cwdCmdEnv.get().getA();
399 }
400
401 if (getProcessID() == this.os.getProcessId()) {
402 String cwd = new File(".").getAbsolutePath();
403
404 if (!cwd.isEmpty()) {
405 return cwd.substring(0, cwd.length() - 1);
406 }
407 }
408 return "";
409 }
410
411 private Pair<String, String> queryUserInfo() {
412 Pair<String, String> pair = null;
413 final HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, getProcessID());
414 if (pHandle != null) {
415 try (CloseableHANDLEByReference phToken = new CloseableHANDLEByReference()) {
416 try {
417 if (Advapi32.INSTANCE.OpenProcessToken(pHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY,
418 phToken)) {
419 Account account = Advapi32Util.getTokenAccount(phToken.getValue());
420 pair = new Pair<>(account.name, account.sidString);
421 } else {
422 int error = Kernel32.INSTANCE.GetLastError();
423
424 if (error != WinError.ERROR_ACCESS_DENIED) {
425 LOG.error("Failed to get process token for process {}: {}", getProcessID(),
426 Kernel32.INSTANCE.GetLastError());
427 }
428 }
429 } catch (Win32Exception e) {
430 LOG.warn("Failed to query user info for process {} ({}): {}", getProcessID(), getName(),
431 e.getMessage());
432 } finally {
433 final HANDLE token = phToken.getValue();
434 if (token != null) {
435 Kernel32.INSTANCE.CloseHandle(token);
436 }
437 Kernel32.INSTANCE.CloseHandle(pHandle);
438 }
439 }
440 }
441 if (pair == null) {
442 return new Pair<>(Constants.UNKNOWN, Constants.UNKNOWN);
443 }
444 return pair;
445 }
446
447 private Pair<String, String> queryGroupInfo() {
448 Pair<String, String> pair = null;
449 final HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, getProcessID());
450 if (pHandle != null) {
451 try (CloseableHANDLEByReference phToken = new CloseableHANDLEByReference()) {
452 if (Advapi32.INSTANCE.OpenProcessToken(pHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken)) {
453 Account account = Advapi32Util.getTokenPrimaryGroup(phToken.getValue());
454 pair = new Pair<>(account.name, account.sidString);
455 } else {
456 int error = Kernel32.INSTANCE.GetLastError();
457
458 if (error != WinError.ERROR_ACCESS_DENIED) {
459 LOG.error("Failed to get process token for process {}: {}", getProcessID(),
460 Kernel32.INSTANCE.GetLastError());
461 }
462 }
463 final HANDLE token = phToken.getValue();
464 if (token != null) {
465 Kernel32.INSTANCE.CloseHandle(token);
466 }
467 Kernel32.INSTANCE.CloseHandle(pHandle);
468 }
469 }
470 if (pair == null) {
471 return new Pair<>(Constants.UNKNOWN, Constants.UNKNOWN);
472 }
473 return pair;
474 }
475
476 private Triplet<String, String, Map<String, String>> queryCwdCommandlineEnvironment() {
477
478 HANDLE h = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ, false,
479 getProcessID());
480 if (h != null) {
481 try {
482
483 if (WindowsOperatingSystem.isX86() == WindowsOperatingSystem.isWow(h)) {
484 try (CloseableIntByReference nRead = new CloseableIntByReference()) {
485
486 NtDll.PROCESS_BASIC_INFORMATION pbi = new NtDll.PROCESS_BASIC_INFORMATION();
487 int ret = NtDll.INSTANCE.NtQueryInformationProcess(h, NtDll.PROCESS_BASIC_INFORMATION,
488 pbi.getPointer(), pbi.size(), nRead);
489 if (ret != 0) {
490 return defaultCwdCommandlineEnvironment();
491 }
492 pbi.read();
493
494
495 NtDll.PEB peb = new NtDll.PEB();
496 Kernel32.INSTANCE.ReadProcessMemory(h, pbi.PebBaseAddress, peb.getPointer(), peb.size(), nRead);
497 if (nRead.getValue() == 0) {
498 return defaultCwdCommandlineEnvironment();
499 }
500 peb.read();
501
502
503 NtDll.RTL_USER_PROCESS_PARAMETERS upp = new NtDll.RTL_USER_PROCESS_PARAMETERS();
504 Kernel32.INSTANCE.ReadProcessMemory(h, peb.ProcessParameters, upp.getPointer(), upp.size(),
505 nRead);
506 if (nRead.getValue() == 0) {
507 return defaultCwdCommandlineEnvironment();
508 }
509 upp.read();
510
511
512 String cwd = readUnicodeString(h, upp.CurrentDirectory.DosPath);
513 String cl = readUnicodeString(h, upp.CommandLine);
514
515
516 int envSize = upp.EnvironmentSize.intValue();
517 if (envSize > 0) {
518 try (Memory buffer = new Memory(envSize)) {
519 Kernel32.INSTANCE.ReadProcessMemory(h, upp.Environment, buffer, envSize, nRead);
520 if (nRead.getValue() > 0) {
521 char[] env = buffer.getCharArray(0, envSize / 2);
522 Map<String, String> envMap = ParseUtil.parseCharArrayToStringMap(env);
523
524 envMap.remove("");
525 return new Triplet<>(cwd, cl, Collections.unmodifiableMap(envMap));
526 }
527 }
528 }
529 return new Triplet<>(cwd, cl, Collections.emptyMap());
530 }
531 }
532 } finally {
533 Kernel32.INSTANCE.CloseHandle(h);
534 }
535 }
536 return defaultCwdCommandlineEnvironment();
537 }
538
539 private static Triplet<String, String, Map<String, String>> defaultCwdCommandlineEnvironment() {
540 return new Triplet<>("", "", Collections.emptyMap());
541 }
542
543 private static String readUnicodeString(HANDLE h, UNICODE_STRING s) {
544 if (s.Length > 0) {
545
546 try (Memory m = new Memory(s.Length + 2L); CloseableIntByReference nRead = new CloseableIntByReference()) {
547 m.clear();
548 Kernel32.INSTANCE.ReadProcessMemory(h, s.Buffer, m, s.Length, nRead);
549 if (nRead.getValue() > 0) {
550 return m.getWideString(0);
551 }
552 }
553 }
554 return "";
555 }
556 }