fork+exec child setup. On Apple this runs ONLY for the exec-failure * fallback (see cbm_posix_spawn_apple), which preserves the documented * "bogus binary => child exits 127" contract across platforms. */
| 1024 | * fallback (see cbm_posix_spawn_apple), which preserves the documented |
| 1025 | * "bogus binary => child exits 127" contract across platforms. */ |
| 1026 | static void cbm_posix_child_exec(cbm_subprocess_t *process, int input, int output, long max_fd) { |
| 1027 | if (setpgid(0, 0) < 0) { |
| 1028 | _exit(127); |
| 1029 | } |
| 1030 | cbm_posix_reset_child_signals(); |
| 1031 | |
| 1032 | /* Never let a worker consume the MCP transport inherited as stdin. Only |
| 1033 | * async-signal-safe calls are used between fork and exec. */ |
| 1034 | if (input < 0 || output < 0 || dup2(input, STDIN_FILENO) < 0 || |
| 1035 | dup2(output, STDOUT_FILENO) < 0 || dup2(output, STDERR_FILENO) < 0) { |
| 1036 | _exit(127); |
| 1037 | } |
| 1038 | if (input > STDERR_FILENO) { |
| 1039 | (void)close(input); |
| 1040 | } |
| 1041 | if (output > STDERR_FILENO) { |
| 1042 | (void)close(output); |
| 1043 | } |
| 1044 | for (int fd = STDERR_FILENO + 1; fd < max_fd; fd++) { |
| 1045 | (void)close(fd); |
| 1046 | } |
| 1047 | /* A fixed literal tool name (for example "git" or "curl") uses the |
| 1048 | * caller's normal PATH without introducing a shell. An explicit path |
| 1049 | * still has execvp's exact-path semantics because it contains '/'. */ |
| 1050 | execvp(process->bin, process->argv); |
| 1051 | _exit(127); |
| 1052 | } |
| 1053 | |
| 1054 | static int cbm_posix_fd_at_least_three(int fd) { |
| 1055 | if (fd < 0 || fd > STDERR_FILENO) { |
no test coverage detected