#include #include #include #include #include #include #include #include static void warn(const char *msg) { fprintf(stderr, "%s\n", msg); } static void warnsys(const char *msg) { fprintf(stderr, "%s: %s\n", msg, strerror(errno)); } static void diesys(const char *msg) { warnsys(msg); exit(1); } static void die(const char *msg) { warn(msg); exit(1); } static void set_close_on_exec(int fd) { int flags; if ((flags = fcntl(fd, F_GETFD)) == -1) { diesys("cannot get file descriptor flags"); } flags |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, flags) == -1) { diesys("cannot set close-on-exec"); } } static void spawn(char **argv) { pid_t child; int status, exec_errno; int fds[2]; struct pollfd pollfd; ssize_t nbyte; if (pipe(fds) == -1) { diesys("cannot create pipe"); } set_close_on_exec(fds[0]); set_close_on_exec(fds[1]); if ((child = fork()) == -1) { diesys("cannot fork"); } if (!child) { // We are in the new child process. close(fds[0]); execvp(argv[0], argv); exec_errno = errno; nbyte = write(fds[1], &exec_errno, sizeof(exec_errno)); if (nbyte == (ssize_t)-1) { warnsys("completely borked (child)"); } else if (nbyte != (ssize_t)sizeof(exec_errno)) { warn("completely borked (child)"); } _exit(126); } // We are in the old parent process. close(fds[1]); memset(&pollfd, 0, sizeof(pollfd)); pollfd.fd = fds[0]; pollfd.events = POLLIN; if (poll(&pollfd, 1, -1) == -1) { diesys("cannot poll"); } exec_errno = 0; if (pollfd.revents & POLLIN) { nbyte = read(pollfd.fd, &exec_errno, sizeof(exec_errno)); if (nbyte == 0) { // We don't get any data, means the pipe was closed. } else if (nbyte == (ssize_t)-1) { warnsys("completely borked (parent)"); } else if (nbyte != (ssize_t)sizeof(exec_errno)) { warn("completely borked (parent)"); } } if (waitpid(child, &status, 0) == -1) { diesys("cannot wait for child process"); } if (!WIFEXITED(status)) { die("child process did not exit normally"); } if (exec_errno != 0) { errno = exec_errno; warnsys("cannot execute command"); } else if (WEXITSTATUS(status) == 0) { // Success, all good. } else { fprintf(stderr, "Child exited with code %d\n", (int)WEXITSTATUS(status)); } } int main(int argc, char **argv) { if (argc < 2) { die("usage: spawn command [args...]"); } spawn(&argv[1]); return 0; }