aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-08-27 08:47:20 +0100
committerRon Yorston <rmy@pobox.com>2017-08-27 08:47:20 +0100
commitc8544e127085b79891003af39d32e4b89c54aab4 (patch)
treef97aec2baaf962346591d9924dc908ae87ccf650
parentb122f9ae692eb5986f0e84826435aea8f2cf35e1 (diff)
downloadbusybox-w32-c8544e127085b79891003af39d32e4b89c54aab4.tar.gz
busybox-w32-c8544e127085b79891003af39d32e4b89c54aab4.tar.bz2
busybox-w32-c8544e127085b79891003af39d32e4b89c54aab4.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>
-rw-r--r--win32/process.c181
1 files changed, 94 insertions, 87 deletions
diff --git a/win32/process.c b/win32/process.c
index 8ecd14176..db2cf87d1 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -401,23 +401,25 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags UNUSED_PAR
401 return sp; 401 return sp;
402} 402}
403 403
404/* 404/**
405 * Terminates the process corresponding to the process ID and all of its 405 * If the process ID is positive invoke the callback for that process
406 * directly and indirectly spawned subprocesses. 406 * only. If negative or zero invoke the callback for all descendants
407 * 407 * of the indicated process. Zero indicates the current process; negative
408 * This way of terminating the processes is not gentle: the processes get 408 * indicates the process with process ID -pid.
409 * no chance of cleaning up after themselves (closing file handles, removing
410 * .lock files, terminating spawned processes (if any), etc).
411 */ 409 */
412static int terminate_process_tree(HANDLE main_process, int exit_status) 410typedef int (*kill_callback)(pid_t pid, int exit_code);
411
412static int kill_pids(pid_t pid, int exit_code, kill_callback killer)
413{ 413{
414 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
415 PROCESSENTRY32 entry;
416 DWORD pids[16384]; 414 DWORD pids[16384];
417 int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0; 415 int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0;
418 pid_t pid = GetProcessId(main_process);
419 416
420 pids[0] = (DWORD)pid; 417 if(pid > 0)
418 pids[0] = (DWORD)pid;
419 else if (pid == 0)
420 pids[0] = (DWORD)getpid();
421 else
422 pids[0] = (DWORD)-pid;
421 len = 1; 423 len = 1;
422 424
423 /* 425 /*
@@ -429,40 +431,46 @@ static int terminate_process_tree(HANDLE main_process, int exit_status)
429 * Therefore, run through them at least twice and stop when no more 431 * Therefore, run through them at least twice and stop when no more
430 * process IDs were added to the list. 432 * process IDs were added to the list.
431 */ 433 */
432 for (;;) { 434 if (pid <= 0) {
433 int orig_len = len; 435 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
434 436
435 memset(&entry, 0, sizeof(entry)); 437 if (snapshot == INVALID_HANDLE_VALUE) {
436 entry.dwSize = sizeof(entry); 438 errno = err_win_to_posix(GetLastError());
439 return -1;
440 }
437 441
438 if (!Process32First(snapshot, &entry)) 442 for (;;) {
439 break; 443 PROCESSENTRY32 entry;
444 int orig_len = len;
440 445
441 do { 446 memset(&entry, 0, sizeof(entry));
442 for (i = len - 1; i >= 0; i--) { 447 entry.dwSize = sizeof(entry);
443 if (pids[i] == entry.th32ProcessID)
444 break;
445 if (pids[i] == entry.th32ParentProcessID)
446 pids[len++] = entry.th32ProcessID;
447 }
448 } while (len < max_len && Process32Next(snapshot, &entry));
449 448
450 if (orig_len == len || len >= max_len) 449 if (!Process32First(snapshot, &entry))
451 break; 450 break;
452 } 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 }
453 464
454 for (i = len - 1; i > 0; i--) { 465 CloseHandle(snapshot);
455 HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pids[i]); 466 }
456 467
457 if (process) { 468 for (i = len - 1; i >= 0; i--) {
458 if (!TerminateProcess(process, exit_status)) 469 if (killer(pids[i], exit_code)) {
459 ret = -1; 470 errno = err_win_to_posix(GetLastError());
460 CloseHandle(process); 471 ret = -1;
461 } 472 }
462 } 473 }
463 if (!TerminateProcess(main_process, exit_status))
464 ret = -1;
465 CloseHandle(main_process);
466 474
467 return ret; 475 return ret;
468} 476}
@@ -488,96 +496,95 @@ static inline int process_architecture_matches_current(HANDLE process)
488} 496}
489 497
490/** 498/**
491 * This function tries to terminate a Win32 process, as gently as possible. 499 * This function tries to terminate a Win32 process, as gently as possible,
492 * 500 * by injecting a thread that calls ExitProcess().
493 * At first, we will attempt to inject a thread that calls ExitProcess(). If
494 * that fails, we will fall back to terminating the entire process tree.
495 * 501 *
496 * Note: as kernel32.dll is loaded before any process, the other process and 502 * Note: as kernel32.dll is loaded before any process, the other process and
497 * this process will have ExitProcess() at the same address. 503 * this process will have ExitProcess() at the same address.
498 * 504 *
499 * This function expects the process handle to have the access rights for
500 * CreateRemoteThread(): PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION,
501 * PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ.
502 *
503 * The idea comes from the Dr Dobb's article "A Safer Alternative to 505 * The idea comes from the Dr Dobb's article "A Safer Alternative to
504 * TerminateProcess()" by Andrew Tucker (July 1, 1999), 506 * TerminateProcess()" by Andrew Tucker (July 1, 1999),
505 * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 507 * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
506 * 508 *
507 * If this method fails, we fall back to running terminate_process_tree().
508 */ 509 */
509static int exit_process(HANDLE process, int exit_code) 510static int exit_process(pid_t pid, int exit_code)
510{ 511{
512 HANDLE process;
511 DWORD code; 513 DWORD code;
514 int ret = 0;
515
516 if (!(process = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD |
517 PROCESS_QUERY_INFORMATION |
518 PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
519 PROCESS_VM_READ, FALSE, pid))) {
520 return -1;
521 }
512 522
513 if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) { 523 if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) {
514 static int initialized; 524 static int initialized;
515 static LPTHREAD_START_ROUTINE exit_process_address; 525 static LPTHREAD_START_ROUTINE exit_process_address;
516 PVOID arg = (PVOID)(intptr_t)exit_code; 526 PVOID arg = (PVOID)(intptr_t)exit_code;
517 DWORD thread_id; 527 DWORD thread_id;
518 HANDLE thread = NULL; 528 HANDLE thread;
519 529
520 if (!initialized) { 530 if (!initialized) {
521 HINSTANCE kernel32 = GetModuleHandle("kernel32"); 531 HINSTANCE kernel32 = GetModuleHandle("kernel32");
522 if (!kernel32) { 532 if (!kernel32) {
523 fprintf(stderr, "BUG: cannot find kernel32"); 533 fprintf(stderr, "BUG: cannot find kernel32");
524 return -1; 534 ret = -1;
535 goto finish;
525 } 536 }
526 exit_process_address = (LPTHREAD_START_ROUTINE) 537 exit_process_address = (LPTHREAD_START_ROUTINE)
527 GetProcAddress(kernel32, "ExitProcess"); 538 GetProcAddress(kernel32, "ExitProcess");
528 initialized = 1; 539 initialized = 1;
529 } 540 }
530 if (!exit_process_address || 541 if (!exit_process_address ||
531 !process_architecture_matches_current(process)) 542 !process_architecture_matches_current(process)) {
532 return terminate_process_tree(process, exit_code); 543 ret = -1;
544 goto finish;
545 }
533 546
534 thread = CreateRemoteThread(process, NULL, 0, 547 if ((thread = CreateRemoteThread(process, NULL, 0,
535 exit_process_address, 548 exit_process_address,
536 arg, 0, &thread_id); 549 arg, 0, &thread_id))) {
537 if (thread) {
538 DWORD result;
539
540 CloseHandle(thread); 550 CloseHandle(thread);
541 /*
542 * If the process survives for 10 seconds (a completely
543 * arbitrary value picked from thin air), fall back to
544 * killing the process tree via TerminateProcess().
545 */
546 result = WaitForSingleObject(process, 10000);
547 if (result == WAIT_OBJECT_0) {
548 CloseHandle(process);
549 return 0;
550 }
551 } 551 }
552
553 return terminate_process_tree(process, exit_code);
554 } 552 }
555 553
556 return 0; 554 finish:
555 CloseHandle(process);
556 return ret;
557} 557}
558 558
559int kill(pid_t pid, int sig) 559/*
560 * This way of terminating processes is not gentle: they get no chance to
561 * clean up after themselves (closing file handles, removing .lock files,
562 * terminating spawned processes (if any), etc).
563 */
564static int terminate_process(pid_t pid, int exit_code)
560{ 565{
561 HANDLE h; 566 HANDLE process;
562 567 int ret;
563 if (pid > 0 && sig == SIGTERM) {
564 if ((h = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD |
565 PROCESS_QUERY_INFORMATION |
566 PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
567 PROCESS_VM_READ, FALSE, pid)))
568 return exit_process(h, 128 + sig);
569 if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL &&
570 terminate_process_tree(h, 128 + sig)) {
571 CloseHandle(h);
572 return 0;
573 }
574 568
575 errno = err_win_to_posix(GetLastError()); 569 if (!(process=OpenProcess(PROCESS_TERMINATE, FALSE, pid))) {
576 if (h != NULL)
577 CloseHandle(h);
578 return -1; 570 return -1;
579 } 571 }
580 572
573 ret = !TerminateProcess(process, exit_code);
574 CloseHandle(process);
575
576 return ret;
577}
578
579int kill(pid_t pid, int sig)
580{
581 if (sig == SIGTERM) {
582 return kill_pids(pid, 128+sig, exit_process);
583 }
584 else if (sig == SIGKILL) {
585 return kill_pids(pid, 128+sig, terminate_process);
586 }
587
581 errno = EINVAL; 588 errno = EINVAL;
582 return -1; 589 return -1;
583} 590}