MCPcopy Create free account
hub / github.com/DeusData/codebase-memory-mcp / cbm_posix_spawn_apple

Function cbm_posix_spawn_apple

src/foundation/subprocess.c:1045–1104  ·  view source on GitHub ↗

macOS: spawn instead of fork+exec. * * fork() duplicates the parent's whole address space bookkeeping, and an * ASan-instrumented parent carries an enormous shadow mapping. Past a * footprint threshold the child is killed (jetsam) BEFORE exec replaces the * image, so the call fails with the child already gone (ESRCH on reap) — a * spawn failure that looks like the launched tool crashing. The

Source from the content-addressed store, hash-verified

1043 * because dup2 clears close-on-exec.
1044 * posix_spawnp keeps execvp's PATH semantics for a bare tool name. */
1045static int cbm_posix_spawn_apple(cbm_subprocess_t *process, int input, int output, pid_t *pid_out) {
1046 /* posix_spawn is the PRIMARY path on macOS; fork+exec is only the
1047 * exec-class fallback. Injection therefore has to live here too, or a test
1048 * on macOS exercises nothing. */
1049#ifdef CBM_ENABLE_TEST_SEAMS
1050 if (cbm_spawn_eagain_injected()) {
1051 return CBM_SPAWN_RETRY;
1052 }
1053#endif
1054 posix_spawn_file_actions_t actions;
1055 posix_spawnattr_t attr;
1056 if (posix_spawn_file_actions_init(&actions) != 0) {
1057 return -1;
1058 }
1059 if (posix_spawnattr_init(&attr) != 0) {
1060 (void)posix_spawn_file_actions_destroy(&actions);
1061 return -1;
1062 }
1063 sigset_t empty_mask;
1064 sigset_t all_signals;
1065 sigemptyset(&empty_mask);
1066 sigfillset(&all_signals);
1067 short flags = (short)(POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
1068 POSIX_SPAWN_CLOEXEC_DEFAULT);
1069 bool configured = posix_spawnattr_setflags(&attr, flags) == 0 &&
1070 posix_spawnattr_setpgroup(&attr, 0) == 0 &&
1071 posix_spawnattr_setsigmask(&attr, &empty_mask) == 0 &&
1072 posix_spawnattr_setsigdefault(&attr, &all_signals) == 0 &&
1073 posix_spawn_file_actions_adddup2(&actions, input, STDIN_FILENO) == 0 &&
1074 posix_spawn_file_actions_adddup2(&actions, output, STDOUT_FILENO) == 0 &&
1075 posix_spawn_file_actions_adddup2(&actions, output, STDERR_FILENO) == 0;
1076 pid_t pid = -1;
1077 int rc =
1078 configured ? posix_spawnp(&pid, process->bin, &actions, &attr, process->argv, environ) : -1;
1079 (void)posix_spawn_file_actions_destroy(&actions);
1080 (void)posix_spawnattr_destroy(&attr);
1081 if (configured && rc == 0 && pid > 0) {
1082 *pid_out = pid;
1083 return 0;
1084 }
1085 /* posix_spawn reports an unusable binary itself, where fork+exec instead
1086 * produces a child that exits 127. Callers (and tests) rely on the latter:
1087 * "spawn_failed" means the SPAWN mechanism failed, not that the tool was
1088 * missing. Fall back to fork+exec for exec-class errors so macOS and Linux
1089 * classify a bogus binary identically; the ASan-fork hazard does not apply
1090 * here, since this child exits immediately. */
1091 if (configured && (rc == ENOENT || rc == EACCES || rc == ENOEXEC || rc == EISDIR ||
1092 rc == ELOOP || rc == ENAMETOOLONG || rc == ENOTDIR)) {
1093 return 1;
1094 }
1095 /* EAGAIN/ENOMEM are the kernel saying "not right now", not "never": the
1096 * process table or a per-user limit is momentarily full. Reporting
1097 * spawn_failed for that turns transient load into a user-visible error —
1098 * a git or LSP probe failing on a busy laptop for no reason the user can
1099 * see or act on. Retry briefly. Everything else stays a hard failure. */
1100 if (configured && (rc == EAGAIN || rc == ENOMEM)) {
1101 return CBM_SPAWN_RETRY;
1102 }

Callers 1

Calls 1

Tested by

no test coverage detected