| OLD | NEW |
| 1 /* | 1 /* |
| 2 * $Id: process_info.c 778 2010-11-08 19:59:08Z g.rodola $ | 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. |
| 3 * | 7 * |
| 4 * Helper functions related to fetching process information. Used by | 8 * Helper functions related to fetching process information. Used by |
| 5 * _psutil_mswindows module methods. | 9 * _psutil_mswindows module methods. |
| 6 */ | 10 */ |
| 7 | 11 |
| 8 #include <Python.h> | 12 #include <Python.h> |
| 9 #include <windows.h> | 13 #include <windows.h> |
| 10 #include <Psapi.h> | 14 #include <Psapi.h> |
| 11 #include <tlhelp32.h> | 15 #include <tlhelp32.h> |
| 12 | 16 |
| 13 #include "security.h" | 17 #include "security.h" |
| 14 #include "process_info.h" | 18 #include "process_info.h" |
| 15 | 19 #include "ntextapi.h" |
| 16 | 20 |
| 17 /* | 21 /* |
| 18 * NtQueryInformationProcess code taken from | 22 * NtQueryInformationProcess code taken from |
| 19 * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/ | 23 * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/ |
| 20 * typedefs needed to compile against ntdll functions not exposted in the API | 24 * typedefs needed to compile against ntdll functions not exposted in the API |
| 21 */ | 25 */ |
| 22 typedef LONG NTSTATUS; | 26 typedef LONG NTSTATUS; |
| 23 | 27 |
| 24 typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( | 28 typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( |
| 25 HANDLE ProcessHandle, | 29 HANDLE ProcessHandle, |
| 26 DWORD ProcessInformationClass, | 30 DWORD ProcessInformationClass, |
| 27 PVOID ProcessInformation, | 31 PVOID ProcessInformation, |
| 28 DWORD ProcessInformationLength, | 32 DWORD ProcessInformationLength, |
| 29 PDWORD ReturnLength | 33 PDWORD ReturnLength |
| 30 ); | 34 ); |
| 31 | 35 |
| 32 typedef struct _UNICODE_STRING | |
| 33 { | |
| 34 USHORT Length; | |
| 35 USHORT MaximumLength; | |
| 36 PWSTR Buffer; | |
| 37 } UNICODE_STRING, *PUNICODE_STRING; | |
| 38 | |
| 39 typedef struct _PROCESS_BASIC_INFORMATION | 36 typedef struct _PROCESS_BASIC_INFORMATION |
| 40 { | 37 { |
| 41 PVOID Reserved1; | 38 PVOID Reserved1; |
| 42 PVOID PebBaseAddress; | 39 PVOID PebBaseAddress; |
| 43 PVOID Reserved2[2]; | 40 PVOID Reserved2[2]; |
| 44 ULONG_PTR UniqueProcessId; | 41 ULONG_PTR UniqueProcessId; |
| 45 PVOID Reserved3; | 42 PVOID Reserved3; |
| 46 } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; | 43 } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; |
| 47 | 44 |
| 48 | 45 |
| 49 /* | 46 /* |
| 50 * Set OSError(errno=ESRCH, strerror="No such process") Python exception. | |
| 51 */ | |
| 52 PyObject * | |
| 53 NoSuchProcess(void) { | |
| 54 PyObject *exc; | |
| 55 char *msg = strerror(ESRCH); | |
| 56 exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg); | |
| 57 PyErr_SetObject(PyExc_OSError, exc); | |
| 58 Py_XDECREF(exc); | |
| 59 return NULL; | |
| 60 } | |
| 61 | |
| 62 /* | |
| 63 * Set OSError(errno=EACCES, strerror="No such process") Python exception. | |
| 64 */ | |
| 65 PyObject * | |
| 66 AccessDenied(void) { | |
| 67 PyObject *exc; | |
| 68 char *msg = strerror(EACCES); | |
| 69 exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg); | |
| 70 PyErr_SetObject(PyExc_OSError, exc); | |
| 71 Py_XDECREF(exc); | |
| 72 return NULL; | |
| 73 } | |
| 74 | |
| 75 | |
| 76 /* | |
| 77 * A wrapper around OpenProcess setting NSP exception if process | 47 * A wrapper around OpenProcess setting NSP exception if process |
| 78 * no longer exists. | 48 * no longer exists. |
| 79 * "pid" is the process pid, "dwDesiredAccess" is the first argument | 49 * "pid" is the process pid, "dwDesiredAccess" is the first argument |
| 80 * exptected by OpenProcess. | 50 * exptected by OpenProcess. |
| 81 * Return a process handle or NULL. | 51 * Return a process handle or NULL. |
| 82 */ | 52 */ |
| 83 HANDLE | 53 HANDLE |
| 84 handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) | 54 handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) |
| 85 { | 55 { |
| 86 HANDLE hProcess; | 56 HANDLE hProcess; |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 } while(enumReturnSz == procArraySz * sizeof(DWORD)); | 140 } while(enumReturnSz == procArraySz * sizeof(DWORD)); |
| 171 | 141 |
| 172 /* The number of elements is the returned size / size of each element */ | 142 /* The number of elements is the returned size / size of each element */ |
| 173 *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); | 143 *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); |
| 174 | 144 |
| 175 return procArray; | 145 return procArray; |
| 176 } | 146 } |
| 177 | 147 |
| 178 | 148 |
| 179 int | 149 int |
| 180 is_system_proc(DWORD pid) { | |
| 181 HANDLE hProcess; | |
| 182 | |
| 183 // Special case for PID 0 System Idle Process | |
| 184 // and PID 4 (SYSTEM) | |
| 185 if ((pid == 0) || (pid == 4)) { | |
| 186 return 1; | |
| 187 } | |
| 188 if (pid < 0) { | |
| 189 return 0; | |
| 190 } | |
| 191 | |
| 192 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, p
id); | |
| 193 if (NULL == hProcess) { | |
| 194 // invalid parameter is no such process | |
| 195 if (GetLastError() == ERROR_INVALID_PARAMETER) { | |
| 196 return 0; | |
| 197 } | |
| 198 | |
| 199 // access denied obviously means there's a process to deny access to... | |
| 200 if (GetLastError() == ERROR_ACCESS_DENIED) { | |
| 201 return 1; | |
| 202 } | |
| 203 | |
| 204 PyErr_SetFromWindowsErr(0); | |
| 205 return -1; | |
| 206 } | |
| 207 | |
| 208 return HasSystemPrivilege(hProcess); | |
| 209 } | |
| 210 | |
| 211 | |
| 212 int | |
| 213 pid_is_running(DWORD pid) | 150 pid_is_running(DWORD pid) |
| 214 { | 151 { |
| 215 HANDLE hProcess; | 152 HANDLE hProcess; |
| 216 DWORD exitCode; | 153 DWORD exitCode; |
| 217 | 154 |
| 218 // Special case for PID 0 System Idle Process | 155 // Special case for PID 0 System Idle Process |
| 219 if (pid == 0) { | 156 if (pid == 0) { |
| 220 return 1; | 157 return 1; |
| 221 } | 158 } |
| 222 | 159 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 263 | 200 |
| 264 int | 201 int |
| 265 pid_in_proclist(DWORD pid) | 202 pid_in_proclist(DWORD pid) |
| 266 { | 203 { |
| 267 DWORD *proclist = NULL; | 204 DWORD *proclist = NULL; |
| 268 DWORD numberOfReturnedPIDs; | 205 DWORD numberOfReturnedPIDs; |
| 269 DWORD i; | 206 DWORD i; |
| 270 | 207 |
| 271 proclist = get_pids(&numberOfReturnedPIDs); | 208 proclist = get_pids(&numberOfReturnedPIDs); |
| 272 if (NULL == proclist) { | 209 if (NULL == proclist) { |
| 273 return NULL; | 210 return -1; |
| 274 } | 211 } |
| 275 | 212 |
| 276 for (i = 0; i < numberOfReturnedPIDs; i++) { | 213 for (i = 0; i < numberOfReturnedPIDs; i++) { |
| 277 if (pid == proclist[i]) { | 214 if (pid == proclist[i]) { |
| 278 free(proclist); | 215 free(proclist); |
| 279 return 1; | 216 return 1; |
| 280 } | 217 } |
| 281 } | 218 } |
| 282 | 219 |
| 283 free(proclist); | 220 free(proclist); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 PyObject* | 270 PyObject* |
| 334 get_ppid(long pid) | 271 get_ppid(long pid) |
| 335 { | 272 { |
| 336 HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | 273 HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
| 337 PROCESSENTRY32 pe = { 0 }; | 274 PROCESSENTRY32 pe = { 0 }; |
| 338 pe.dwSize = sizeof(PROCESSENTRY32); | 275 pe.dwSize = sizeof(PROCESSENTRY32); |
| 339 | 276 |
| 340 if( Process32First(h, &pe)) { | 277 if( Process32First(h, &pe)) { |
| 341 do { | 278 do { |
| 342 if (pe.th32ProcessID == pid) { | 279 if (pe.th32ProcessID == pid) { |
| 343 //printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID); | 280 ////printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID); |
| 344 CloseHandle(h); | 281 CloseHandle(h); |
| 345 return Py_BuildValue("I", pe.th32ParentProcessID); | 282 return Py_BuildValue("I", pe.th32ParentProcessID); |
| 346 } | 283 } |
| 347 } while(Process32Next(h, &pe)); | 284 } while(Process32Next(h, &pe)); |
| 348 | 285 |
| 349 // the process was never found, set NoSuchProcess exception | 286 // the process was never found, set NoSuchProcess exception |
| 350 NoSuchProcess(); | 287 NoSuchProcess(); |
| 351 CloseHandle(h); | 288 CloseHandle(h); |
| 352 return NULL; | 289 return NULL; |
| 353 } | 290 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 369 LPWSTR *szArglist; | 306 LPWSTR *szArglist; |
| 370 HANDLE hProcess; | 307 HANDLE hProcess; |
| 371 PVOID pebAddress; | 308 PVOID pebAddress; |
| 372 PVOID rtlUserProcParamsAddress; | 309 PVOID rtlUserProcParamsAddress; |
| 373 UNICODE_STRING commandLine; | 310 UNICODE_STRING commandLine; |
| 374 WCHAR *commandLineContents; | 311 WCHAR *commandLineContents; |
| 375 PyObject *arg = NULL; | 312 PyObject *arg = NULL; |
| 376 PyObject *arg_from_wchar = NULL; | 313 PyObject *arg_from_wchar = NULL; |
| 377 PyObject *argList = NULL; | 314 PyObject *argList = NULL; |
| 378 | 315 |
| 379 | |
| 380 hProcess = handle_from_pid(pid); | 316 hProcess = handle_from_pid(pid); |
| 381 if(hProcess == NULL) { | 317 if(hProcess == NULL) { |
| 382 return NULL; | 318 return NULL; |
| 383 } | 319 } |
| 384 | 320 |
| 385 pebAddress = GetPebAddress(hProcess); | 321 pebAddress = GetPebAddress(hProcess); |
| 386 | 322 |
| 387 /* get the address of ProcessParameters */ | 323 /* get the address of ProcessParameters */ |
| 388 #ifdef _WIN64 | 324 #ifdef _WIN64 |
| 389 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32, | 325 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32, |
| 390 &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) | 326 &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) |
| 391 #else | 327 #else |
| 392 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, | 328 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, |
| 393 &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) | 329 &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) |
| 394 #endif | 330 #endif |
| 395 { | 331 { |
| 396 //printf("Could not read the address of ProcessParameters!\n"); | 332 ////printf("Could not read the address of ProcessParameters!\n"); |
| 397 PyErr_SetFromWindowsErr(0); | 333 PyErr_SetFromWindowsErr(0); |
| 398 CloseHandle(hProcess); | 334 CloseHandle(hProcess); |
| 399 return NULL; | 335 return NULL; |
| 400 } | 336 } |
| 401 | 337 |
| 402 /* read the CommandLine UNICODE_STRING structure */ | 338 /* read the CommandLine UNICODE_STRING structure */ |
| 403 #ifdef _WIN64 | 339 #ifdef _WIN64 |
| 404 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112, | 340 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112, |
| 405 &commandLine, sizeof(commandLine), NULL)) | 341 &commandLine, sizeof(commandLine), NULL)) |
| 406 #else | 342 #else |
| 407 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40, | 343 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40, |
| 408 &commandLine, sizeof(commandLine), NULL)) | 344 &commandLine, sizeof(commandLine), NULL)) |
| 409 #endif | 345 #endif |
| 410 { | 346 { |
| 411 //printf("Could not read CommandLine!\n"); | 347 ////printf("Could not read CommandLine!\n"); |
| 348 CloseHandle(hProcess); |
| 412 PyErr_SetFromWindowsErr(0); | 349 PyErr_SetFromWindowsErr(0); |
| 413 CloseHandle(hProcess); | |
| 414 return NULL; | 350 return NULL; |
| 415 } | 351 } |
| 416 | 352 |
| 417 | 353 |
| 418 /* allocate memory to hold the command line */ | 354 /* allocate memory to hold the command line */ |
| 419 commandLineContents = (WCHAR *)malloc(commandLine.Length+1); | 355 commandLineContents = (WCHAR *)malloc(commandLine.Length+1); |
| 420 | 356 |
| 421 /* read the command line */ | 357 /* read the command line */ |
| 422 if (!ReadProcessMemory(hProcess, commandLine.Buffer, | 358 if (!ReadProcessMemory(hProcess, commandLine.Buffer, |
| 423 commandLineContents, commandLine.Length, NULL)) | 359 commandLineContents, commandLine.Length, NULL)) |
| 424 { | 360 { |
| 425 //printf("Could not read the command line string!\n"); | 361 ////printf("Could not read the command line string!\n"); |
| 362 CloseHandle(hProcess); |
| 426 PyErr_SetFromWindowsErr(0); | 363 PyErr_SetFromWindowsErr(0); |
| 427 CloseHandle(hProcess); | |
| 428 free(commandLineContents); | 364 free(commandLineContents); |
| 429 return NULL; | 365 return NULL; |
| 430 } | 366 } |
| 431 | 367 |
| 432 /* print the commandline */ | 368 /* print the commandline */ |
| 433 //printf("%.*S\n", commandLine.Length / 2, commandLineContents); | 369 ////printf("%.*S\n", commandLine.Length / 2, commandLineContents); |
| 434 | 370 |
| 435 // null-terminate the string to prevent wcslen from returning incorrect leng
th | 371 // null-terminate the string to prevent wcslen from returning incorrect leng
th |
| 436 // the length specifier is in characters, but commandLine.Length is in bytes | 372 // the length specifier is in characters, but commandLine.Length is in bytes |
| 437 commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0'; | 373 commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0'; |
| 438 | 374 |
| 439 // attemempt tp parse the command line using Win32 API, fall back on string | 375 // attemempt tp parse the command line using Win32 API, fall back on string |
| 440 // cmdline version otherwise | 376 // cmdline version otherwise |
| 441 szArglist = CommandLineToArgvW(commandLineContents, &nArgs); | 377 szArglist = CommandLineToArgvW(commandLineContents, &nArgs); |
| 442 if (NULL == szArglist) { | 378 if (NULL == szArglist) { |
| 443 // failed to parse arglist | 379 // failed to parse arglist |
| 444 // encode as a UTF8 Python string object from WCHAR string | 380 // encode as a UTF8 Python string object from WCHAR string |
| 445 arg_from_wchar = PyUnicode_FromWideChar(commandLineContents, | 381 arg_from_wchar = PyUnicode_FromWideChar(commandLineContents, |
| 446 commandLine.Length / 2); | 382 commandLine.Length / 2); |
| 447 #if PY_MAJOR_VERSION >= 3 | 383 #if PY_MAJOR_VERSION >= 3 |
| 448 argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar))
; | 384 argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar))
; |
| 449 #else | 385 #else |
| 450 argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar)); | 386 argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar)); |
| 451 #endif | 387 #endif |
| 452 } | 388 } |
| 453 else { | 389 else { |
| 454 // arglist parsed as array of UNICODE_STRING, so convert each to Python | 390 // arglist parsed as array of UNICODE_STRING, so convert each to Python |
| 455 // string object and add to arg list | 391 // string object and add to arg list |
| 456 argList = Py_BuildValue("[]"); | 392 argList = Py_BuildValue("[]"); |
| 457 for(i=0; i<nArgs; i++) { | 393 for(i=0; i<nArgs; i++) { |
| 458 //printf("%d: %.*S (%d characters)\n", i, wcslen(szArglist[i]), | 394 ////printf("%d: %.*S (%d characters)\n", i, wcslen(szArglist[i]), |
| 459 // szArglist[i], wcslen(szArglist[i])); | 395 // szArglist[i], wcslen(szArglist[i])); |
| 460 arg_from_wchar = PyUnicode_FromWideChar(szArglist[i], | 396 arg_from_wchar = PyUnicode_FromWideChar(szArglist[i], |
| 461 wcslen(szArglist[i]) | 397 wcslen(szArglist[i]) |
| 462 ); | 398 ); |
| 463 #if PY_MAJOR_VERSION >= 3 | 399 #if PY_MAJOR_VERSION >= 3 |
| 464 arg = PyUnicode_FromObject(arg_from_wchar); | 400 arg = PyUnicode_FromObject(arg_from_wchar); |
| 465 #else | 401 #else |
| 466 arg = PyUnicode_AsUTF8String(arg_from_wchar); | 402 arg = PyUnicode_AsUTF8String(arg_from_wchar); |
| 467 #endif | 403 #endif |
| 468 Py_XDECREF(arg_from_wchar); | 404 Py_XDECREF(arg_from_wchar); |
| 469 PyList_Append(argList, arg); | 405 PyList_Append(argList, arg); |
| 470 Py_XDECREF(arg); | 406 Py_XDECREF(arg); |
| 471 } | 407 } |
| 472 } | 408 } |
| 473 | 409 |
| 474 LocalFree(szArglist); | 410 LocalFree(szArglist); |
| 475 free(commandLineContents); | 411 free(commandLineContents); |
| 476 CloseHandle(hProcess); | 412 CloseHandle(hProcess); |
| 477 return argList; | 413 return argList; |
| 478 } | 414 } |
| 479 | 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 |
| OLD | NEW |