static void test_util_process_set_waitpid_callback(void *ignored) { (void)ignored; waitpid_callback_t *res; int previous_log = setup_capture_of_logs(LOG_WARN); pid_t pid = (pid_t)42; res = set_waitpid_callback(pid, temp_callback, NULL); tt_assert(res); res = set_waitpid_callback(pid, temp_callback, NULL); tt_assert(res); tt_str_op(mock_saved_log_at(0), OP_EQ, "Replaced a waitpid monitor on pid 42. That should be impossible.\n"); done: teardown_capture_of_logs(previous_log); }
static void test_util_process_set_waitpid_callback(void *ignored) { (void)ignored; waitpid_callback_t *res1 = NULL, *res2 = NULL; int previous_log = setup_capture_of_logs(LOG_WARN); pid_t pid = (pid_t)42; res1 = set_waitpid_callback(pid, temp_callback, NULL); tt_assert(res1); res2 = set_waitpid_callback(pid, temp_callback, NULL); tt_assert(res2); expect_log_msg("Replaced a waitpid monitor on pid 42. That should be " "impossible.\n"); done: teardown_capture_of_logs(previous_log); clear_waitpid_callback(res1); clear_waitpid_callback(res2); }
static void test_util_process_clear_waitpid_callback(void *ignored) { (void)ignored; waitpid_callback_t *res; int previous_log = setup_capture_of_logs(LOG_WARN); pid_t pid = (pid_t)43; clear_waitpid_callback(NULL); res = set_waitpid_callback(pid, temp_callback, NULL); clear_waitpid_callback(res); clear_waitpid_callback(res); // done: teardown_capture_of_logs(previous_log); }
static void test_util_process_clear_waitpid_callback(void *ignored) { (void)ignored; waitpid_callback_t *res; int previous_log = setup_capture_of_logs(LOG_WARN); pid_t pid = (pid_t)43; clear_waitpid_callback(NULL); res = set_waitpid_callback(pid, temp_callback, NULL); clear_waitpid_callback(res); expect_no_log_entry(); #if 0 /* No. This is use-after-free. We don't _do_ that. XXXX */ clear_waitpid_callback(res); expect_log_msg("Couldn't remove waitpid monitor for pid 43.\n"); #endif done: teardown_capture_of_logs(previous_log); }
/** Executes the given process as a child process of Tor. This function is * responsible for setting up the child process and run it. This includes * setting up pipes for interprocess communication, initialize the waitpid * callbacks, and finally run fork() followed by execve(). Returns * <b>PROCESS_STATUS_RUNNING</b> upon success. */ process_status_t process_unix_exec(process_t *process) { static int max_fd = -1; process_unix_t *unix_process; pid_t pid; int stdin_pipe[2]; int stdout_pipe[2]; int stderr_pipe[2]; int retval, fd; unix_process = process_get_unix_process(process); /* Create standard in pipe. */ retval = pipe(stdin_pipe); if (-1 == retval) { log_warn(LD_PROCESS, "Unable to create pipe for stdin " "communication with process: %s", strerror(errno)); return PROCESS_STATUS_ERROR; } /* Create standard out pipe. */ retval = pipe(stdout_pipe); if (-1 == retval) { log_warn(LD_PROCESS, "Unable to create pipe for stdout " "communication with process: %s", strerror(errno)); /** Cleanup standard in pipe. */ close(stdin_pipe[0]); close(stdin_pipe[1]); return PROCESS_STATUS_ERROR; } /* Create standard error pipe. */ retval = pipe(stderr_pipe); if (-1 == retval) { log_warn(LD_PROCESS, "Unable to create pipe for stderr " "communication with process: %s", strerror(errno)); /** Cleanup standard in pipe. */ close(stdin_pipe[0]); close(stdin_pipe[1]); /** Cleanup standard out pipe. */ close(stdout_pipe[0]); close(stdout_pipe[1]); return PROCESS_STATUS_ERROR; } #ifdef _SC_OPEN_MAX if (-1 == max_fd) { max_fd = (int)sysconf(_SC_OPEN_MAX); if (max_fd == -1) { max_fd = DEFAULT_MAX_FD; log_warn(LD_PROCESS, "Cannot find maximum file descriptor, assuming: %d", max_fd); } } #else /* !(defined(_SC_OPEN_MAX)) */ max_fd = DEFAULT_MAX_FD; #endif /* defined(_SC_OPEN_MAX) */ pid = fork(); if (0 == pid) { /* This code is running in the child process context. */ #if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) /* Attempt to have the kernel issue a SIGTERM if the parent * goes away. Certain attributes of the binary being execve()ed * will clear this during the execve() call, but it's better * than nothing. */ prctl(PR_SET_PDEATHSIG, SIGTERM); #endif /* defined(HAVE_SYS_PRCTL_H) && defined(__linux__) */ /* Link process stdout to the write end of the pipe. */ retval = dup2(stdout_pipe[1], STDOUT_FILENO); if (-1 == retval) goto error; /* Link process stderr to the write end of the pipe. */ retval = dup2(stderr_pipe[1], STDERR_FILENO); if (-1 == retval) goto error; /* Link process stdin to the read end of the pipe */ retval = dup2(stdin_pipe[0], STDIN_FILENO); if (-1 == retval) goto error; /* Close our pipes now after they have been dup2()'ed. */ close(stderr_pipe[0]); close(stderr_pipe[1]); close(stdout_pipe[0]); close(stdout_pipe[1]); close(stdin_pipe[0]); close(stdin_pipe[1]); /* Close all other fds, including the read end of the pipe. XXX: We should * now be doing enough FD_CLOEXEC setting to make this needless. */ for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) close(fd); /* Create the argv value for our new process. */ char **argv = process_get_argv(process); /* Create the env value for our new process. */ process_environment_t *env = process_get_environment(process); /* Call the requested program. */ retval = execve(argv[0], argv, env->unixoid_environment_block); /* If we made it here it is because execve failed :-( */ if (-1 == retval) fprintf(stderr, "Call to execve() failed: %s", strerror(errno)); tor_free(argv); process_environment_free(env); tor_assert_unreached(); error: /* LCOV_EXCL_START */ fprintf(stderr, "Error from child process: %s", strerror(errno)); _exit(1); /* LCOV_EXCL_STOP */ } /* We are in the parent process. */ if (-1 == pid) { log_warn(LD_PROCESS, "Failed to create child process: %s", strerror(errno)); /** Cleanup standard in pipe. */ close(stdin_pipe[0]); close(stdin_pipe[1]); /** Cleanup standard out pipe. */ close(stdout_pipe[0]); close(stdout_pipe[1]); /** Cleanup standard error pipe. */ close(stderr_pipe[0]); close(stderr_pipe[1]); return PROCESS_STATUS_ERROR; } /* Register our PID. */ unix_process->pid = pid; /* Setup waitpid callbacks. */ unix_process->waitpid = set_waitpid_callback(pid, process_unix_waitpid_callback, process); /* Handle standard out. */ unix_process->stdout_handle.fd = stdout_pipe[0]; retval = close(stdout_pipe[1]); if (-1 == retval) { log_warn(LD_PROCESS, "Failed to close write end of standard out pipe: %s", strerror(errno)); } /* Handle standard error. */ unix_process->stderr_handle.fd = stderr_pipe[0]; retval = close(stderr_pipe[1]); if (-1 == retval) { log_warn(LD_PROCESS, "Failed to close write end of standard error pipe: %s", strerror(errno)); } /* Handle standard in. */ unix_process->stdin_handle.fd = stdin_pipe[1]; retval = close(stdin_pipe[0]); if (-1 == retval) { log_warn(LD_PROCESS, "Failed to close read end of standard in pipe: %s", strerror(errno)); } /* Setup our handles. */ process_unix_setup_handle(process, &unix_process->stdout_handle, EV_READ|EV_PERSIST, stdout_read_callback); process_unix_setup_handle(process, &unix_process->stderr_handle, EV_READ|EV_PERSIST, stderr_read_callback); process_unix_setup_handle(process, &unix_process->stdin_handle, EV_WRITE|EV_PERSIST, stdin_write_callback); /* Start reading from standard out and standard error. */ process_unix_start_reading(&unix_process->stdout_handle); process_unix_start_reading(&unix_process->stderr_handle); return PROCESS_STATUS_RUNNING; }