Пример #1
0
/*
 * Used for initializing stdio streams like options.stdin_stream. Returns
 * zero on success.
 */
static int uv__process_init_pipe(uv_pipe_t* handle, int fds[2], int flags) {
  if (handle->type != UV_NAMED_PIPE) {
    errno = EINVAL;
    return -1;
  }

  if (handle->ipc)
    return uv__make_socketpair(fds, flags);
  else
    return uv__make_pipe(fds, flags);
}
Пример #2
0
/*
 * Used for initializing stdio streams like options.stdin_stream. Returns
 * zero on success.
 */
static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2],
                                  int writable) {
  int fd = -1;
  switch (container->flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
                              UV_INHERIT_STREAM | UV_NATIVE_PIPE)) {
    case UV_IGNORE:
      return 0;
    case UV_CREATE_PIPE:
      assert(container->data.stream != NULL);

      if (container->data.stream->type != UV_NAMED_PIPE) {
        errno = EINVAL;
        return -1;
      }

      return uv__make_socketpair(fds, 0);
    case UV_NATIVE_PIPE:
      assert(container->data.stream != NULL);

      if(uv__make_pipe(*(int**)container->data.pipe, 0) == -1)
          return -1;
      if (writable)
          fds[1] = container->data.pipe->write;
      else
          fds[0] = container->data.pipe->read;

      return 0;
    case UV_INHERIT_FD:
    case UV_INHERIT_STREAM:
      if (container->flags & UV_INHERIT_FD) {
        fd = container->data.fd;
      } else {
        fd = container->data.stream->fd;
      }

      if (fd == -1) {
        errno = EINVAL;
        return -1;
      }

      fds[writable ? 1 : 0] = fd;

      return 0;
    default:
      assert(0 && "Unexpected flags");
      return -1;
  }
}
Пример #3
0
int uv__async_start(uv_loop_t* loop, struct uv__async* wa, uv__async_cb cb) {
    int pipefd[2];
    int err;

    if (wa->io_watcher.fd != -1)
        return 0;

    err = uv__async_eventfd();
    if (err >= 0) {
        pipefd[0] = err;
        pipefd[1] = -1;
    }
    else if (err == -ENOSYS) {
        err = uv__make_pipe(pipefd, UV__F_NONBLOCK);
#if defined(__linux__)
        /* Save a file descriptor by opening one of the pipe descriptors as
         * read/write through the procfs.  That file descriptor can then
         * function as both ends of the pipe.
         */
        if (err == 0) {
            char buf[32];
            int fd;

            snprintf(buf, sizeof(buf), "/proc/self/fd/%d", pipefd[0]);
            fd = uv__open_cloexec(buf, O_RDWR);
            if (fd >= 0) {
                uv__close(pipefd[0]);
                uv__close(pipefd[1]);
                pipefd[0] = fd;
                pipefd[1] = fd;
            }
        }
#endif
    }

    if (err < 0)
        return err;

    uv__io_init(&wa->io_watcher, uv__async_io, pipefd[0]);
    uv__io_start(loop, &wa->io_watcher, POLLIN);
    wa->wfd = pipefd[1];
    wa->cb = cb;

    return 0;
}
Пример #4
0
int uv_spawn(uv_loop_t* loop,
             uv_process_t* process,
             const uv_process_options_t* options) {
  int signal_pipe[2] = { -1, -1 };
  int (*pipes)[2];
  int stdio_count;
  QUEUE* q;
  ssize_t r;
  pid_t pid;
  int err;
  int exec_errorno;
  int i;

  assert(options->file != NULL);
  assert(!(options->flags & ~(UV_PROCESS_DETACHED |
                              UV_PROCESS_SETGID |
                              UV_PROCESS_SETUID |
                              UV_PROCESS_WINDOWS_HIDE |
                              UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));

  uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
  QUEUE_INIT(&process->queue);

  stdio_count = options->stdio_count;
  if (stdio_count < 3)
    stdio_count = 3;

  err = -ENOMEM;
  pipes = malloc(stdio_count * sizeof(*pipes));
  if (pipes == NULL)
    goto error;

  for (i = 0; i < stdio_count; i++) {
    pipes[i][0] = -1;
    pipes[i][1] = -1;
  }

  for (i = 0; i < options->stdio_count; i++) {
    err = uv__process_init_stdio(options->stdio + i, pipes[i]);
    if (err)
      goto error;
  }

  /* This pipe is used by the parent to wait until
   * the child has called `execve()`. We need this
   * to avoid the following race condition:
   *
   *    if ((pid = fork()) > 0) {
   *      kill(pid, SIGTERM);
   *    }
   *    else if (pid == 0) {
   *      execve("/bin/cat", argp, envp);
   *    }
   *
   * The parent sends a signal immediately after forking.
   * Since the child may not have called `execve()` yet,
   * there is no telling what process receives the signal,
   * our fork or /bin/cat.
   *
   * To avoid ambiguity, we create a pipe with both ends
   * marked close-on-exec. Then, after the call to `fork()`,
   * the parent polls the read end until it EOFs or errors with EPIPE.
   */
  err = uv__make_pipe(signal_pipe, 0);
  if (err)
    goto error;

  uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);

  /* Acquire write lock to prevent opening new fds in worker threads */
  uv_rwlock_wrlock(&loop->cloexec_lock);
  pid = fork();

  if (pid == -1) {
    err = -errno;
    uv_rwlock_wrunlock(&loop->cloexec_lock);
    uv__close(signal_pipe[0]);
    uv__close(signal_pipe[1]);
    goto error;
  }

  if (pid == 0) {
    uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]);
    abort();
  }

  /* Release lock in parent process */
  uv_rwlock_wrunlock(&loop->cloexec_lock);
  uv__close(signal_pipe[1]);

  process->status = 0;
  exec_errorno = 0;
  do
    r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno));
  while (r == -1 && errno == EINTR);

  if (r == 0)
    ; /* okay, EOF */
  else if (r == sizeof(exec_errorno))
    ; /* okay, read errorno */
  else if (r == -1 && errno == EPIPE)
    ; /* okay, got EPIPE */
  else
    abort();

  uv__close(signal_pipe[0]);

  for (i = 0; i < options->stdio_count; i++) {
    err = uv__process_open_stream(options->stdio + i, pipes[i], i == 0);
    if (err == 0)
      continue;

    while (i--)
      uv__process_close_stream(options->stdio + i);

    goto error;
  }

  /* Only activate this handle if exec() happened successfully */
  if (exec_errorno == 0) {
    q = uv__process_queue(loop, pid);
    QUEUE_INSERT_TAIL(q, &process->queue);
    uv__handle_start(process);
  }

  process->pid = pid;
  process->exit_cb = options->exit_cb;

  free(pipes);
  return exec_errorno;

