/**
* Oshi (https://github.com/oshi/oshi)
*
* Copyright (c) 2010 - 2017 The Oshi Project Team
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Maintainers:
* dblock[at]dblock[dot]org
* widdis[at]gmail[dot]com
* enrico.bianchi[at]gmail[dot]com
*
* Contributors:
* https://github.com/oshi/oshi/graphs/contributors
*/
package oshi.software.os.windows;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Advapi32;
import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.Advapi32Util.Account;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
import oshi.jna.platform.windows.Psapi;
import oshi.jna.platform.windows.Psapi.PERFORMANCE_INFORMATION;
import oshi.software.common.AbstractOperatingSystem;
import oshi.software.os.FileSystem;
import oshi.software.os.NetworkParams;
import oshi.software.os.OSProcess;
import oshi.util.FormatUtil;
import oshi.util.ParseUtil;
import oshi.util.platform.windows.WmiUtil;
import oshi.util.platform.windows.WmiUtil.ValueType;
public class WindowsOperatingSystem extends AbstractOperatingSystem {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(WindowsOperatingSystem.class);
// For WMI Process queries
private static String processProperties = "Name,ExecutablePath,CommandLine,ExecutionState,ProcessID,ParentProcessId"
+ ",ThreadCount,Priority,VirtualSize,WorkingSetSize,KernelModeTime,UserModeTime,CreationDate"
+ ",ReadTransferCount,WriteTransferCount,__PATH,__PATH";
private static ValueType[] processPropertyTypes = { ValueType.STRING, ValueType.STRING, ValueType.STRING,
ValueType.UINT32, ValueType.UINT32, ValueType.UINT32, ValueType.UINT32, ValueType.UINT32, ValueType.STRING,
ValueType.STRING, ValueType.STRING, ValueType.STRING, ValueType.DATETIME, ValueType.UINT64,
ValueType.UINT64, ValueType.PROCESS_GETOWNER, ValueType.PROCESS_GETOWNERSID };
/*
* Windows Execution States:
*/
private static final int UNKNOWN = 0;
private static final int OTHER = 1;
private static final int READY = 2;
private static final int RUNNING = 3;
private static final int BLOCKED = 4;
private static final int SUSPENDED_BLOCKED = 5;
private static final int SUSPENDED_READY = 6;
private static final int TERMINATED = 7;
private static final int STOPPED = 8;
private static final int GROWING = 9;
/*
* LastError
*/
private static final int ERROR_ACCESS_DENIED = 5;
static {
enableDebugPrivilege();
}
public WindowsOperatingSystem() {
this.manufacturer = "Microsoft";
this.family = "Windows";
this.version = new WindowsOSVersionInfoEx();
}
/**
* {@inheritDoc}
*/
@Override
public FileSystem getFileSystem() {
return new WindowsFileSystem();
}
/**
* {@inheritDoc}
*/
@Override
public OSProcess[] getProcesses(int limit, ProcessSort sort) {
Map<String, List<Object>> procs = WmiUtil.selectObjectsFrom(null, "Win32_Process", processProperties, null,
processPropertyTypes);
List<OSProcess> procList = processMapToList(procs);
List<OSProcess> sorted = processSort(procList, limit, sort);
return sorted.toArray(new OSProcess[sorted.size()]);
}
/**
* {@inheritDoc}
*/
@Override
public OSProcess getProcess(int pid) {
Map<String, List<Object>> procs = WmiUtil.selectObjectsFrom(null, "Win32_Process", processProperties,
String.format("WHERE ProcessId=%d", pid), processPropertyTypes);
List<OSProcess> procList = processMapToList(procs);
return procList.isEmpty() ? null : procList.get(0);
}
private List<OSProcess> processMapToList(Map<String, List<Object>> procs) {
long now = System.currentTimeMillis();
List<OSProcess> procList = new ArrayList<>();
List<String> groupList = new ArrayList<>();
List<String> groupIDList = new ArrayList<>();
// All map lists should be the same length. Pick one size and iterate
final int procCount = procs.get("Name").size();
int myPid = getProcessId();
for (int p = 0; p < procCount; p++) {
OSProcess proc = new OSProcess();
proc.setName((String) procs.get("Name").get(p));
proc.setPath((String) procs.get("ExecutablePath").get(p));
proc.setCommandLine((String) procs.get("CommandLine").get(p));
proc.setProcessID(((Long) procs.get("ProcessID").get(p)).intValue());
if (myPid == proc.getProcessID()) {
proc.setCurrentWorkingDirectory(new File(".").getAbsolutePath());
}
proc.setParentProcessID(((Long) procs.get("ParentProcessId").get(p)).intValue());
proc.setUser((String) procs.get("PROCESS_GETOWNER").get(p));
proc.setUserID((String) procs.get("PROCESS_GETOWNERSID").get(p));
// Fetching group information incurs significant latency.
// Only do for single-process queries
if (procCount == 1) {
final HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(
WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ, false, proc.getProcessID());
if (pHandle != null) {
final HANDLEByReference phToken = new HANDLEByReference();
if (Advapi32.INSTANCE.OpenProcessToken(pHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY,
phToken)) {
Account[] accounts = Advapi32Util.getTokenGroups(phToken.getValue());
// get groups
groupList.clear();
groupIDList.clear();
for (Account account : accounts) {
groupList.add(account.name);
groupIDList.add(account.sidString);
}
proc.setGroup(FormatUtil.join(",", groupList));
proc.setGroupID(FormatUtil.join(",", groupIDList));
} else {
int error = Kernel32.INSTANCE.GetLastError();
// Access denied errors are common and will silently
// fail
if (error != ERROR_ACCESS_DENIED) {
LOG.error("Failed to get process token for process {}: {}", proc.getProcessID(),
Kernel32.INSTANCE.GetLastError());
}
}
}
Kernel32.INSTANCE.CloseHandle(pHandle);
}
switch (((Long) procs.get("ExecutionState").get(p)).intValue()) {
case READY:
case SUSPENDED_READY:
proc.setState(OSProcess.State.SLEEPING);
break;
case BLOCKED:
case SUSPENDED_BLOCKED:
proc.setState(OSProcess.State.WAITING);
break;
case RUNNING:
proc.setState(OSProcess.State.RUNNING);
break;
case GROWING:
proc.setState(OSProcess.State.NEW);
break;
case TERMINATED:
proc.setState(OSProcess.State.ZOMBIE);
break;
case STOPPED:
proc.setState(OSProcess.State.STOPPED);
break;
case UNKNOWN:
case OTHER:
default:
proc.setState(OSProcess.State.OTHER);
break;
}
proc.setThreadCount(((Long) procs.get("ThreadCount").get(p)).intValue());
proc.setPriority(((Long) procs.get("Priority").get(p)).intValue());
proc.setVirtualSize(ParseUtil.parseLongOrDefault((String) procs.get("VirtualSize").get(p), 0L));
proc.setResidentSetSize(ParseUtil.parseLongOrDefault((String) procs.get("WorkingSetSize").get(p), 0L));
// Kernel and User time units are 100ns
proc.setKernelTime(ParseUtil.parseLongOrDefault((String) procs.get("KernelModeTime").get(p), 0L) / 10000L);
proc.setUserTime(ParseUtil.parseLongOrDefault((String) procs.get("UserModeTime").get(p), 0L) / 10000L);
proc.setStartTime((Long) procs.get("CreationDate").get(p));
proc.setUpTime(now - proc.getStartTime());
proc.setBytesRead((Long) procs.get("ReadTransferCount").get(p));
proc.setBytesWritten((Long) procs.get("WriteTransferCount").get(p));
procList.add(proc);
}
return procList;
}
/**
* {@inheritDoc}
*/
@Override
public int getProcessId() {
return Kernel32.INSTANCE.GetCurrentProcessId();
}
/**
* {@inheritDoc}
*/
@Override
public int getProcessCount() {
PERFORMANCE_INFORMATION perfInfo = new PERFORMANCE_INFORMATION();
if (!Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size())) {
LOG.error("Failed to get Performance Info. Error code: {}", Kernel32.INSTANCE.GetLastError());
return 0;
}
return perfInfo.ProcessCount.intValue();
}
/**
* {@inheritDoc}
*/
@Override
public int getThreadCount() {
PERFORMANCE_INFORMATION perfInfo = new PERFORMANCE_INFORMATION();
if (!Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size())) {
LOG.error("Failed to get Performance Info. Error code: {}", Kernel32.INSTANCE.GetLastError());
return 0;
}
return perfInfo.ThreadCount.intValue();
}
/**
* {@inheritDoc}
*/
@Override
public NetworkParams getNetworkParams() {
return new WindowsNetworkParams();
}
/**
* Enables debug privileges for this process, required for OpenProcess() to
* get processes other than the current user
*/
private static void enableDebugPrivilege() {
HANDLEByReference hToken = new HANDLEByReference();
boolean success = Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(),
WinNT.TOKEN_QUERY | WinNT.TOKEN_ADJUST_PRIVILEGES, hToken);
if (!success) {
LOG.error("OpenProcessToken failed. Error: {}" + Native.getLastError());
return;
}
WinNT.LUID luid = new WinNT.LUID();
success = Advapi32.INSTANCE.LookupPrivilegeValue(null, WinNT.SE_DEBUG_NAME, luid);
if (!success) {
LOG.error("LookupprivilegeValue failed. Error: {}" + Native.getLastError());
return;
}
WinNT.TOKEN_PRIVILEGES tkp = new WinNT.TOKEN_PRIVILEGES(1);
tkp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(luid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
success = Advapi32.INSTANCE.AdjustTokenPrivileges(hToken.getValue(), false, tkp, 0, null, null);
if (!success) {
LOG.error("AdjustTokenPrivileges failed. Error: {}" + Native.getLastError());
}
Kernel32.INSTANCE.CloseHandle(hToken.getValue());
}
}