| 637 | } |
| 638 | |
| 639 | static int cbm_subprocess_spawn_win(cbm_subprocess_t *process) { |
| 640 | char cmdline[8192]; |
| 641 | bool built = |
| 642 | process->windows_cmd_payload |
| 643 | ? cbm_build_win_cmd_payload(cmdline, sizeof(cmdline), process->bin, |
| 644 | process->windows_cmd_payload) |
| 645 | : cbm_build_win_cmdline(cmdline, sizeof(cmdline), (const char *const *)process->argv); |
| 646 | if (!built) { |
| 647 | return -1; |
| 648 | } |
| 649 | wchar_t *wbin = cbm_utf8_to_wide(process->bin); |
| 650 | wchar_t *wcmdline = cbm_utf8_to_wide(cmdline); |
| 651 | if (!wbin || !wcmdline) { |
| 652 | free(wbin); |
| 653 | free(wcmdline); |
| 654 | return -1; |
| 655 | } |
| 656 | |
| 657 | JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits; |
| 658 | ZeroMemory(&limits, sizeof(limits)); |
| 659 | limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; |
| 660 | HANDLE job = CreateJobObjectW(NULL, NULL); |
| 661 | if (!job || |
| 662 | !SetInformationJobObject(job, JobObjectExtendedLimitInformation, &limits, sizeof(limits))) { |
| 663 | if (job) { |
| 664 | CloseHandle(job); |
| 665 | } |
| 666 | free(wbin); |
| 667 | free(wcmdline); |
| 668 | return -1; |
| 669 | } |
| 670 | |
| 671 | SECURITY_ATTRIBUTES security; |
| 672 | ZeroMemory(&security, sizeof(security)); |
| 673 | security.nLength = sizeof(security); |
| 674 | security.bInheritHandle = TRUE; |
| 675 | HANDLE nul = CreateFileW(L"NUL", GENERIC_READ | GENERIC_WRITE, |
| 676 | FILE_SHARE_READ | FILE_SHARE_WRITE, &security, OPEN_EXISTING, 0, NULL); |
| 677 | HANDLE log = INVALID_HANDLE_VALUE; |
| 678 | if (nul == INVALID_HANDLE_VALUE) { |
| 679 | CloseHandle(job); |
| 680 | free(wbin); |
| 681 | free(wcmdline); |
| 682 | return -1; |
| 683 | } |
| 684 | if (process->log_file) { |
| 685 | wchar_t *wlog = cbm_path_to_wide(process->log_file); |
| 686 | if (wlog) { |
| 687 | /* FILE_SHARE_WRITE keeps POSIX parity: on unix nothing stops a |
| 688 | * second producer from appending to the redirected log while the |
| 689 | * child holds it (supervision wrappers rely on that), and the |
| 690 | * log's owner-only directory is what gates who can. Without it |
| 691 | * the child's handle mandatory-locks every other writer out. */ |
| 692 | log = CreateFileW(wlog, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &security, |
| 693 | CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
| 694 | free(wlog); |
| 695 | } |
| 696 | if (log == INVALID_HANDLE_VALUE) { |
no test coverage detected