error:
  if (pipes != NULL) {
    for (i = 0; i < stdio_count; i++) {
      if (i < options->stdio_count)
        if (options->stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM))
          continue;
      if (pipes[i][0] != -1)
        close(pipes[i][0]);
      if (pipes[i][1] != -1)
        close(pipes[i][1]);
    }
    free(pipes);
  }

  return err;
}
Пример #5
0
int uv_spawn(uv_loop_t* loop, uv_process_t* process,
    uv_process_options_t options) {
  /*
   * Save environ in the case that we get it clobbered
   * by the child process.
   */
  char** save_our_env = environ;
  int stdin_pipe[2] = { -1, -1 };
  int stdout_pipe[2] = { -1, -1 };
  int stderr_pipe[2] = { -1, -1 };
#if SPAWN_WAIT_EXEC
  int signal_pipe[2] = { -1, -1 };
  struct pollfd pfd;
#endif
  int status;
  pid_t pid;
  int flags;

  uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
  loop->counters.process_init++;

  process->exit_cb = options.exit_cb;

  if (options.stdin_stream &&
      uv__process_init_pipe(options.stdin_stream, stdin_pipe, 0)) {
    goto error;
  }

  if (options.stdout_stream &&
      uv__process_init_pipe(options.stdout_stream, stdout_pipe, 0)) {
    goto error;
  }

  if (options.stderr_stream &&
      uv__process_init_pipe(options.stderr_stream, stderr_pipe, 0)) {
    goto error;
  }

  /* This pipe is used by the parent to wait until
   * the child has called `execve()`. We need this
   * to avoid the following race condition:
   *
   *    if ((pid = fork()) > 0) {
   *      kill(pid, SIGTERM);
   *    }
   *    else if (pid == 0) {
   *      execve("/bin/cat", argp, envp);
   *    }
   *
   * The parent sends a signal immediately after forking.
   * Since the child may not have called `execve()` yet,
   * there is no telling what process receives the signal,
   * our fork or /bin/cat.
   *
   * To avoid ambiguity, we create a pipe with both ends
   * marked close-on-exec. Then, after the call to `fork()`,
   * the parent polls the read end until it sees POLLHUP.
   */
#if SPAWN_WAIT_EXEC
  if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK))
    goto error;
