/* Starts a subprocess with the arguments in the null-terminated argv[] array. * argv[0] is used as the name of the process. Searches the PATH environment * variable to find the program to execute. * * All file descriptors are closed before executing the subprocess, except for * fds 0, 1, and 2 and the 'n_keep_fds' fds listed in 'keep_fds'. Also, any of * the 'n_null_fds' fds listed in 'null_fds' are replaced by /dev/null. * * Returns 0 if successful, otherwise a positive errno value indicating the * error. If successful, '*pp' is assigned a new struct process that may be * used to query the process's status. On failure, '*pp' is set to NULL. */ int process_start(char **argv, const int keep_fds[], size_t n_keep_fds, const int null_fds[], size_t n_null_fds, struct process **pp) { sigset_t oldsigs; pid_t pid; int error; *pp = NULL; COVERAGE_INC(process_start); error = process_prestart(argv); if (error) { return error; } block_sigchld(&oldsigs); pid = fork(); if (pid < 0) { unblock_sigchld(&oldsigs); VLOG_WARN("fork failed: %s", strerror(errno)); return errno; } else if (pid) { /* Running in parent process. */ *pp = process_register(argv[0], pid); unblock_sigchld(&oldsigs); return 0; } else { /* Running in child process. */ int fd_max = get_max_fds(); int fd; fatal_signal_fork(); unblock_sigchld(&oldsigs); for (fd = 0; fd < fd_max; fd++) { if (is_member(fd, null_fds, n_null_fds)) { /* We can't use get_null_fd() here because we might have * already closed its fd. */ int nullfd = open("/dev/null", O_RDWR); dup2(nullfd, fd); close(nullfd); } else if (fd >= 3 && !is_member(fd, keep_fds, n_keep_fds)) { close(fd); } } execvp(argv[0], argv); fprintf(stderr, "execvp(\"%s\") failed: %s\n", argv[0], strerror(errno)); _exit(1); } }
/* Calls fork() and on success returns its return value. On failure, logs an * error and exits unsuccessfully. * * Post-fork, but before returning, this function calls a few other functions * that are generally useful if the child isn't planning to exec a new * process. */ static pid_t fork_and_clean_up(void) { pid_t pid = xfork(); if (pid > 0) { /* Running in parent process. */ fatal_signal_fork(); } else if (!pid) { /* Running in child process. */ lockfile_postfork(); } return pid; }
static pid_t fork_and_wait_for_startup(int *fdp) { int fds[2]; pid_t pid; if (pipe(fds) < 0) { ovs_fatal(errno, "pipe failed"); } pid = fork(); if (pid > 0) { /* Running in parent process. */ char c; close(fds[1]); fatal_signal_fork(); if (read(fds[0], &c, 1) != 1) { int retval; int status; do { retval = waitpid(pid, &status, 0); } while (retval == -1 && errno == EINTR); if (retval == pid && WIFEXITED(status) && WEXITSTATUS(status)) { /* Child exited with an error. Convey the same error to * our parent process as a courtesy. */ exit(WEXITSTATUS(status)); } ovs_fatal(errno, "fork child failed to signal startup"); } close(fds[0]); *fdp = -1; } else if (!pid) { /* Running in child process. */ close(fds[0]); time_postfork(); lockfile_postfork(); *fdp = fds[1]; } else { ovs_fatal(errno, "could not fork"); } return pid; }
static pid_t fork_and_wait_for_startup(int *fdp) { int fds[2]; pid_t pid; xpipe(fds); pid = fork(); if (pid > 0) { /* Running in parent process. */ size_t bytes_read; char c; close(fds[1]); fatal_signal_fork(); if (read_fully(fds[0], &c, 1, &bytes_read) != 0) { int retval; int status; do { retval = waitpid(pid, &status, 0); } while (retval == -1 && errno == EINTR); if (retval == pid && WIFEXITED(status) && WEXITSTATUS(status)) { /* Child exited with an error. Convey the same error to * our parent process as a courtesy. */ exit(WEXITSTATUS(status)); } VLOG_FATAL("fork child failed to signal startup (%s)", strerror(errno)); } close(fds[0]); *fdp = -1; } else if (!pid) { /* Running in child process. */ close(fds[0]); time_postfork(); lockfile_postfork(); *fdp = fds[1]; } else { VLOG_FATAL("fork failed (%s)", strerror(errno)); } return pid; }
/* Calls fork() and on success returns its return value. On failure, logs an * error and exits unsuccessfully. * * Post-fork, but before returning, this function calls a few other functions * that are generally useful if the child isn't planning to exec a new * process. */ pid_t fork_and_clean_up(void) { pid_t pid; pid = fork(); if (pid > 0) { /* Running in parent process. */ fatal_signal_fork(); } else if (!pid) { /* Running in child process. */ time_postfork(); lockfile_postfork(); } else { VLOG_FATAL("fork failed (%s)", strerror(errno)); } return pid; }
/* If configured with set_pidfile() or set_detach(), creates the pid file and * detaches from the foreground session. */ void daemonize(void) { if (detach) { char c = 0; int fds[2]; if (pipe(fds) < 0) { ofp_fatal(errno, "pipe failed"); } switch (fork()) { default: /* Parent process: wait for child to create pidfile, then exit. */ close(fds[1]); fatal_signal_fork(); if (read(fds[0], &c, 1) != 1) { ofp_fatal(errno, "daemon child failed to signal startup"); } exit(0); case 0: /* Child process. */ close(fds[0]); make_pidfile(); write(fds[1], &c, 1); close(fds[1]); setsid(); chdir("/"); break; case -1: /* Error. */ ofp_fatal(errno, "could not fork"); break; } } else { make_pidfile(); } }
/* Starts the process whose arguments are given in the null-terminated array * 'argv' and waits for it to exit. On success returns 0 and stores the * process exit value (suitable for passing to process_status_msg()) in * '*status'. On failure, returns a positive errno value and stores 0 in * '*status'. * * If 'stdout_log' is nonnull, then the subprocess's output to stdout (up to a * limit of PROCESS_MAX_CAPTURE bytes) is captured in a memory buffer, which * when this function returns 0 is stored as a null-terminated string in * '*stdout_log'. The caller is responsible for freeing '*stdout_log' (by * passing it to free()). When this function returns an error, '*stdout_log' * is set to NULL. * * If 'stderr_log' is nonnull, then it is treated like 'stdout_log' except * that it captures the subprocess's output to stderr. */ int process_run_capture(char **argv, char **stdout_log, char **stderr_log, int *status) { struct stream s_stdout, s_stderr; sigset_t oldsigs; pid_t pid; int error; COVERAGE_INC(process_run_capture); if (stdout_log) { *stdout_log = NULL; } if (stderr_log) { *stderr_log = NULL; } *status = 0; error = process_prestart(argv); if (error) { return error; } error = stream_open(&s_stdout); if (error) { return error; } error = stream_open(&s_stderr); if (error) { stream_close(&s_stdout); return error; } block_sigchld(&oldsigs); pid = fork(); if (pid < 0) { int error = errno; unblock_sigchld(&oldsigs); VLOG_WARN("fork failed: %s", strerror(error)); stream_close(&s_stdout); stream_close(&s_stderr); *status = 0; return error; } else if (pid) { /* Running in parent process. */ struct process *p; p = process_register(argv[0], pid); unblock_sigchld(&oldsigs); close(s_stdout.fds[1]); close(s_stderr.fds[1]); while (!process_exited(p)) { stream_read(&s_stdout); stream_read(&s_stderr); stream_wait(&s_stdout); stream_wait(&s_stderr); process_wait(p); poll_block(); } stream_read(&s_stdout); stream_read(&s_stderr); if (stdout_log) { *stdout_log = ds_steal_cstr(&s_stdout.log); } if (stderr_log) { *stderr_log = ds_steal_cstr(&s_stderr.log); } stream_close(&s_stdout); stream_close(&s_stderr); *status = process_status(p); process_destroy(p); return 0; } else { /* Running in child process. */ int max_fds; int i; fatal_signal_fork(); unblock_sigchld(&oldsigs); dup2(get_null_fd(), 0); dup2(s_stdout.fds[1], 1); dup2(s_stderr.fds[1], 2); max_fds = get_max_fds(); for (i = 3; i < max_fds; i++) { close(i); } execvp(argv[0], argv); fprintf(stderr, "execvp(\"%s\") failed: %s\n", argv[0], strerror(errno)); exit(EXIT_FAILURE); } }