Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
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);
}
Exemplo n.º 3
0
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);
}
Exemplo n.º 4
0
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);
}
Exemplo n.º 5
0
/** 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;
}