Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(427)

Side by Side Diff: third_party/psutil/psutil/arch/mswindows/process_info.c

Issue 8919026: Remove psutil from tree, install via install-build-deps.sh (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * $Id: process_info.c 1142 2011-10-05 18:45:49Z g.rodola $
3 *
4 * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 *
8 * Helper functions related to fetching process information. Used by
9 * _psutil_mswindows module methods.
10 */
11
12 #include <Python.h>
13 #include <windows.h>
14 #include <Psapi.h>
15 #include <tlhelp32.h>
16
17 #include "security.h"
18 #include "process_info.h"
19 #include "ntextapi.h"
20
21 /*
22 * NtQueryInformationProcess code taken from
23 * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/
24 * typedefs needed to compile against ntdll functions not exposted in the API
25 */
26 typedef LONG NTSTATUS;
27
28 typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
29 HANDLE ProcessHandle,
30 DWORD ProcessInformationClass,
31 PVOID ProcessInformation,
32 DWORD ProcessInformationLength,
33 PDWORD ReturnLength
34 );
35
36 typedef struct _PROCESS_BASIC_INFORMATION
37 {
38 PVOID Reserved1;
39 PVOID PebBaseAddress;
40 PVOID Reserved2[2];
41 ULONG_PTR UniqueProcessId;
42 PVOID Reserved3;
43 } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
44
45
46 /*
47 * A wrapper around OpenProcess setting NSP exception if process
48 * no longer exists.
49 * "pid" is the process pid, "dwDesiredAccess" is the first argument
50 * exptected by OpenProcess.
51 * Return a process handle or NULL.
52 */
53 HANDLE
54 handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess)
55 {
56 HANDLE hProcess;
57 DWORD processExitCode = 0;
58
59 hProcess = OpenProcess(dwDesiredAccess, FALSE, pid);
60 if (hProcess == NULL) {
61 if (GetLastError() == ERROR_INVALID_PARAMETER) {
62 NoSuchProcess();
63 }
64 else {
65 PyErr_SetFromWindowsErr(0);
66 }
67 return NULL;
68 }
69
70 /* make sure the process is running */
71 GetExitCodeProcess(hProcess, &processExitCode);
72 if (processExitCode == 0) {
73 NoSuchProcess();
74 CloseHandle(hProcess);
75 return NULL;
76 }
77 return hProcess;
78 }
79
80
81 /*
82 * Same as handle_from_pid_waccess but implicitly uses
83 * PROCESS_QUERY_INFORMATION | PROCESS_VM_READ as dwDesiredAccess
84 * parameter for OpenProcess.
85 */
86 HANDLE
87 handle_from_pid(DWORD pid) {
88 DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
89 return handle_from_pid_waccess(pid, dwDesiredAccess);
90 }
91
92
93 // fetch the PEB base address from NtQueryInformationProcess()
94 PVOID
95 GetPebAddress(HANDLE ProcessHandle)
96 {
97 _NtQueryInformationProcess NtQueryInformationProcess =
98 (_NtQueryInformationProcess)GetProcAddress(
99 GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
100 PROCESS_BASIC_INFORMATION pbi;
101
102 NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);
103 return pbi.PebBaseAddress;
104 }
105
106
107 DWORD*
108 get_pids(DWORD *numberOfReturnedPIDs) {
109 int procArraySz = 1024;
110
111 /* Win32 SDK says the only way to know if our process array
112 * wasn't large enough is to check the returned size and make
113 * sure that it doesn't match the size of the array.
114 * If it does we allocate a larger array and try again*/
115
116 /* Stores the actual array */
117 DWORD *procArray = NULL;
118 DWORD procArrayByteSz;
119
120 /* Stores the byte size of the returned array from enumprocesses */
121 DWORD enumReturnSz = 0;
122
123 do {
124 free(procArray);
125 procArrayByteSz = procArraySz * sizeof(DWORD);
126 procArray = malloc(procArrayByteSz);
127
128 if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) {
129 free(procArray);
130 PyErr_SetFromWindowsErr(0);
131 return NULL;
132 }
133 else if (enumReturnSz == procArrayByteSz) {
134 /* Process list was too large. Allocate more space*/
135 procArraySz += 1024;
136 }
137
138 /* else we have a good list */
139
140 } while(enumReturnSz == procArraySz * sizeof(DWORD));
141
142 /* The number of elements is the returned size / size of each element */
143 *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD);
144
145 return procArray;
146 }
147
148
149 int
150 pid_is_running(DWORD pid)
151 {
152 HANDLE hProcess;
153 DWORD exitCode;
154
155 // Special case for PID 0 System Idle Process
156 if (pid == 0) {
157 return 1;
158 }
159
160 if (pid < 0) {
161 return 0;
162 }
163
164 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
165 FALSE, pid);
166 if (NULL == hProcess) {
167 // invalid parameter is no such process
168 if (GetLastError() == ERROR_INVALID_PARAMETER) {
169 CloseHandle(hProcess);
170 return 0;
171 }
172
173 // access denied obviously means there's a process to deny access to...
174 if (GetLastError() == ERROR_ACCESS_DENIED) {
175 CloseHandle(hProcess);
176 return 1;
177 }
178
179 CloseHandle(hProcess);
180 PyErr_SetFromWindowsErr(0);
181 return -1;
182 }
183
184 if (GetExitCodeProcess(hProcess, &exitCode)) {
185 CloseHandle(hProcess);
186 return (exitCode == STILL_ACTIVE);
187 }
188
189 // access denied means there's a process there so we'll assume it's running
190 if (GetLastError() == ERROR_ACCESS_DENIED) {
191 CloseHandle(hProcess);
192 return 1;
193 }
194
195 PyErr_SetFromWindowsErr(0);
196 CloseHandle(hProcess);
197 return -1;
198 }
199
200
201 int
202 pid_in_proclist(DWORD pid)
203 {
204 DWORD *proclist = NULL;
205 DWORD numberOfReturnedPIDs;
206 DWORD i;
207
208 proclist = get_pids(&numberOfReturnedPIDs);
209 if (NULL == proclist) {
210 return -1;
211 }
212
213 for (i = 0; i < numberOfReturnedPIDs; i++) {
214 if (pid == proclist[i]) {
215 free(proclist);
216 return 1;
217 }
218 }
219
220 free(proclist);
221 return 0;
222 }
223
224
225 // Check exit code from a process handle. Return FALSE on an error also
226 BOOL is_running(HANDLE hProcess)
227 {
228 DWORD dwCode;
229
230 if (NULL == hProcess) {
231 return FALSE;
232 }
233
234 if (GetExitCodeProcess(hProcess, &dwCode)) {
235 return (dwCode == STILL_ACTIVE);
236 }
237 return FALSE;
238 }
239
240
241 // Return None to represent NoSuchProcess, else return NULL for
242 // other exception or the name as a Python string
243 PyObject*
244 get_name(long pid)
245 {
246 HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
247 PROCESSENTRY32 pe = { 0 };
248 pe.dwSize = sizeof(PROCESSENTRY32);
249
250 if( Process32First(h, &pe)) {
251 do {
252 if (pe.th32ProcessID == pid) {
253 CloseHandle(h);
254 return Py_BuildValue("s", pe.szExeFile);
255 }
256 } while(Process32Next(h, &pe));
257
258 // the process was never found, set NoSuchProcess exception
259 NoSuchProcess();
260 CloseHandle(h);
261 return NULL;
262 }
263
264 CloseHandle(h);
265 return PyErr_SetFromWindowsErr(0);
266 }
267
268
269 /* returns parent pid (as a Python int) for given pid or None on failure */
270 PyObject*
271 get_ppid(long pid)
272 {
273 HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
274 PROCESSENTRY32 pe = { 0 };
275 pe.dwSize = sizeof(PROCESSENTRY32);
276
277 if( Process32First(h, &pe)) {
278 do {
279 if (pe.th32ProcessID == pid) {
280 ////printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID);
281 CloseHandle(h);
282 return Py_BuildValue("I", pe.th32ParentProcessID);
283 }
284 } while(Process32Next(h, &pe));
285
286 // the process was never found, set NoSuchProcess exception
287 NoSuchProcess();
288 CloseHandle(h);
289 return NULL;
290 }
291
292 CloseHandle(h);
293 return PyErr_SetFromWindowsErr(0);
294 }
295
296
297
298 /*
299 * returns a Python list representing the arguments for the process
300 * with given pid or NULL on error.
301 */
302 PyObject*
303 get_arg_list(long pid)
304 {
305 int nArgs, i;
306 LPWSTR *szArglist;
307 HANDLE hProcess;
308 PVOID pebAddress;
309 PVOID rtlUserProcParamsAddress;
310 UNICODE_STRING commandLine;
311 WCHAR *commandLineContents;
312 PyObject *arg = NULL;
313 PyObject *arg_from_wchar = NULL;
314 PyObject *argList = NULL;
315
316 hProcess = handle_from_pid(pid);
317 if(hProcess == NULL) {
318 return NULL;
319 }
320
321 pebAddress = GetPebAddress(hProcess);
322
323 /* get the address of ProcessParameters */
324 #ifdef _WIN64
325 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32,
326 &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
327 #else
328 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10,
329 &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
330 #endif
331 {
332 ////printf("Could not read the address of ProcessParameters!\n");
333 PyErr_SetFromWindowsErr(0);
334 CloseHandle(hProcess);
335 return NULL;
336 }
337
338 /* read the CommandLine UNICODE_STRING structure */
339 #ifdef _WIN64
340 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112,
341 &commandLine, sizeof(commandLine), NULL))
342 #else
343 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40,
344 &commandLine, sizeof(commandLine), NULL))
345 #endif
346 {
347 ////printf("Could not read CommandLine!\n");
348 CloseHandle(hProcess);
349 PyErr_SetFromWindowsErr(0);
350 return NULL;
351 }
352
353
354 /* allocate memory to hold the command line */
355 commandLineContents = (WCHAR *)malloc(commandLine.Length+1);
356
357 /* read the command line */
358 if (!ReadProcessMemory(hProcess, commandLine.Buffer,
359 commandLineContents, commandLine.Length, NULL))
360 {
361 ////printf("Could not read the command line string!\n");
362 CloseHandle(hProcess);
363 PyErr_SetFromWindowsErr(0);
364 free(commandLineContents);
365 return NULL;
366 }
367
368 /* print the commandline */
369 ////printf("%.*S\n", commandLine.Length / 2, commandLineContents);
370
371 // null-terminate the string to prevent wcslen from returning incorrect leng th
372 // the length specifier is in characters, but commandLine.Length is in bytes
373 commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0';
374
375 // attemempt tp parse the command line using Win32 API, fall back on string
376 // cmdline version otherwise
377 szArglist = CommandLineToArgvW(commandLineContents, &nArgs);
378 if (NULL == szArglist) {
379 // failed to parse arglist
380 // encode as a UTF8 Python string object from WCHAR string
381 arg_from_wchar = PyUnicode_FromWideChar(commandLineContents,
382 commandLine.Length / 2);
383 #if PY_MAJOR_VERSION >= 3
384 argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar)) ;
385 #else
386 argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar));
387 #endif
388 }
389 else {
390 // arglist parsed as array of UNICODE_STRING, so convert each to Python
391 // string object and add to arg list
392 argList = Py_BuildValue("[]");
393 for(i=0; i<nArgs; i++) {
394 ////printf("%d: %.*S (%d characters)\n", i, wcslen(szArglist[i]),
395 // szArglist[i], wcslen(szArglist[i]));
396 arg_from_wchar = PyUnicode_FromWideChar(szArglist[i],
397 wcslen(szArglist[i])
398 );
399 #if PY_MAJOR_VERSION >= 3
400 arg = PyUnicode_FromObject(arg_from_wchar);
401 #else
402 arg = PyUnicode_AsUTF8String(arg_from_wchar);
403 #endif
404 Py_XDECREF(arg_from_wchar);
405 PyList_Append(argList, arg);
406 Py_XDECREF(arg);
407 }
408 }
409
410 LocalFree(szArglist);
411 free(commandLineContents);
412 CloseHandle(hProcess);
413 return argList;
414 }
415
416
417 #define PH_FIRST_PROCESS(Processes) ((PSYSTEM_PROCESS_INFORMATION)(Processes))
418
419 #define PH_NEXT_PROCESS(Process) ( \
420 ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \
421 (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \
422 ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : \
423 NULL \
424 )
425
426 const STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
427 const STATUS_BUFFER_TOO_SMALL = 0xC0000023L;
428
429 /*
430 * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure
431 * fills the structure with process information.
432 * On success return 1, else 0 with Python exception already set.
433 */
434 int
435 get_process_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, PVOID *retB uffer)
436 {
437 static ULONG initialBufferSize = 0x4000;
438 NTSTATUS status;
439 PVOID buffer;
440 ULONG bufferSize;
441 PSYSTEM_PROCESS_INFORMATION process;
442
443 // get NtQuerySystemInformation
444 typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG);
445 NTQSI_PROC NtQuerySystemInformation;
446 HINSTANCE hNtDll;
447 hNtDll = LoadLibrary(TEXT("ntdll.dll"));
448 NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress(
449 hNtDll, "NtQuerySystemInformation");
450
451 bufferSize = initialBufferSize;
452 buffer = malloc(bufferSize);
453
454 while (TRUE) {
455 status = NtQuerySystemInformation(SystemProcessInformation, buffer,
456 bufferSize, &bufferSize);
457
458 if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MI SMATCH)
459 {
460 free(buffer);
461 buffer = malloc(bufferSize);
462 }
463 else {
464 break;
465 }
466 }
467
468 if (status != 0) {
469 PyErr_Format(PyExc_RuntimeError, "NtQuerySystemInformation() failed");
470 return 0;
471 }
472
473 if (bufferSize <= 0x20000) {
474 initialBufferSize = bufferSize;
475 }
476
477 process = PH_FIRST_PROCESS(buffer);
478 do {
479 if (process->UniqueProcessId == (HANDLE)pid) {
480 *retProcess = process;
481 *retBuffer = buffer;
482 return 1;
483 }
484 } while ( (process = PH_NEXT_PROCESS(process)) );
485
486 NoSuchProcess();
487 return 0;
488 }
489
490
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698