#endif

  pid = fork();

  if (pid == -1) {
#if SPAWN_WAIT_EXEC
    uv__close(signal_pipe[0]);
    uv__close(signal_pipe[1]);
#endif
    environ = save_our_env;
    goto error;
  }

  if (pid == 0) {
    if (stdin_pipe[0] >= 0) {
      uv__close(stdin_pipe[1]);
      dup2(stdin_pipe[0],  STDIN_FILENO);
    } else {
      /* Reset flags that might be set by Node */
      uv__cloexec(STDIN_FILENO, 0);
      uv__nonblock(STDIN_FILENO, 0);
    }

    if (stdout_pipe[1] >= 0) {
      uv__close(stdout_pipe[0]);
      dup2(stdout_pipe[1], STDOUT_FILENO);
    } else {
      /* Reset flags that might be set by Node */
      uv__cloexec(STDOUT_FILENO, 0);
      uv__nonblock(STDOUT_FILENO, 0);
    }

    if (stderr_pipe[1] >= 0) {
      uv__close(stderr_pipe[0]);
      dup2(stderr_pipe[1], STDERR_FILENO);
    } else {
      /* Reset flags that might be set by Node */
      uv__cloexec(STDERR_FILENO, 0);
      uv__nonblock(STDERR_FILENO, 0);
    }

    if (options.cwd && chdir(options.cwd)) {
      perror("chdir()");
      _exit(127);
    }

    environ = options.env;

    execvp(options.file, options.args);
    perror("execvp()");
    _exit(127);
    /* Execution never reaches here. */
  }

  /* Parent. */

  /* Restore environment. */
  environ = save_our_env;

#if SPAWN_WAIT_EXEC
  /* POLLHUP signals child has exited or execve()'d. */
  uv__close(signal_pipe[1]);
  do {
    pfd.fd = signal_pipe[0];
    pfd.events = POLLIN|POLLHUP;
    pfd.revents = 0;
    errno = 0, status = poll(&pfd, 1, -1);
  }
  while (status == -1 && (errno == EINTR || errno == ENOMEM));

  assert((status == 1) && "poll() on pipe read end failed");
  uv__close(signal_pipe[0]);
#endif

  process->pid = pid;

  ev_child_init(&process->child_watcher, uv__chld, pid, 0);
  ev_child_start(process->loop->ev, &process->child_watcher);
  process->child_watcher.data = process;

  if (stdin_pipe[1] >= 0) {
    assert(options.stdin_stream);
    assert(stdin_pipe[0] >= 0);
    uv__close(stdin_pipe[0]);
    uv__nonblock(stdin_pipe[1], 1);
    flags = UV_WRITABLE | (options.stdin_stream->ipc ? UV_READABLE : 0);
    uv__stream_open((uv_stream_t*)options.stdin_stream, stdin_pipe[1],
        flags);
  }

  if (stdout_pipe[0] >= 0) {
    assert(options.stdout_stream);
    assert(stdout_pipe[1] >= 0);
    uv__close(stdout_pipe[1]);
    uv__nonblock(stdout_pipe[0], 1);
    flags = UV_READABLE | (options.stdout_stream->ipc ? UV_WRITABLE : 0);
    uv__stream_open((uv_stream_t*)options.stdout_stream, stdout_pipe[0],
        flags);
  }

  if (stderr_pipe[0] >= 0) {
    assert(options.stderr_stream);
    assert(stderr_pipe[1] >= 0);
    uv__close(stderr_pipe[1]);
    uv__nonblock(stderr_pipe[0], 1);
    flags = UV_READABLE | (options.stderr_stream->ipc ? UV_WRITABLE : 0);
    uv__stream_open((uv_stream_t*)options.stderr_stream, stderr_pipe[0],
        flags);
  }

  return 0;

