/* Close stdin, stdout, stderr. If we're started from e.g. an SSH session, * then this keeps us from holding that session open artificially. */ static void close_standard_fds(void) { int null_fd = get_null_fd(); if (null_fd >= 0) { dup2(null_fd, STDIN_FILENO); dup2(null_fd, STDOUT_FILENO); dup2(null_fd, STDERR_FILENO); } }
/* Close stdin, stdout, stderr. If we're started from e.g. an SSH session, * then this keeps us from holding that session open artificially. */ static void close_standard_fds(void) { int null_fd = get_null_fd(); if (null_fd >= 0) { dup2(null_fd, STDIN_FILENO); dup2(null_fd, STDOUT_FILENO); dup2(null_fd, STDERR_FILENO); } /* Disable logging to stderr to avoid wasting CPU time. */ vlog_set_levels(NULL, VLF_CONSOLE, VLL_OFF); }
/* Close standard file descriptors (except any that the client has requested we * leave open by calling daemon_save_fd()). If we're started from e.g. an SSH * session, then this keeps us from holding that session open artificially. */ void close_standard_fds(void) { int null_fd = get_null_fd(); if (null_fd >= 0) { int fd; for (fd = 0; fd < 3; fd++) { if (!save_fds[fd]) { dup2(null_fd, fd); } } } /* Disable logging to stderr to avoid wasting CPU time. */ //vlog_set_levels(NULL, VLF_CONSOLE, VLL_OFF); }
int spawn(const char* file, char* const* argv, char* const* envv, const char* cwd, int outfd) { int err_fds[2] = { -1, -1 }; pid_t child_pid = 0; int spawn_error, r; size_t bytes_read; if (pipe(err_fds) < 0) goto error; child_pid = fork(); if (child_pid < 0) goto error; if (child_pid == 0) { /* We are the child. */ int e; close(err_fds[0]); if (ioctl(err_fds[1], FIOCLEX, NULL) < 0) goto error; if (dup2(get_null_fd(), 0) < 0) goto child_error; if (dup2(outfd, 1) < 0) goto child_error; if (dup2(outfd, 2) < 0) goto child_error; if (chdir(cwd) < 0) goto child_error; if (setpgrp() < 0) goto child_error; environ = (char**) envv; execvp(file, argv); child_error: e = errno; r = write(err_fds[1], &e, sizeof e); USE(r); exit(127); } close(err_fds[1]); err_fds[1] = -1; spawn_error = 0; bytes_read = 0; while (bytes_read < sizeof spawn_error) { r = read(err_fds[0], bytes_read + (char*) &spawn_error, sizeof spawn_error - bytes_read); if (r == -1 && errno == EINTR) continue; if (r == -1) goto error; if (r == 0) break; bytes_read += r; } if (bytes_read == 4) { errno = spawn_error; goto error; } if (r != 0) goto error; close(err_fds[0]); return child_pid; error: { int saved_errno = errno; if (err_fds[0] != -1) close(err_fds[0]); if (err_fds[1] != -1) close(err_fds[1]); if (child_pid > 0) kill(-child_pid, SIGKILL); printf("spawn error %s\n", strerror(saved_errno)); errno = saved_errno; return -1; } }
/* 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); } }