aboutsummaryrefslogtreecommitdiff
path: root/win32
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-08-27 08:47:20 +0100
committerRon Yorston <rmy@pobox.com>2017-09-27 08:42:36 +0100
commit9da9454f418db5424357cef80b681eab22fc4379 (patch)
tree0c80c2abf5405f7c692343dd74293e668de20812 /win32
parent292c8b3000d71908897bd0d140cbeca470e39741 (diff)
downloadbusybox-w32-9da9454f418db5424357cef80b681eab22fc4379.tar.gz
busybox-w32-9da9454f418db5424357cef80b681eab22fc4379.tar.bz2
busybox-w32-9da9454f418db5424357cef80b681eab22fc4379.zip
win32: improvements to implementation of kill(2)
Extend the implementation of kill(2) so that: - Sending the TERM signal asks the target process to exit. As on Unix it may not comply. - Sending the KILL signal forcibly terminates the target process. - Using a negative pid treats the target process as a process group leader and signals it and all of its descendants. - Using a pid of zero treats the current process as a process group leader and signals it and all of its descendants. Signed-off-by: Ron Yorston <rmy@pobox.com>
Diffstat (limited to 'win32')
-rw-r--r--win32/process.c181
1 files changed, 94 insertions, 87 deletions
diff --git a/win32/process.c b/win32/process.c
index 7840741c4..ada0d743f 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -412,23 +412,25 @@ UNUSED_PARAM
412 return sp; 412 return sp;
413} 413}
414 414
415/* 415/**
416 * Terminates the process corresponding to the process ID and all of its 416 * If the process ID is positive invoke the callback for that process
417 * directly and indirectly spawned subprocesses. 417 * only. If negative or zero invoke the callback for all descendants
418 * 418 * of the indicated process. Zero indicates the current process; negative
419 * This way of terminating the processes is not gentle: the processes get 419 * indicates the process with process ID -pid.
420 * no chance of cleaning up after themselves (closing file handles, removing
421 * .lock files, terminating spawned processes (if any), etc).
422 */ 420 */
423static int terminate_process_tree(HANDLE main_process, int exit_status) 421typedef int (*kill_callback)(pid_t pid, int exit_code);
422
423static int kill_pids(pid_t pid, int exit_code, kill_callback killer)
424{ 424{
425 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
426 PROCESSENTRY32 entry;
427 DWORD pids[16384]; 425 DWORD pids[16384];
428 int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0; 426 int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0;
429 pid_t pid = GetProcessId(main_process);
430 427
431 pids[0] = (DWORD)pid; 428 if(pid > 0)
429 pids[0] = (DWORD)pid;
430 else if (pid == 0)
431 pids[0] = (DWORD)getpid();
432 else
433 pids[0] = (DWORD)-pid;
432 len = 1; 434 len = 1;
433 435
434 /* 436 /*
@@ -440,40 +442,46 @@ static int terminate_process_tree(HANDLE main_process, int exit_status)
440 * Therefore, run through them at least twice and stop when no more 442 * Therefore, run through them at least twice and stop when no more
441 * process IDs were added to the list. 443 * process IDs were added to the list.
442 */ 444 */
443 for (;;) { 445 if (pid <= 0) {
444 int orig_len = len; 446 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
445 447
446 memset(&entry, 0, sizeof(entry)); 448 if (snapshot == INVALID_HANDLE_VALUE) {
447 entry.dwSize = sizeof(entry); 449 errno = err_win_to_posix(GetLastError());
450 return -1;
451 }
448 452
449 if (!Process32First(snapshot, &entry)) 453 for (;;) {
450 break; 454 PROCESSENTRY32 entry;
455 int orig_len = len;
451 456
452 do { 457 memset(&entry, 0, sizeof(entry));
453 for (i = len - 1; i >= 0; i--) { 458 entry.dwSize = sizeof(entry);
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 459
461 if (orig_len == len || len >= max_len) 460 if (!Process32First(snapshot, &entry))
462 break; 461 break;
463 }
464 462
465 for (i = len - 1; i > 0; i--) { 463 do {
466 HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pids[i]); 464 for (i = len - 1; i >= 0; i--) {
465 if (pids[i] == entry.th32ProcessID)
466 break;
467 if (pids[i] == entry.th32ParentProcessID)
468 pids[len++] = entry.th32ProcessID;
469 }
470 } while (len < max_len && Process32Next(snapshot, &entry));
467 471
468 if (process) { 472 if (orig_len == len || len >= max_len)
469 if (!TerminateProcess(process, exit_status)) 473 break;
470 ret = -1; 474 }
471 CloseHandle(process); 475
476 CloseHandle(snapshot);
477 }
478
479 for (i = len - 1; i >= 0; i--) {
480 if (killer(pids[i], exit_code)) {
481 errno = err_win_to_posix(GetLastError());
482 ret = -1;
472 } 483 }
473 } 484 }
474 if (!TerminateProcess(main_process, exit_status))
475 ret = -1;
476 CloseHandle(main_process);
477 485
478 return ret; 486 return ret;
479} 487}
@@ -499,96 +507,95 @@ static inline int process_architecture_matches_current(HANDLE process)
499} 507}
500 508
501/** 509/**
502 * This function tries to terminate a Win32 process, as gently as possible. 510 * This function tries to terminate a Win32 process, as gently as possible,
503 * 511 * by injecting a thread that calls ExitProcess().
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 * 512 *
507 * Note: as kernel32.dll is loaded before any process, the other process and 513 * Note: as kernel32.dll is loaded before any process, the other process and
508 * this process will have ExitProcess() at the same address. 514 * this process will have ExitProcess() at the same address.
509 * 515 *
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 516 * The idea comes from the Dr Dobb's article "A Safer Alternative to
515 * TerminateProcess()" by Andrew Tucker (July 1, 1999), 517 * TerminateProcess()" by Andrew Tucker (July 1, 1999),
516 * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 518 * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
517 * 519 *
518 * If this method fails, we fall back to running terminate_process_tree().
519 */ 520 */
520static int exit_process(HANDLE process, int exit_code) 521static int exit_process(pid_t pid, int exit_code)
521{ 522{
523 HANDLE process;
522 DWORD code; 524 DWORD code;
525 int ret = 0;
526
527 if (!(process = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD |
528 PROCESS_QUERY_INFORMATION |
529 PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
530 PROCESS_VM_READ, FALSE, pid))) {
531 return -1;
532 }
523 533
524 if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) { 534 if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) {
525 static int initialized; 535 static int initialized;
526 static LPTHREAD_START_ROUTINE exit_process_address; 536 static LPTHREAD_START_ROUTINE exit_process_address;
527 PVOID arg = (PVOID)(intptr_t)exit_code; 537 PVOID arg = (PVOID)(intptr_t)exit_code;
528 DWORD thread_id; 538 DWORD thread_id;
529 HANDLE thread = NULL; 539 HANDLE thread;
530 540
531 if (!initialized) { 541 if (!initialized) {
532 HINSTANCE kernel32 = GetModuleHandle("kernel32"); 542 HINSTANCE kernel32 = GetModuleHandle("kernel32");
533 if (!kernel32) { 543 if (!kernel32) {
534 fprintf(stderr, "BUG: cannot find kernel32"); 544 fprintf(stderr, "BUG: cannot find kernel32");
535 return -1; 545 ret = -1;
546 goto finish;
536 } 547 }
537 exit_process_address = (LPTHREAD_START_ROUTINE) 548 exit_process_address = (LPTHREAD_START_ROUTINE)
538 GetProcAddress(kernel32, "ExitProcess"); 549 GetProcAddress(kernel32, "ExitProcess");
539 initialized = 1; 550 initialized = 1;
540 } 551 }
541 if (!exit_process_address || 552 if (!exit_process_address ||
542 !process_architecture_matches_current(process)) 553 !process_architecture_matches_current(process)) {
543 return terminate_process_tree(process, exit_code); 554 ret = -1;
555 goto finish;
556 }
544 557
545 thread = CreateRemoteThread(process, NULL, 0, 558 if ((thread = CreateRemoteThread(process, NULL, 0,
546 exit_process_address, 559 exit_process_address,
547 arg, 0, &thread_id); 560 arg, 0, &thread_id))) {
548 if (thread) {
549 DWORD result;
550
551 CloseHandle(thread); 561 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 } 562 }
563
564 return terminate_process_tree(process, exit_code);
565 } 563 }
566 564
567 return 0; 565 finish:
566 CloseHandle(process);
567 return ret;
568} 568}
569 569
570int kill(pid_t pid, int sig) 570/*
571 * This way of terminating processes is not gentle: they get no chance to
572 * clean up after themselves (closing file handles, removing .lock files,
573 * terminating spawned processes (if any), etc).
574 */
575static int terminate_process(pid_t pid, int exit_code)
571{ 576{
572 HANDLE h; 577 HANDLE process;
573 578 int ret;
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);
580 if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL &&
581 terminate_process_tree(h, 128 + sig)) {
582 CloseHandle(h);
583 return 0;
584 }
585 579
586 errno = err_win_to_posix(GetLastError()); 580 if (!(process=OpenProcess(PROCESS_TERMINATE, FALSE, pid))) {
587 if (h != NULL)
588 CloseHandle(h);
589 return -1; 581 return -1;
590 } 582 }
591 583
584 ret = !TerminateProcess(process, exit_code);
585 CloseHandle(process);
586
587 return ret;
588}
589
590int kill(pid_t pid, int sig)
591{
592 if (sig == SIGTERM) {
593 return kill_pids(pid, 128+sig, exit_process);
594 }
595 else if (sig == SIGKILL) {
596 return kill_pids(pid, 128+sig, terminate_process);
597 }
598
592 errno = EINVAL; 599 errno = EINVAL;
593 return -1; 600 return -1;
594} 601}