error:
  uv__set_sys_error(process->loop, errno);
  uv__close(stdin_pipe[0]);
  uv__close(stdin_pipe[1]);
  uv__close(stdout_pipe[0]);
  uv__close(stdout_pipe[1]);
  uv__close(stderr_pipe[0]);
  uv__close(stderr_pipe[1]);
  return -1;
}
Пример #6
0
int uv_spawn(uv_loop_t* loop, uv_process_t* process,
    uv_process_options_t options) {
  /*
   * Save environ in the case that we get it clobbered
   * by the child process.
   */
  char** save_our_env = environ;

  int* pipes = malloc(2 * options.stdio_count * sizeof(int));

#if SPAWN_WAIT_EXEC
  int signal_pipe[2] = { -1, -1 };
  struct pollfd pfd;
#endif
  int status;
  pid_t pid;
  int i;

  if (pipes == NULL) {
    errno = ENOMEM;
    goto error;
  }

  assert(options.file != NULL);
  assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
                             UV_PROCESS_DETACHED |
                             UV_PROCESS_SETGID |
                             UV_PROCESS_SETUID)));


  uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
  loop->counters.process_init++;
  uv__handle_start(process);

  process->exit_cb = options.exit_cb;

  /* Init pipe pairs */
  for (i = 0; i < options.stdio_count; i++) {
    pipes[i * 2] = -1;
    pipes[i * 2 + 1] = -1;
  }

  /* Create socketpairs/pipes, or use raw fd */
  for (i = 0; i < options.stdio_count; i++) {
    if (uv__process_init_stdio(&options.stdio[i], pipes + i * 2, i != 0)) {
      goto error;
    }
  }

  /* This pipe is used by the parent to wait until
   * the child has called `execve()`. We need this
   * to avoid the following race condition:
   *
   *    if ((pid = fork()) > 0) {
   *      kill(pid, SIGTERM);
   *    }
   *    else if (pid == 0) {
   *      execve("/bin/cat", argp, envp);
   *    }
   *
   * The parent sends a signal immediately after forking.
   * Since the child may not have called `execve()` yet,
   * there is no telling what process receives the signal,
   * our fork or /bin/cat.
   *
   * To avoid ambiguity, we create a pipe with both ends
   * marked close-on-exec. Then, after the call to `fork()`,
   * the parent polls the read end until it sees POLLHUP.
   */
#if SPAWN_WAIT_EXEC
  if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK))
    goto error;
#endif

  pid = fork();

  if (pid == -1) {
#if SPAWN_WAIT_EXEC
    close(signal_pipe[0]);
    close(signal_pipe[1]);
#endif
    environ = save_our_env;
    goto error;
  }

  if (pid == 0) {
    /* Child */
    if (options.flags & UV_PROCESS_DETACHED) {
      setsid();
    }

    /* Dup fds */
    for (i = 0; i < options.stdio_count; i++) {
      /*
       * stdin has swapped ends of pipe
       * (it's the only one readable stream)
       */
      int close_fd = i == 0 ? pipes[i * 2 + 1] : pipes[i * 2];
      int use_fd = i == 0 ? pipes[i * 2] : pipes[i * 2 + 1];

      if (use_fd >= 0) {
        close(close_fd);
        dup2(use_fd, i);
      } else {
        /* Reset flags that might be set by Node */
        uv__cloexec(i, 0);
        uv__nonblock(i, 0);
      }
    }

    if (options.cwd && chdir(options.cwd)) {
      perror("chdir()");
      _exit(127);
    }

    if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
      perror("setgid()");
      _exit(127);
    }

    if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) {
      perror("setuid()");
      _exit(127);
    }

    environ = options.env;

    execvp(options.file, options.args);
    perror("execvp()");
    _exit(127);
    /* Execution never reaches here. */
  }

  /* Parent. */

  /* Restore environment. */
  environ = save_our_env;

#if SPAWN_WAIT_EXEC
  /* POLLHUP signals child has exited or execve()'d. */
  close(signal_pipe[1]);
  do {
    pfd.fd = signal_pipe[0];
    pfd.events = POLLIN|POLLHUP;
    pfd.revents = 0;
    errno = 0, status = poll(&pfd, 1, -1);
  }
  while (status == -1 && (errno == EINTR || errno == ENOMEM));

  assert((status == 1) && "poll() on pipe read end failed");
  close(signal_pipe[0]);
#endif

  process->pid = pid;

  ev_child_init(&process->child_watcher, uv__chld, pid, 0);
  ev_child_start(process->loop->ev, &process->child_watcher);
  process->child_watcher.data = process;

  for (i = 0; i < options.stdio_count; i++) {
    if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
      int j;
      /* Close all opened streams */
      for (j = 0; j < i; j++) {
        uv__process_close_stream(&options.stdio[j]);
      }

      goto error;
    }
  }

  free(pipes);

  return 0;

error:
  uv__set_sys_error(process->loop, errno);

  for (i = 0; i < options.stdio_count; i++) {
    close(pipes[i * 2]);
    close(pipes[i * 2 + 1]);
  }

  free(pipes);

  return -1;
}