| Index: third_party/psutil/psutil/arch/mswindows/process_info.c
|
| diff --git a/third_party/psutil/psutil/arch/mswindows/process_info.c b/third_party/psutil/psutil/arch/mswindows/process_info.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3713eae00910b4bde0c2ec6492de6e8a1e45143d
|
| --- /dev/null
|
| +++ b/third_party/psutil/psutil/arch/mswindows/process_info.c
|
| @@ -0,0 +1,479 @@
|
| +/*
|
| + * $Id: process_info.c 778 2010-11-08 19:59:08Z g.rodola $
|
| + *
|
| + * Helper functions related to fetching process information. Used by
|
| + * _psutil_mswindows module methods.
|
| + */
|
| +
|
| +#include <Python.h>
|
| +#include <windows.h>
|
| +#include <Psapi.h>
|
| +#include <tlhelp32.h>
|
| +
|
| +#include "security.h"
|
| +#include "process_info.h"
|
| +
|
| +
|
| +/*
|
| + * NtQueryInformationProcess code taken from
|
| + * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/
|
| + * typedefs needed to compile against ntdll functions not exposted in the API
|
| + */
|
| +typedef LONG NTSTATUS;
|
| +
|
| +typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
|
| + HANDLE ProcessHandle,
|
| + DWORD ProcessInformationClass,
|
| + PVOID ProcessInformation,
|
| + DWORD ProcessInformationLength,
|
| + PDWORD ReturnLength
|
| + );
|
| +
|
| +typedef struct _UNICODE_STRING
|
| +{
|
| + USHORT Length;
|
| + USHORT MaximumLength;
|
| + PWSTR Buffer;
|
| +} UNICODE_STRING, *PUNICODE_STRING;
|
| +
|
| +typedef struct _PROCESS_BASIC_INFORMATION
|
| +{
|
| + PVOID Reserved1;
|
| + PVOID PebBaseAddress;
|
| + PVOID Reserved2[2];
|
| + ULONG_PTR UniqueProcessId;
|
| + PVOID Reserved3;
|
| +} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
|
| +
|
| +
|
| +/*
|
| + * Set OSError(errno=ESRCH, strerror="No such process") Python exception.
|
| + */
|
| +PyObject *
|
| +NoSuchProcess(void) {
|
| + PyObject *exc;
|
| + char *msg = strerror(ESRCH);
|
| + exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg);
|
| + PyErr_SetObject(PyExc_OSError, exc);
|
| + Py_XDECREF(exc);
|
| + return NULL;
|
| +}
|
| +
|
| +/*
|
| + * Set OSError(errno=EACCES, strerror="No such process") Python exception.
|
| + */
|
| +PyObject *
|
| +AccessDenied(void) {
|
| + PyObject *exc;
|
| + char *msg = strerror(EACCES);
|
| + exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg);
|
| + PyErr_SetObject(PyExc_OSError, exc);
|
| + Py_XDECREF(exc);
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| +/*
|
| + * A wrapper around OpenProcess setting NSP exception if process
|
| + * no longer exists.
|
| + * "pid" is the process pid, "dwDesiredAccess" is the first argument
|
| + * exptected by OpenProcess.
|
| + * Return a process handle or NULL.
|
| + */
|
| +HANDLE
|
| +handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess)
|
| +{
|
| + HANDLE hProcess;
|
| + DWORD processExitCode = 0;
|
| +
|
| + hProcess = OpenProcess(dwDesiredAccess, FALSE, pid);
|
| + if (hProcess == NULL) {
|
| + if (GetLastError() == ERROR_INVALID_PARAMETER) {
|
| + NoSuchProcess();
|
| + }
|
| + else {
|
| + PyErr_SetFromWindowsErr(0);
|
| + }
|
| + return NULL;
|
| + }
|
| +
|
| + /* make sure the process is running */
|
| + GetExitCodeProcess(hProcess, &processExitCode);
|
| + if (processExitCode == 0) {
|
| + NoSuchProcess();
|
| + CloseHandle(hProcess);
|
| + return NULL;
|
| + }
|
| + return hProcess;
|
| +}
|
| +
|
| +
|
| +/*
|
| + * Same as handle_from_pid_waccess but implicitly uses
|
| + * PROCESS_QUERY_INFORMATION | PROCESS_VM_READ as dwDesiredAccess
|
| + * parameter for OpenProcess.
|
| + */
|
| +HANDLE
|
| +handle_from_pid(DWORD pid) {
|
| + DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
|
| + return handle_from_pid_waccess(pid, dwDesiredAccess);
|
| +}
|
| +
|
| +
|
| +// fetch the PEB base address from NtQueryInformationProcess()
|
| +PVOID
|
| +GetPebAddress(HANDLE ProcessHandle)
|
| +{
|
| + _NtQueryInformationProcess NtQueryInformationProcess =
|
| + (_NtQueryInformationProcess)GetProcAddress(
|
| + GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
|
| + PROCESS_BASIC_INFORMATION pbi;
|
| +
|
| + NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);
|
| + return pbi.PebBaseAddress;
|
| +}
|
| +
|
| +
|
| +DWORD*
|
| +get_pids(DWORD *numberOfReturnedPIDs) {
|
| + int procArraySz = 1024;
|
| +
|
| + /* Win32 SDK says the only way to know if our process array
|
| + * wasn't large enough is to check the returned size and make
|
| + * sure that it doesn't match the size of the array.
|
| + * If it does we allocate a larger array and try again*/
|
| +
|
| + /* Stores the actual array */
|
| + DWORD *procArray = NULL;
|
| + DWORD procArrayByteSz;
|
| +
|
| + /* Stores the byte size of the returned array from enumprocesses */
|
| + DWORD enumReturnSz = 0;
|
| +
|
| + do {
|
| + free(procArray);
|
| + procArrayByteSz = procArraySz * sizeof(DWORD);
|
| + procArray = malloc(procArrayByteSz);
|
| +
|
| + if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) {
|
| + free(procArray);
|
| + PyErr_SetFromWindowsErr(0);
|
| + return NULL;
|
| + }
|
| + else if (enumReturnSz == procArrayByteSz) {
|
| + /* Process list was too large. Allocate more space*/
|
| + procArraySz += 1024;
|
| + }
|
| +
|
| + /* else we have a good list */
|
| +
|
| + } while(enumReturnSz == procArraySz * sizeof(DWORD));
|
| +
|
| + /* The number of elements is the returned size / size of each element */
|
| + *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD);
|
| +
|
| + return procArray;
|
| +}
|
| +
|
| +
|
| +int
|
| +is_system_proc(DWORD pid) {
|
| + HANDLE hProcess;
|
| +
|
| + // Special case for PID 0 System Idle Process
|
| + // and PID 4 (SYSTEM)
|
| + if ((pid == 0) || (pid == 4)) {
|
| + return 1;
|
| + }
|
| + if (pid < 0) {
|
| + return 0;
|
| + }
|
| +
|
| + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
|
| + if (NULL == hProcess) {
|
| + // invalid parameter is no such process
|
| + if (GetLastError() == ERROR_INVALID_PARAMETER) {
|
| + return 0;
|
| + }
|
| +
|
| + // access denied obviously means there's a process to deny access to...
|
| + if (GetLastError() == ERROR_ACCESS_DENIED) {
|
| + return 1;
|
| + }
|
| +
|
| + PyErr_SetFromWindowsErr(0);
|
| + return -1;
|
| + }
|
| +
|
| + return HasSystemPrivilege(hProcess);
|
| +}
|
| +
|
| +
|
| +int
|
| +pid_is_running(DWORD pid)
|
| +{
|
| + HANDLE hProcess;
|
| + DWORD exitCode;
|
| +
|
| + // Special case for PID 0 System Idle Process
|
| + if (pid == 0) {
|
| + return 1;
|
| + }
|
| +
|
| + if (pid < 0) {
|
| + return 0;
|
| + }
|
| +
|
| + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
| + FALSE, pid);
|
| + if (NULL == hProcess) {
|
| + // invalid parameter is no such process
|
| + if (GetLastError() == ERROR_INVALID_PARAMETER) {
|
| + CloseHandle(hProcess);
|
| + return 0;
|
| + }
|
| +
|
| + // access denied obviously means there's a process to deny access to...
|
| + if (GetLastError() == ERROR_ACCESS_DENIED) {
|
| + CloseHandle(hProcess);
|
| + return 1;
|
| + }
|
| +
|
| + CloseHandle(hProcess);
|
| + PyErr_SetFromWindowsErr(0);
|
| + return -1;
|
| + }
|
| +
|
| + if (GetExitCodeProcess(hProcess, &exitCode)) {
|
| + CloseHandle(hProcess);
|
| + return (exitCode == STILL_ACTIVE);
|
| + }
|
| +
|
| + // access denied means there's a process there so we'll assume it's running
|
| + if (GetLastError() == ERROR_ACCESS_DENIED) {
|
| + CloseHandle(hProcess);
|
| + return 1;
|
| + }
|
| +
|
| + PyErr_SetFromWindowsErr(0);
|
| + CloseHandle(hProcess);
|
| + return -1;
|
| +}
|
| +
|
| +
|
| +int
|
| +pid_in_proclist(DWORD pid)
|
| +{
|
| + DWORD *proclist = NULL;
|
| + DWORD numberOfReturnedPIDs;
|
| + DWORD i;
|
| +
|
| + proclist = get_pids(&numberOfReturnedPIDs);
|
| + if (NULL == proclist) {
|
| + return NULL;
|
| + }
|
| +
|
| + for (i = 0; i < numberOfReturnedPIDs; i++) {
|
| + if (pid == proclist[i]) {
|
| + free(proclist);
|
| + return 1;
|
| + }
|
| + }
|
| +
|
| + free(proclist);
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +// Check exit code from a process handle. Return FALSE on an error also
|
| +BOOL is_running(HANDLE hProcess)
|
| +{
|
| + DWORD dwCode;
|
| +
|
| + if (NULL == hProcess) {
|
| + return FALSE;
|
| + }
|
| +
|
| + if (GetExitCodeProcess(hProcess, &dwCode)) {
|
| + return (dwCode == STILL_ACTIVE);
|
| + }
|
| + return FALSE;
|
| +}
|
| +
|
| +
|
| +// Return None to represent NoSuchProcess, else return NULL for
|
| +// other exception or the name as a Python string
|
| +PyObject*
|
| +get_name(long pid)
|
| +{
|
| + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
| + PROCESSENTRY32 pe = { 0 };
|
| + pe.dwSize = sizeof(PROCESSENTRY32);
|
| +
|
| + if( Process32First(h, &pe)) {
|
| + do {
|
| + if (pe.th32ProcessID == pid) {
|
| + CloseHandle(h);
|
| + return Py_BuildValue("s", pe.szExeFile);
|
| + }
|
| + } while(Process32Next(h, &pe));
|
| +
|
| + // the process was never found, set NoSuchProcess exception
|
| + NoSuchProcess();
|
| + CloseHandle(h);
|
| + return NULL;
|
| + }
|
| +
|
| + CloseHandle(h);
|
| + return PyErr_SetFromWindowsErr(0);
|
| +}
|
| +
|
| +
|
| +/* returns parent pid (as a Python int) for given pid or None on failure */
|
| +PyObject*
|
| +get_ppid(long pid)
|
| +{
|
| + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
| + PROCESSENTRY32 pe = { 0 };
|
| + pe.dwSize = sizeof(PROCESSENTRY32);
|
| +
|
| + if( Process32First(h, &pe)) {
|
| + do {
|
| + if (pe.th32ProcessID == pid) {
|
| + //printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID);
|
| + CloseHandle(h);
|
| + return Py_BuildValue("I", pe.th32ParentProcessID);
|
| + }
|
| + } while(Process32Next(h, &pe));
|
| +
|
| + // the process was never found, set NoSuchProcess exception
|
| + NoSuchProcess();
|
| + CloseHandle(h);
|
| + return NULL;
|
| + }
|
| +
|
| + CloseHandle(h);
|
| + return PyErr_SetFromWindowsErr(0);
|
| +}
|
| +
|
| +
|
| +
|
| +/*
|
| + * returns a Python list representing the arguments for the process
|
| + * with given pid or NULL on error.
|
| + */
|
| +PyObject*
|
| +get_arg_list(long pid)
|
| +{
|
| + int nArgs, i;
|
| + LPWSTR *szArglist;
|
| + HANDLE hProcess;
|
| + PVOID pebAddress;
|
| + PVOID rtlUserProcParamsAddress;
|
| + UNICODE_STRING commandLine;
|
| + WCHAR *commandLineContents;
|
| + PyObject *arg = NULL;
|
| + PyObject *arg_from_wchar = NULL;
|
| + PyObject *argList = NULL;
|
| +
|
| +
|
| + hProcess = handle_from_pid(pid);
|
| + if(hProcess == NULL) {
|
| + return NULL;
|
| + }
|
| +
|
| + pebAddress = GetPebAddress(hProcess);
|
| +
|
| + /* get the address of ProcessParameters */
|
| +#ifdef _WIN64
|
| + if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32,
|
| + &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
|
| +#else
|
| + if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10,
|
| + &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
|
| +#endif
|
| + {
|
| + //printf("Could not read the address of ProcessParameters!\n");
|
| + PyErr_SetFromWindowsErr(0);
|
| + CloseHandle(hProcess);
|
| + return NULL;
|
| + }
|
| +
|
| + /* read the CommandLine UNICODE_STRING structure */
|
| +#ifdef _WIN64
|
| + if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112,
|
| + &commandLine, sizeof(commandLine), NULL))
|
| +#else
|
| + if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40,
|
| + &commandLine, sizeof(commandLine), NULL))
|
| +#endif
|
| + {
|
| + //printf("Could not read CommandLine!\n");
|
| + PyErr_SetFromWindowsErr(0);
|
| + CloseHandle(hProcess);
|
| + return NULL;
|
| + }
|
| +
|
| +
|
| + /* allocate memory to hold the command line */
|
| + commandLineContents = (WCHAR *)malloc(commandLine.Length+1);
|
| +
|
| + /* read the command line */
|
| + if (!ReadProcessMemory(hProcess, commandLine.Buffer,
|
| + commandLineContents, commandLine.Length, NULL))
|
| + {
|
| + //printf("Could not read the command line string!\n");
|
| + PyErr_SetFromWindowsErr(0);
|
| + CloseHandle(hProcess);
|
| + free(commandLineContents);
|
| + return NULL;
|
| + }
|
| +
|
| + /* print the commandline */
|
| + //printf("%.*S\n", commandLine.Length / 2, commandLineContents);
|
| +
|
| + // null-terminate the string to prevent wcslen from returning incorrect length
|
| + // the length specifier is in characters, but commandLine.Length is in bytes
|
| + commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0';
|
| +
|
| + // attemempt tp parse the command line using Win32 API, fall back on string
|
| + // cmdline version otherwise
|
| + szArglist = CommandLineToArgvW(commandLineContents, &nArgs);
|
| + if (NULL == szArglist) {
|
| + // failed to parse arglist
|
| + // encode as a UTF8 Python string object from WCHAR string
|
| + arg_from_wchar = PyUnicode_FromWideChar(commandLineContents,
|
| + commandLine.Length / 2);
|
| + #if PY_MAJOR_VERSION >= 3
|
| + argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar));
|
| + #else
|
| + argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar));
|
| + #endif
|
| + }
|
| + else {
|
| + // arglist parsed as array of UNICODE_STRING, so convert each to Python
|
| + // string object and add to arg list
|
| + argList = Py_BuildValue("[]");
|
| + for(i=0; i<nArgs; i++) {
|
| + //printf("%d: %.*S (%d characters)\n", i, wcslen(szArglist[i]),
|
| + // szArglist[i], wcslen(szArglist[i]));
|
| + arg_from_wchar = PyUnicode_FromWideChar(szArglist[i],
|
| + wcslen(szArglist[i])
|
| + );
|
| + #if PY_MAJOR_VERSION >= 3
|
| + arg = PyUnicode_FromObject(arg_from_wchar);
|
| + #else
|
| + arg = PyUnicode_AsUTF8String(arg_from_wchar);
|
| + #endif
|
| + Py_XDECREF(arg_from_wchar);
|
| + PyList_Append(argList, arg);
|
| + Py_XDECREF(arg);
|
| + }
|
| + }
|
| +
|
| + LocalFree(szArglist);
|
| + free(commandLineContents);
|
| + CloseHandle(hProcess);
|
| + return argList;
|
| +}
|
| +
|
|
|