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