pid_t spawn (const char *path, char *const *argv, int in, int out, int err, cbv::ptr postforkcb, char *const *env) { int fds[2]; if (pipe (fds) < 0) return -1; close_on_exec (fds[0]); close_on_exec (fds[1]); pid_t pid = afork (); if (pid < 0) return pid; if (!pid) { amain_panic = true; close (fds[0]); setstdfds (in, out, err); if (postforkcb) (*postforkcb) (); if (env) execve (path, argv, env); else execv (path, argv); int saved_err = errno; v_write (fds[1], &saved_err, sizeof (saved_err)); close (fds[1]); // Since we return a useful errno, there is no need to print // anything in the child process. Just exit. _exit (1); } close (fds[1]); int chld_err; int n = read (fds[0], &chld_err, sizeof (chld_err)); close (fds[0]); if (n == 0) return pid; errno = chld_err; return -1; }
static int setup(int argc, char** argv) { setargs(argc, argv); if(setstdfds()) retwarn(-1, "cannot set initial fds 0, 1, 2"); if(setinitctl()) /* Not having telinit is bad, but aborting system startup for this mere reason is likely even worse. */ warn("can't initialize initctl, init will be uncontrollable"); if(setsignals()) retwarn(-1, "failed to set signal handlers"); if(!configure(NONSTRICT)) setnewconf(); else if(!cfg) retwarn(-1, "initial configuration error"); return 0; }
pid_t aspawn (const char *path, char *const *argv, int in, int out, int err, cbv::ptr postforkcb, char *const *env) { pid_t pid = afork (); if (pid < 0) return pid; if (pid) return pid; setstdfds (in, out, err); if (postforkcb) (*postforkcb) (); if (env) execve (path, argv, env); else execv (path, argv); warn ("%s: %m\n", path); _exit (1); }