diff options
Diffstat (limited to 'win32')
-rw-r--r-- | win32/process.c | 162 |
1 files changed, 161 insertions, 1 deletions
diff --git a/win32/process.c b/win32/process.c index f88a4898c..7840741c4 100644 --- a/win32/process.c +++ b/win32/process.c | |||
@@ -412,13 +412,173 @@ UNUSED_PARAM | |||
412 | return sp; | 412 | return sp; |
413 | } | 413 | } |
414 | 414 | ||
415 | /* | ||
416 | * Terminates the process corresponding to the process ID and all of its | ||
417 | * directly and indirectly spawned subprocesses. | ||
418 | * | ||
419 | * This way of terminating the processes is not gentle: the processes get | ||
420 | * no chance of cleaning up after themselves (closing file handles, removing | ||
421 | * .lock files, terminating spawned processes (if any), etc). | ||
422 | */ | ||
423 | static int terminate_process_tree(HANDLE main_process, int exit_status) | ||
424 | { | ||
425 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | ||
426 | PROCESSENTRY32 entry; | ||
427 | DWORD pids[16384]; | ||
428 | int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0; | ||
429 | pid_t pid = GetProcessId(main_process); | ||
430 | |||
431 | pids[0] = (DWORD)pid; | ||
432 | len = 1; | ||
433 | |||
434 | /* | ||
435 | * Even if Process32First()/Process32Next() seem to traverse the | ||
436 | * processes in topological order (i.e. parent processes before | ||
437 | * child processes), there is nothing in the Win32 API documentation | ||
438 | * suggesting that this is guaranteed. | ||
439 | * | ||
440 | * Therefore, run through them at least twice and stop when no more | ||
441 | * process IDs were added to the list. | ||
442 | */ | ||
443 | for (;;) { | ||
444 | int orig_len = len; | ||
445 | |||
446 | memset(&entry, 0, sizeof(entry)); | ||
447 | entry.dwSize = sizeof(entry); | ||
448 | |||
449 | if (!Process32First(snapshot, &entry)) | ||
450 | break; | ||
451 | |||
452 | do { | ||
453 | for (i = len - 1; i >= 0; i--) { | ||
454 | if (pids[i] == entry.th32ProcessID) | ||
455 | break; | ||
456 | if (pids[i] == entry.th32ParentProcessID) | ||
457 | pids[len++] = entry.th32ProcessID; | ||
458 | } | ||
459 | } while (len < max_len && Process32Next(snapshot, &entry)); | ||
460 | |||
461 | if (orig_len == len || len >= max_len) | ||
462 | break; | ||
463 | } | ||
464 | |||
465 | for (i = len - 1; i > 0; i--) { | ||
466 | HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pids[i]); | ||
467 | |||
468 | if (process) { | ||
469 | if (!TerminateProcess(process, exit_status)) | ||
470 | ret = -1; | ||
471 | CloseHandle(process); | ||
472 | } | ||
473 | } | ||
474 | if (!TerminateProcess(main_process, exit_status)) | ||
475 | ret = -1; | ||
476 | CloseHandle(main_process); | ||
477 | |||
478 | return ret; | ||
479 | } | ||
480 | |||
481 | /** | ||
482 | * Determine whether a process runs in the same architecture as the current | ||
483 | * one. That test is required before we assume that GetProcAddress() returns | ||
484 | * a valid address *for the target process*. | ||
485 | */ | ||
486 | static inline int process_architecture_matches_current(HANDLE process) | ||
487 | { | ||
488 | static BOOL current_is_wow = -1; | ||
489 | BOOL is_wow; | ||
490 | |||
491 | if (current_is_wow == -1 && | ||
492 | !IsWow64Process (GetCurrentProcess(), ¤t_is_wow)) | ||
493 | current_is_wow = -2; | ||
494 | if (current_is_wow == -2) | ||
495 | return 0; /* could not determine current process' WoW-ness */ | ||
496 | if (!IsWow64Process (process, &is_wow)) | ||
497 | return 0; /* cannot determine */ | ||
498 | return is_wow == current_is_wow; | ||
499 | } | ||
500 | |||
501 | /** | ||
502 | * This function tries to terminate a Win32 process, as gently as possible. | ||
503 | * | ||
504 | * At first, we will attempt to inject a thread that calls ExitProcess(). If | ||
505 | * that fails, we will fall back to terminating the entire process tree. | ||
506 | * | ||
507 | * Note: as kernel32.dll is loaded before any process, the other process and | ||
508 | * this process will have ExitProcess() at the same address. | ||
509 | * | ||
510 | * This function expects the process handle to have the access rights for | ||
511 | * CreateRemoteThread(): PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, | ||
512 | * PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ. | ||
513 | * | ||
514 | * The idea comes from the Dr Dobb's article "A Safer Alternative to | ||
515 | * TerminateProcess()" by Andrew Tucker (July 1, 1999), | ||
516 | * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 | ||
517 | * | ||
518 | * If this method fails, we fall back to running terminate_process_tree(). | ||
519 | */ | ||
520 | static int exit_process(HANDLE process, int exit_code) | ||
521 | { | ||
522 | DWORD code; | ||
523 | |||
524 | if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) { | ||
525 | static int initialized; | ||
526 | static LPTHREAD_START_ROUTINE exit_process_address; | ||
527 | PVOID arg = (PVOID)(intptr_t)exit_code; | ||
528 | DWORD thread_id; | ||
529 | HANDLE thread = NULL; | ||
530 | |||
531 | if (!initialized) { | ||
532 | HINSTANCE kernel32 = GetModuleHandle("kernel32"); | ||
533 | if (!kernel32) { | ||
534 | fprintf(stderr, "BUG: cannot find kernel32"); | ||
535 | return -1; | ||
536 | } | ||
537 | exit_process_address = (LPTHREAD_START_ROUTINE) | ||
538 | GetProcAddress(kernel32, "ExitProcess"); | ||
539 | initialized = 1; | ||
540 | } | ||
541 | if (!exit_process_address || | ||
542 | !process_architecture_matches_current(process)) | ||
543 | return terminate_process_tree(process, exit_code); | ||
544 | |||
545 | thread = CreateRemoteThread(process, NULL, 0, | ||
546 | exit_process_address, | ||
547 | arg, 0, &thread_id); | ||
548 | if (thread) { | ||
549 | DWORD result; | ||
550 | |||
551 | CloseHandle(thread); | ||
552 | /* | ||
553 | * If the process survives for 10 seconds (a completely | ||
554 | * arbitrary value picked from thin air), fall back to | ||
555 | * killing the process tree via TerminateProcess(). | ||
556 | */ | ||
557 | result = WaitForSingleObject(process, 10000); | ||
558 | if (result == WAIT_OBJECT_0) { | ||
559 | CloseHandle(process); | ||
560 | return 0; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | return terminate_process_tree(process, exit_code); | ||
565 | } | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
415 | int kill(pid_t pid, int sig) | 570 | int kill(pid_t pid, int sig) |
416 | { | 571 | { |
417 | HANDLE h; | 572 | HANDLE h; |
418 | 573 | ||
419 | if (pid > 0 && sig == SIGTERM) { | 574 | if (pid > 0 && sig == SIGTERM) { |
575 | if ((h = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD | | ||
576 | PROCESS_QUERY_INFORMATION | | ||
577 | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | | ||
578 | PROCESS_VM_READ, FALSE, pid))) | ||
579 | return exit_process(h, 128 + sig); | ||
420 | if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL && | 580 | if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL && |
421 | TerminateProcess(h, 0)) { | 581 | terminate_process_tree(h, 128 + sig)) { |
422 | CloseHandle(h); | 582 | CloseHandle(h); |
423 | return 0; | 583 | return 0; |
424 | } | 584 | } |