diff options
author | Ron Yorston <rmy@pobox.com> | 2017-08-27 08:47:20 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-09-27 08:42:36 +0100 |
commit | 9da9454f418db5424357cef80b681eab22fc4379 (patch) | |
tree | 0c80c2abf5405f7c692343dd74293e668de20812 /win32 | |
parent | 292c8b3000d71908897bd0d140cbeca470e39741 (diff) | |
download | busybox-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.c | 181 |
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 | */ |
423 | static int terminate_process_tree(HANDLE main_process, int exit_status) | 421 | typedef int (*kill_callback)(pid_t pid, int exit_code); |
422 | |||
423 | static 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 | */ |
520 | static int exit_process(HANDLE process, int exit_code) | 521 | static 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 | ||
570 | int 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 | */ | ||
575 | static 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 | |||
590 | int 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 | } |