aboutsummaryrefslogtreecommitdiff
path: root/win32
diff options
context:
space:
mode:
Diffstat (limited to 'win32')
-rw-r--r--win32/process.c162
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 */
423static 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 */
486static 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(), &current_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 */
520static 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
415int kill(pid_t pid, int sig) 570int 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 }