Beispiel #1
1
static bool run_helper(const char ** argv)
{
    posix_spawn_file_actions_t action;
    int status;
    int err;
    pid_t child;

    err = posix_spawn_file_actions_init(&action);
    if (err != 0) {
	VERBOSE("spawn init failed for %s: %s\n", *argv, strerror(err));
	return false;
    }

    /* Redirect /dev/null to stdin. */
    err = posix_spawn_file_actions_addopen(&action, STDIN_FILENO,
		"/dev/null", O_RDONLY, 0 /* mode */);
    if (err != 0) {
	VERBOSE("stdin redirect failed for %s: %s\n", *argv, strerror(err));
    }

    /* Redirect stdout to /dev/null unless we are debugging. We continue past
     * any errors because redirecting is a nicety, rather than an essential
     * functions.
     */
    if (!(Options::Verbose || Options::Debug)) {
	err = posix_spawn_file_actions_addopen(&action, STDOUT_FILENO,
		    "/dev/null", O_WRONLY, 0 /* mode */);
	if (err != 0) {
	    VERBOSE("stdout redirect failed for %s: %s\n",
		    *argv, strerror(err));
	}

	err = posix_spawn_file_actions_addopen(&action, STDERR_FILENO,
		    "/dev/null", O_WRONLY, 0 /* mode */);
	if (err != 0) {
	    VERBOSE("stderr redirect failed for %s: %s\n",
		    *argv, strerror(err));
	}
    }

    err = posix_spawn(&child, *argv, &action, NULL /* posix_spawnattr_t */,
			(char * const *)argv, NULL /* environ */);
    if (err != 0) {
	VERBOSE("failed to spawn %s: %s\n", *argv, strerror(err));
	posix_spawn_file_actions_destroy(&action);
	return false;
    }

    posix_spawn_file_actions_destroy(&action);

    while (waitpid(child, &status, 0) != child) {
    }

    if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS) {
	/* yay, the helper ran and secceeded. */
	return true;
    }

    return false;
}
Beispiel #2
0
void launch_thread(jobtype ptype, pkgstate* state, pkg_exec* item, pkgdata* data) {
	char* arr[2];
	create_script(ptype, state, item, data);
	log_timestamp(1);
	log_putspace(1);
	if(ptype == JT_DOWNLOAD) {
		log_puts(1, SPL("downloading "));
	} else 
		log_puts(1, SPL("building "));

	log_put(1, VARIS(item->name), VARISL("("), VARIS(item->scripts.filename), VARISL(") -> "), VARIS(item->scripts.stdoutfn), NULL);

	arr[0] = item->scripts.filename->ptr;
	arr[1] = NULL;
	
	posix_spawn_file_actions_init(&item->fa);
	posix_spawn_file_actions_addclose(&item->fa, 0);
	posix_spawn_file_actions_addclose(&item->fa, 1);
	posix_spawn_file_actions_addclose(&item->fa, 2);
	posix_spawn_file_actions_addopen(&item->fa, 0, "/dev/null", O_RDONLY, 0);
	posix_spawn_file_actions_addopen(&item->fa, 1, item->scripts.stdoutfn->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
	posix_spawn_file_actions_adddup2(&item->fa, 1, 2);
	int ret = posix_spawnp(&item->pid, arr[0], &item->fa, NULL, arr, environ);
	if(ret == -1) {
		log_perror("posix_spawn");
		die(SPL(""));
	}
}
Beispiel #3
0
static void spawn_win32(void) {
  char module_name[WATCHMAN_NAME_MAX];
  GetModuleFileName(NULL, module_name, sizeof(module_name));
  char *argv[MAX_DAEMON_ARGS] = {
    module_name,
    "--foreground",
    NULL
  };
  posix_spawn_file_actions_t actions;
  posix_spawnattr_t attr;
  pid_t pid;
  int i;

  for (i = 0; daemon_argv[i]; i++) {
    append_argv(argv, daemon_argv[i]);
  }

  posix_spawnattr_init(&attr);
  posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP);
  posix_spawn_file_actions_init(&actions);
  posix_spawn_file_actions_addopen(&actions,
      STDIN_FILENO, "/dev/null", O_RDONLY, 0);
  posix_spawn_file_actions_addopen(&actions,
      STDOUT_FILENO, log_name, O_WRONLY|O_CREAT|O_APPEND, 0600);
  posix_spawn_file_actions_adddup2(&actions,
      STDOUT_FILENO, STDERR_FILENO);
  posix_spawnp(&pid, argv[0], &actions, &attr, argv, environ);
  posix_spawnattr_destroy(&attr);
  posix_spawn_file_actions_destroy(&actions);
}
Beispiel #4
0
static int
parent_main (void)
{
  FILE *fp;
  char *argv[3] = { CHILD_PROGRAM_FILENAME, "-child", NULL };
  posix_spawn_file_actions_t actions;
  bool actions_allocated;
  int err;
  pid_t child;
  int status;
  int exitstatus;

  /* Create a data file with specific contents.  */
  fp = fopen (DATA_FILENAME, "wb");
  if (fp == NULL)
    {
      perror ("cannot create data file");
      return 1;
    }
  fwrite ("Halle Potta", 1, 11, fp);
  if (fflush (fp) || fclose (fp))
    {
      perror ("cannot prepare data file");
      return 1;
    }

  /* Avoid reading from our stdin, as it could block.  */
  freopen ("/dev/null", "rb", stdin);

  /* Test whether posix_spawn_file_actions_addopen with this file name
     actually works, but spawning a child that reads from this file.  */
  actions_allocated = false;
  if ((err = posix_spawn_file_actions_init (&actions)) != 0
      || (actions_allocated = true,
          (err = posix_spawn_file_actions_addopen (&actions, STDIN_FILENO, DATA_FILENAME, O_RDONLY, 0600)) != 0
          || (err = posix_spawn (&child, CHILD_PROGRAM_FILENAME, &actions, NULL, argv, environ)) != 0))
    {
      if (actions_allocated)
        posix_spawn_file_actions_destroy (&actions);
      errno = err;
      perror ("subprocess failed");
      return 1;
    }
  posix_spawn_file_actions_destroy (&actions);
  status = 0;
  while (waitpid (child, &status, 0) != child)
    ;
  if (!WIFEXITED (status))
    {
      fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
      return 1;
    }
  exitstatus = WEXITSTATUS (status);
  if (exitstatus != 0)
    {
      fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
      return 1;
    }
  return 0;
}
Beispiel #5
0
static void spawn_via_gimli(void)
{
  char *argv[MAX_DAEMON_ARGS] = {
    GIMLI_MONITOR_PATH,
#ifdef WATCHMAN_STATE_DIR
    "--trace-dir=" WATCHMAN_STATE_DIR "/traces",
#endif
    "--pidfile", pid_file,
    "watchman",
    "--foreground",
    NULL
  };
  posix_spawn_file_actions_t actions;
  posix_spawnattr_t attr;
  pid_t pid;
  int i;

  for (i = 0; daemon_argv[i]; i++) {
    append_argv(argv, daemon_argv[i]);
  }

  close_random_fds();

  posix_spawnattr_init(&attr);
  posix_spawn_file_actions_init(&actions);
  posix_spawn_file_actions_addopen(&actions,
      STDOUT_FILENO, log_name, O_WRONLY|O_CREAT|O_APPEND, 0600);
  posix_spawn_file_actions_adddup2(&actions,
      STDOUT_FILENO, STDERR_FILENO);
  posix_spawnp(&pid, argv[0], &actions, &attr, argv, environ);
  posix_spawnattr_destroy(&attr);
  posix_spawn_file_actions_destroy(&actions);
}
Beispiel #6
0
static pid_t
spawn_staprun(char** args)
{
  pid_t pid = -1;
  posix_spawn_file_actions_t fa;

  int err;
  if ((err = posix_spawn_file_actions_init(&fa)) != 0)
    {
      reply ("ERROR: Can't initialize posix_spawn actions: %s\n", strerror(err));
      return -1;
    }

  // no stdin for staprun
  if ((err = posix_spawn_file_actions_addopen(&fa, 0, "/dev/null", O_RDONLY, 0)) != 0)
    {
      reply("ERROR: Can't set posix_spawn actions: %s\n", strerror(err));
      posix_spawn_file_actions_destroy(&fa);
      return -1;
    }

  if ((err = posix_spawn(&pid, args[0], &fa, NULL, args, environ)) != 0)
    {
      reply("ERROR: Can't launch stap backend: %s\n", strerror(err));
      posix_spawn_file_actions_destroy(&fa);
      return -1;
    }

  posix_spawn_file_actions_destroy(&fa);
  return pid;
}
Beispiel #7
0
TEST(spawn, posix_spawn_file_actions) {
  int fds[2];
  ASSERT_NE(-1, pipe(fds));

  posix_spawn_file_actions_t fa;
  ASSERT_EQ(0, posix_spawn_file_actions_init(&fa));

  ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[0]));
  ASSERT_EQ(0, posix_spawn_file_actions_adddup2(&fa, fds[1], 1));
  ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[1]));
  // Check that close(2) failures are ignored by closing the same fd again.
  ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[1]));
  ASSERT_EQ(0, posix_spawn_file_actions_addopen(&fa, 56, "/proc/version", O_RDONLY, 0));

  ExecTestHelper eth;
  eth.SetArgs({"ls", "-l", "/proc/self/fd", nullptr});
  pid_t pid;
  ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), &fa, nullptr, eth.GetArgs(), eth.GetEnv()));
  ASSERT_EQ(0, posix_spawn_file_actions_destroy(&fa));

  ASSERT_EQ(0, close(fds[1]));
  std::string content;
  ASSERT_TRUE(android::base::ReadFdToString(fds[0], &content));
  ASSERT_EQ(0, close(fds[0]));

  AssertChildExited(pid, 0);

  // We'll know the dup2 worked if we see any ls(1) output in our pipe.
  // The open we can check manually...
  bool open_to_fd_56_worked = false;
  for (const auto& line : android::base::Split(content, "\n")) {
    if (line.find(" 56 -> /proc/version") != std::string::npos) open_to_fd_56_worked = true;
  }
  ASSERT_TRUE(open_to_fd_56_worked);
}
Beispiel #8
0
int __darwin_posix_spawn_file_actions_addopen(
    posix_spawn_file_actions_t** file_actions,
    int fd,
    const char* path,
    int oflag,
    int mode) {
    return posix_spawn_file_actions_addopen(
        *file_actions, fd, path, oflag, mode);
}
Beispiel #9
0
void ChildProcess::Options::open(
    int targetFd,
    const char* path,
    int flags,
    int mode) {
  auto err = posix_spawn_file_actions_addopen(
      &inner_->actions, targetFd, path, flags, mode);
  if (err) {
    throw std::system_error(
        err, std::generic_category(), "posix_spawn_file_actions_addopen");
  }
}
Beispiel #10
0
// Spawn watchman via a site-specific spawn helper program.
// We'll pass along any daemon-appropriate arguments that
// we noticed during argument parsing.
static void spawn_site_specific(const char *spawner)
{
  char *argv[MAX_DAEMON_ARGS] = {
    (char*)spawner,
    NULL
  };
  posix_spawn_file_actions_t actions;
  posix_spawnattr_t attr;
  pid_t pid;
  int i;
  int res, err;

  for (i = 0; daemon_argv[i]; i++) {
    append_argv(argv, daemon_argv[i]);
  }

  close_random_fds();

  posix_spawnattr_init(&attr);
  posix_spawn_file_actions_init(&actions);
  posix_spawn_file_actions_addopen(&actions,
      STDOUT_FILENO, log_name, O_WRONLY|O_CREAT|O_APPEND, 0600);
  posix_spawn_file_actions_adddup2(&actions,
      STDOUT_FILENO, STDERR_FILENO);
  res = posix_spawnp(&pid, argv[0], &actions, &attr, argv, environ);
  err = errno;

  posix_spawnattr_destroy(&attr);
  posix_spawn_file_actions_destroy(&actions);

  if (res) {
    w_log(W_LOG_FATAL, "Failed to spawn watchman via `%s': %s\n", spawner,
          strerror(err));
  }

  if (waitpid(pid, &res, 0) == -1) {
    w_log(W_LOG_FATAL, "Failed waiting for %s: %s\n", spawner, strerror(errno));
  }

  if (WIFEXITED(res) && WEXITSTATUS(res) == 0) {
    return;
  }

  if (WIFEXITED(res)) {
    w_log(W_LOG_FATAL, "%s: exited with status %d\n", spawner,
          WEXITSTATUS(res));
  } else if (WIFSIGNALED(res)) {
    w_log(W_LOG_FATAL, "%s: signaled with %d\n", spawner, WTERMSIG(res));
  }
  w_log(W_LOG_ERR, "%s: failed to start, exit status %d\n", spawner, res);
}
Beispiel #11
0
static int l_posix_spawn_file_actions_addopen(lua_State *L) {
	int r;
	posix_spawn_file_actions_t *file_actions = luaL_checkudata(L, 1, "posix_spawn_file_actions_t");
	int fd = luaL_checkinteger(L, 2);
	const char *path = luaL_checkstring(L, 3);
	int oflag = luaL_checkinteger(L, 4);
	mode_t mode = luaL_checkinteger(L, 4);
	if (0 != (r = posix_spawn_file_actions_addopen(file_actions, fd, path, oflag, mode))) {
		lua_pushnil(L);
		lua_pushstring(L, strerror(r));
		lua_pushinteger(L, r);
		return 3;
	}
	lua_pushboolean(L, 1);
	return 1;
}
Beispiel #12
0
/**
* @fn                   : tc_libc_spawn_posix_spawn_file_actions_destroy
* @brief                : destroy the object referenced by st_fileactions
* @scenario             : destroys the object referenced by st_fileactions which was previously intialized
* @API's covered        : posix_spawn_file_actions_init, posix_spawn_file_actions_addopen ,posix_spawn_file_actions_destroy
* @Preconditions        : posix_spawn_file_actions_init ,posix_spawn_file_actions_addopen
* @Postconditions       : none
* @Return               : void
*/
static void tc_libc_spawn_posix_spawn_file_actions_destroy(void)
{
	const char szfilepath[] = "./testdata.txt";
	posix_spawn_file_actions_t st_fileactions;
	int ret_chk = ERROR;

	ret_chk = posix_spawn_file_actions_init(&st_fileactions);
	TC_ASSERT_EQ("posix_spawn_file_actions_init", ret_chk, OK);

	ret_chk = posix_spawn_file_actions_addopen(&st_fileactions, 1, szfilepath, O_WRONLY, 0644);
	TC_ASSERT_EQ("posix_spawn_file_actions_addopen", ret_chk, OK);
	TC_ASSERT_NOT_NULL("posix_spawn_file_actions_addopen", st_fileactions);

	ret_chk = posix_spawn_file_actions_destroy(&st_fileactions);
	TC_ASSERT_EQ("posix_spawn_file_actions_destroy", ret_chk, OK);
	TC_ASSERT_EQ("posix_spawn_file_actions_destroy", st_fileactions, NULL);

	TC_SUCCESS_RESULT();
}
Beispiel #13
0
int main()
{
	int ret_err = 0;
	pid_t pid_child;
	char buf_err[64];
	posix_spawn_file_actions_t posix_faction;
	char *argv_child[] = { "forkexec_child", NULL };
	printf("Parent[%d]: Start\n", getpid());
	if ((ret_err = posix_spawn_file_actions_init(&posix_faction)) != 0)
	{
		strerror_r(ret_err, buf_err, sizeof(buf_err));
		fprintf(stderr, "Fail: file_actions_addopen: %s\n", buf_err);
		exit(EXIT_FAILURE);
	}

	if ((ret_err = posix_spawn_file_actions_addopen(&posix_faction,
					3, "pspawn.log", O_WRONLY | O_CREAT | O_APPEND, 0664)) != 0)
	{
		strerror_r(ret_err, buf_err, sizeof(buf_err));
		fprintf(stderr, "Fail: file_actions_addopen: %s\n", buf_err);
		exit(EXIT_FAILURE);
	}

	ret_err = posix_spawn( &pid_child,
			argv_child[0],
			&posix_faction,
			NULL,
			argv_child,
			NULL);
	if ((ret_err = posix_spawn_file_actions_destroy(&posix_faction)) != 0)
	{
		strerror_r(ret_err, buf_err, sizeof(buf_err));
		fprintf(stderr, "Fail: file_actions_destroy: %s\n", buf_err);
		exit(EXIT_FAILURE);
	}

	printf("Parent[%d]: Wait for child(%d) \n", getpid(), (int)pid_child);
	(void)wait(NULL);
	printf("Parent[%d]: Exit\n", getpid());
	return 0;
}
Beispiel #14
0
/**
* @fn                   : tc_libc_spawn_add_file_action
* @brief                : Add the file action
* @scenario             : Add the file action to the end for the file action list
* @API's covered        : posix_spawn_file_actions_init, posix_spawn_file_actions_addopen, add_file_action
* @Preconditions        : posix_spawn_file_actions_init,posix_spawn_file_actions_addopen
* @Postconditions       : posix_spawn_file_actions_destroy
* @Return               : void
*/
static void tc_libc_spawn_add_file_action(void)
{
	const char szfilepath[] = "./testdata.txt";
	posix_spawn_file_actions_t st_fileactions;
	struct spawn_open_file_action_s *entry1;
	struct spawn_open_file_action_s *entry2;
	size_t length;
	size_t alloc_num;
	int ret_chk = ERROR;

	ret_chk = posix_spawn_file_actions_init(&st_fileactions);
	TC_ASSERT_EQ("posix_spawn_file_actions_init", ret_chk, OK);

	ret_chk = posix_spawn_file_actions_addopen(&st_fileactions, 1, szfilepath, O_WRONLY, 0644);
	TC_ASSERT_EQ("posix_spawn_file_actions_addopen", ret_chk, OK);

	length = strlen(szfilepath);
	alloc_num = SIZEOF_OPEN_FILE_ACTION_S(length);

	/* Allocate the action list entry of this size */

	entry1 = (struct spawn_open_file_action_s *)zalloc(alloc_num);
	TC_ASSERT_NOT_NULL("zalloc", entry1);

	/* And add it to the file action list */

	add_file_action(st_fileactions, (struct spawn_general_file_action_s *)entry1);

	entry2 = (struct spawn_open_file_action_s *)zalloc(alloc_num);
	TC_ASSERT_NOT_NULL("zalloc", entry2);

	/* And add it to the file action list */

	add_file_action(st_fileactions, (struct spawn_general_file_action_s *)entry2);
	TC_ASSERT_EQ("add_file_action", entry1->flink, (struct spawn_general_file_action_s *)entry2);

	ret_chk = posix_spawn_file_actions_destroy(&st_fileactions);
	TC_ASSERT_EQ("posix_spawn_file_actions_destroy", ret_chk, OK);

	TC_SUCCESS_RESULT();
}
Beispiel #15
0
/* Add an action to FILE-ACTIONS which tells the implementation to call
   'open' for the given file during the 'spawn' call.  */
int
posix_spawn_file_actions_addopen (posix_spawn_file_actions_t *file_actions,
                                  int fd, const char *path, int oflag,
                                  mode_t mode)
#undef posix_spawn_file_actions_addopen
{
  int maxfd = __sysconf (_SC_OPEN_MAX);

  /* Test for the validity of the file descriptor.  */
  if (fd < 0 || fd >= maxfd)
    return EBADF;

#if HAVE_WORKING_POSIX_SPAWN
  return posix_spawn_file_actions_addopen (file_actions, fd, path, oflag, mode);
#else
  /* Allocate more memory if needed.  */
  if (file_actions->_used == file_actions->_allocated
      && __posix_spawn_file_actions_realloc (file_actions) != 0)
    /* This can only mean we ran out of memory.  */
    return ENOMEM;

  {
    struct __spawn_action *rec;

    /* Add the new value.  */
    rec = &file_actions->_actions[file_actions->_used];
    rec->tag = spawn_do_open;
    rec->action.open_action.fd = fd;
    rec->action.open_action.path = path;
    rec->action.open_action.oflag = oflag;
    rec->action.open_action.mode = mode;

    /* Account for the new entry.  */
    ++file_actions->_used;

    return 0;
  }
#endif
}
Beispiel #16
0
/**
* @fn                   : tc_libc_spawn_posix_spawn_file_actions_addOpenClose
* @brief                : adds an open action to the object referenced by fact_p that causes the file named by path to be opened
* @scenario             : Files that need to be opened for use by the spawned process can alternatively be handled either by having the
*                         calling process open or by passing filenames to the spawned process
* @API's covered        : posix_spawnattr_init,posix_spawn_file_actions_init,posix_spawn_file_actions_addopen
* @Preconditions        : posix_spawnattr_init,posix_spawn_file_actions_init
* @Postconditions       : posix_spawn_file_actions_destroy
* @Return               : void
*/
static void tc_libc_spawn_posix_spawn_file_actions_addopenclose(void)
{
	const char szfilepath[] = "./testdata.txt";
	posix_spawn_file_actions_t st_fileactions;
	posix_spawnattr_t st_attr;
	int ret_chk = ERROR;

	ret_chk = posix_spawnattr_init(&st_attr);
	TC_ASSERT_EQ("posix_spawnattr_init", ret_chk, OK);

	ret_chk = posix_spawn_file_actions_init(&st_fileactions);
	TC_ASSERT_EQ("posix_spawn_file_actions_init", ret_chk, OK);

	ret_chk = posix_spawn_file_actions_addopen(&st_fileactions, 0, szfilepath, O_RDONLY, 0644);
	TC_ASSERT_EQ("posix_spawn_file_actions_addopen", ret_chk, OK);

	ret_chk = posix_spawn_file_actions_addclose(&st_fileactions, 0);
	TC_ASSERT_EQ("posix_spawn_file_actions_addclose", ret_chk, OK);

	ret_chk = posix_spawn_file_actions_destroy(&st_fileactions);
	TC_ASSERT_EQ("posix_spawn_file_actions_destroy", ret_chk, OK);

	TC_SUCCESS_RESULT();
}
Beispiel #17
0
int exec_builtin(FAR const char *appname, FAR char * const *argv,
                 FAR const char *redirfile, int oflags)
{
  FAR const struct builtin_s *builtin;
  posix_spawnattr_t attr;
  posix_spawn_file_actions_t file_actions;
  struct sched_param param;
  pid_t pid;
  int index;
  int ret;

  /* Verify that an application with this name exists */

  index = builtin_isavail(appname);
  if (index < 0)
    {
      ret = ENOENT;
      goto errout_with_errno;
    }

  /* Get information about the builtin */

  builtin = builtin_for_index(index);
  if (builtin == NULL)
    { 
      ret = ENOENT;
      goto errout_with_errno;
    }

  /* Initialize attributes for task_spawn(). */

  ret = posix_spawnattr_init(&attr);
  if (ret != 0)
    {
      goto errout_with_errno;
    }

  ret = posix_spawn_file_actions_init(&file_actions);
  if (ret != 0)
    {
      goto errout_with_attrs;
    }

  /* Set the correct task size and priority */

  param.sched_priority = builtin->priority;
  ret = posix_spawnattr_setschedparam(&attr, &param);
  if (ret != 0)
    {
      goto errout_with_actions;
    }

  ret = task_spawnattr_setstacksize(&attr, builtin->stacksize);
  if (ret != 0)
    {
      goto errout_with_actions;
    }

   /* If robin robin scheduling is enabled, then set the scheduling policy
    * of the new task to SCHED_RR before it has a chance to run.
    */

#if CONFIG_RR_INTERVAL > 0
  ret = posix_spawnattr_setschedpolicy(&attr, SCHED_RR);
  if (ret != 0)
    {
      goto errout_with_actions;
    }

  ret = posix_spawnattr_setflags(&attr,
                                 POSIX_SPAWN_SETSCHEDPARAM |
                                 POSIX_SPAWN_SETSCHEDULER);
  if (ret != 0)
    {
      goto errout_with_actions;
    }
#else
  ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSCHEDPARAM);
  if (ret != 0)
    {
      goto errout_with_actions;
    }
#endif

  /* Is output being redirected? */

  if (redirfile)
    {
      /* Set up to close open redirfile and set to stdout (1) */

      ret = posix_spawn_file_actions_addopen(&file_actions, 1,
                                             redirfile, O_WRONLY, 0644);
      if (ret != 0)
        {
          sdbg("ERROR: posix_spawn_file_actions_addopen failed: %d\n", ret);
          goto errout_with_actions;
        }
    }

  /* Start the built-in */

  ret = task_spawn(&pid, builtin->name, builtin->main, &file_actions,
                   &attr, (argv) ? &argv[1] : (FAR char * const *)NULL,
                   (FAR char * const *)NULL);
  if (ret != 0)
    {
      sdbg("ERROR: task_spawn failed: %d\n", ret);
      goto errout_with_actions;
    }

  /* Free attibutes and file actions.  Ignoring return values in the case
   * of an error.
   */

  /* Return the task ID of the new task if the task was sucessfully
   * started.  Otherwise, ret will be ERROR (and the errno value will
   * be set appropriately).
   */

  (void)posix_spawn_file_actions_destroy(&file_actions);
  (void)posix_spawnattr_destroy(&attr);
  return pid;

errout_with_actions:
  (void)posix_spawn_file_actions_destroy(&file_actions);

errout_with_attrs:
  (void)posix_spawnattr_destroy(&attr);

errout_with_errno:
  set_errno(ret);
  return ERROR;
}
Beispiel #18
0
/* Open a pipe connected to a child process.
 *
 *           write       system                read
 *    parent  ->   fd[1]   ->   STDIN_FILENO    ->   child       if pipe_stdin
 *    parent  <-   fd[0]   <-   STDOUT_FILENO   <-   child       if pipe_stdout
 *           read        system                write
 *
 * At least one of pipe_stdin, pipe_stdout must be true.
 * pipe_stdin and prog_stdin together determine the child's standard input.
 * pipe_stdout and prog_stdout together determine the child's standard output.
 * If pipe_stdin is true, prog_stdin is ignored.
 * If pipe_stdout is true, prog_stdout is ignored.
 */
static pid_t
create_pipe (const char *progname,
             const char *prog_path, char **prog_argv,
             bool pipe_stdin, bool pipe_stdout,
             const char *prog_stdin, const char *prog_stdout,
             bool null_stderr,
             bool slave_process, bool exit_on_error,
             int fd[2])
{
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__

  /* Native Windows API.
     This uses _pipe(), dup2(), and spawnv().  It could also be implemented
     using the low-level functions CreatePipe(), DuplicateHandle(),
     CreateProcess() and _open_osfhandle(); see the GNU make and GNU clisp
     and cvs source code.  */
  int ifd[2];
  int ofd[2];
  int orig_stdin;
  int orig_stdout;
  int orig_stderr;
  int child;
  int nulloutfd;
  int stdinfd;
  int stdoutfd;
  int saved_errno;

  /* FIXME: Need to free memory allocated by prepare_spawn.  */
  prog_argv = prepare_spawn (prog_argv);

  if (pipe_stdout)
    if (pipe2_safer (ifd, O_BINARY | O_CLOEXEC) < 0)
      error (EXIT_FAILURE, errno, _("cannot create pipe"));
  if (pipe_stdin)
    if (pipe2_safer (ofd, O_BINARY | O_CLOEXEC) < 0)
      error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           write        system         read
 *    parent  ->   ofd[1]   ->   ofd[0]   ->   child       if pipe_stdin
 *    parent  <-   ifd[0]   <-   ifd[1]   <-   child       if pipe_stdout
 *           read         system         write
 *
 */

  /* Save standard file handles of parent process.  */
  if (pipe_stdin || prog_stdin != NULL)
    orig_stdin = dup_safer_noinherit (STDIN_FILENO);
  if (pipe_stdout || prog_stdout != NULL)
    orig_stdout = dup_safer_noinherit (STDOUT_FILENO);
  if (null_stderr)
    orig_stderr = dup_safer_noinherit (STDERR_FILENO);
  child = -1;

  /* Create standard file handles of child process.  */
  nulloutfd = -1;
  stdinfd = -1;
  stdoutfd = -1;
  if ((!pipe_stdin || dup2 (ofd[0], STDIN_FILENO) >= 0)
      && (!pipe_stdout || dup2 (ifd[1], STDOUT_FILENO) >= 0)
      && (!null_stderr
          || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
              && (nulloutfd == STDERR_FILENO
                  || (dup2 (nulloutfd, STDERR_FILENO) >= 0
                      && close (nulloutfd) >= 0))))
      && (pipe_stdin
          || prog_stdin == NULL
          || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
              && (stdinfd == STDIN_FILENO
                  || (dup2 (stdinfd, STDIN_FILENO) >= 0
                      && close (stdinfd) >= 0))))
      && (pipe_stdout
          || prog_stdout == NULL
          || ((stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0
              && (stdoutfd == STDOUT_FILENO
                  || (dup2 (stdoutfd, STDOUT_FILENO) >= 0
                      && close (stdoutfd) >= 0)))))
    /* The child process doesn't inherit ifd[0], ifd[1], ofd[0], ofd[1],
       but it inherits all open()ed or dup2()ed file handles (which is what
       we want in the case of STD*_FILENO).  */
    /* Use spawnvpe and pass the environment explicitly.  This is needed if
       the program has modified the environment using putenv() or [un]setenv().
       On Windows, programs have two environments, one in the "environment
       block" of the process and managed through SetEnvironmentVariable(), and
       one inside the process, in the location retrieved by the 'environ'
       macro.  When using spawnvp() without 'e', the child process inherits a
       copy of the environment block - ignoring the effects of putenv() and
       [un]setenv().  */
    {
      child = spawnvpe (P_NOWAIT, prog_path, (const char **) prog_argv,
                        (const char **) environ);
      if (child < 0 && errno == ENOEXEC)
        {
          /* prog is not a native executable.  Try to execute it as a
             shell script.  Note that prepare_spawn() has already prepended
             a hidden element "sh.exe" to prog_argv.  */
          --prog_argv;
          child = spawnvpe (P_NOWAIT, prog_argv[0], (const char **) prog_argv,
                            (const char **) environ);
        }
    }
  if (child == -1)
    saved_errno = errno;
  if (stdinfd >= 0)
    close (stdinfd);
  if (stdoutfd >= 0)
    close (stdoutfd);
  if (nulloutfd >= 0)
    close (nulloutfd);

  /* Restore standard file handles of parent process.  */
  if (null_stderr)
    undup_safer_noinherit (orig_stderr, STDERR_FILENO);
  if (pipe_stdout || prog_stdout != NULL)
    undup_safer_noinherit (orig_stdout, STDOUT_FILENO);
  if (pipe_stdin || prog_stdin != NULL)
    undup_safer_noinherit (orig_stdin, STDIN_FILENO);

  if (pipe_stdin)
    close (ofd[0]);
  if (pipe_stdout)
    close (ifd[1]);
  if (child == -1)
    {
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, saved_errno,
               _("%s subprocess failed"), progname);
      if (pipe_stdout)
        close (ifd[0]);
      if (pipe_stdin)
        close (ofd[1]);
      errno = saved_errno;
      return -1;
    }

  if (pipe_stdout)
    fd[0] = ifd[0];
  if (pipe_stdin)
    fd[1] = ofd[1];
  return child;

#else

  /* Unix API.  */
  int ifd[2];
  int ofd[2];
  sigset_t blocked_signals;
  posix_spawn_file_actions_t actions;
  bool actions_allocated;
  posix_spawnattr_t attrs;
  bool attrs_allocated;
  int err;
  pid_t child;

  if (pipe_stdout)
    if (pipe_safer (ifd) < 0)
      error (EXIT_FAILURE, errno, _("cannot create pipe"));
  if (pipe_stdin)
    if (pipe_safer (ofd) < 0)
      error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           write        system         read
 *    parent  ->   ofd[1]   ->   ofd[0]   ->   child       if pipe_stdin
 *    parent  <-   ifd[0]   <-   ifd[1]   <-   child       if pipe_stdout
 *           read         system         write
 *
 */

  if (slave_process)
    {
      sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
      block_fatal_signals ();
    }
  actions_allocated = false;
  attrs_allocated = false;
  if ((err = posix_spawn_file_actions_init (&actions)) != 0
      || (actions_allocated = true,
          (pipe_stdin
           && (err = posix_spawn_file_actions_adddup2 (&actions,
                                                       ofd[0], STDIN_FILENO))
              != 0)
          || (pipe_stdout
              && (err = posix_spawn_file_actions_adddup2 (&actions,
                                                          ifd[1], STDOUT_FILENO))
                 != 0)
          || (pipe_stdin
              && (err = posix_spawn_file_actions_addclose (&actions, ofd[0]))
                 != 0)
          || (pipe_stdout
              && (err = posix_spawn_file_actions_addclose (&actions, ifd[1]))
                 != 0)
          || (pipe_stdin
              && (err = posix_spawn_file_actions_addclose (&actions, ofd[1]))
                 != 0)
          || (pipe_stdout
              && (err = posix_spawn_file_actions_addclose (&actions, ifd[0]))
                 != 0)
          || (null_stderr
              && (err = posix_spawn_file_actions_addopen (&actions,
                                                          STDERR_FILENO,
                                                          "/dev/null", O_RDWR,
                                                          0))
                 != 0)
          || (!pipe_stdin
              && prog_stdin != NULL
              && (err = posix_spawn_file_actions_addopen (&actions,
                                                          STDIN_FILENO,
                                                          prog_stdin, O_RDONLY,
                                                          0))
                 != 0)
          || (!pipe_stdout
              && prog_stdout != NULL
              && (err = posix_spawn_file_actions_addopen (&actions,
                                                          STDOUT_FILENO,
                                                          prog_stdout, O_WRONLY,
                                                          0))
                 != 0)
          || (slave_process
              && ((err = posix_spawnattr_init (&attrs)) != 0
                  || (attrs_allocated = true,
                      (err = posix_spawnattr_setsigmask (&attrs,
                                                         &blocked_signals))
                      != 0
                      || (err = posix_spawnattr_setflags (&attrs,
                                                        POSIX_SPAWN_SETSIGMASK))
                         != 0)))
          ))
    {
      if (actions_allocated)
        posix_spawn_file_actions_destroy (&actions);
      if (attrs_allocated)
        posix_spawnattr_destroy (&attrs);
      if (slave_process)
        unblock_fatal_signals ();
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, err,
               _("%s subprocess failed"), progname);
      if (pipe_stdout)
        {
          close (ifd[0]);
          close (ifd[1]);
        }
      if (pipe_stdin)
        {
          close (ofd[0]);
          close (ofd[1]);
        }
      errno = err;
      return -1;
    }
  posix_spawn_file_actions_destroy (&actions);
  if (attrs_allocated)
    posix_spawnattr_destroy (&attrs);
  if (slave_process)
    {
      register_slave_subprocess (child);
      unblock_fatal_signals ();
    }
  if (pipe_stdin)
    close (ofd[0]);
  if (pipe_stdout)
    close (ifd[1]);

  if (pipe_stdout)
    fd[0] = ifd[0];
  if (pipe_stdin)
    fd[1] = ofd[1];
  return child;

#endif
}
Beispiel #19
0
pid_t spawnvex(const char *path, char *const argv[], char *const envv[], Spawnvex_t *vex) {
    int i;
    int op;
    unsigned int flags = 0;
    pid_t pid;
    int arg;
    int err;
    int fd;
    posix_spawnattr_t ax;
    posix_spawn_file_actions_t fx;
    Spawnvex_t *xev = NULL;
#if _lib_spawn_mode || _lib_spawn && _mem_pgroup_inheritance
    pid_t pgid;
    int arg;
    int j;
    int k;
    int m;
    int ic;
    int jc;
    Spawnvex_t *xev;
#if !_lib_spawn_mode
    int *map;
    int a;
    struct inheritance inherit;
#endif
#endif

    if (vex && vex->debug >= 0) {
        error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\"", __LINE__, getpid(),
              vex, vex->cur, path);
    }
#if _lib_spawn_mode || _lib_spawn && _mem_pgroup_inheritance
    if (!envv) envv = environ;
    pid = -1;
    m = 0;
    if (vex) {
        vex->noexec = -1;
        vex->pgrp = -1;
        flags = vex->flags;
        if (!(xev = spawnvex_open(0))) goto bad;
        j = -1;
        for (i = 0; i < vex->cur;) {
            op = vex->op[i++].number;
            arg = vex->op[i++].number;
            if (op & 1) i += 2;
            op /= 2;
            if (op >= 0) {
                if (m < op) m = op + 1;
                if (m < arg) m = arg + 1;
            } else if (op == SPAWN_cwd)
                j = arg;
        }
        if (j >= 0) {
            if ((i = open(".", O_RDONLY)) < 0) goto bad;
            if ((i = save(i, &ic, m)) < 0) goto bad;
            if (spawnvex_add(xev, SPAWN_cwd, i, 0, 0) < 0 || restore(xev, i, -1, 0) < 0) {
                close(i);
                goto bad;
            }
            if (fchdir(j) < 0) goto bad;
            if ((i = save(j, &jc, m)) < 0) goto bad;
            if (restore(xev, i, j, jc) < 0) {
                close(i);
                goto bad;
            }
        }
    } else {
        flags = 0;
        xev = NULL;
    }
#if _lib_spawn_mode
    if (vex)
        for (i = 0; i < vex->cur;) {
            op = vex->op[i++].number;
            arg = vex->op[i++].number;
            if (op & 1) i += 2;
            switch (op /= 2) {
                case SPAWN_frame:
                    vex->frame = (unsigned int)arg;
                    break;
                case SPAWN_pgrp:
                    vex->pgrp = (pid_t)arg;
                    break;
                default:
                    if (op >= 0) {
                        if (arg < 0) {
                            if ((i = save(op, &ic, m)) < 0) {
                                if (i < -1) goto bad;
                            } else if (restore(xev, i, op, ic) < 0) {
                                close(i);
                                goto bad;
                            } else
                                close(op);
                        } else if (arg == op) {
                            if (spawnvex_add(xev, SPAWN_cloexec, arg, 0, 0) < 0) goto bad;
                            if (fcntl(arg, F_SETFD, 0) < 0) goto bad;
                        } else if ((j = save(arg, &jc, m)) < -1)
                            goto bad;
                        else {
                            close(arg);
                            if (fcntl(op, F_DUPFD, arg) >= 0) {
                                if ((i = save(op, &ic, m)) >= 0) {
                                    if (restore(xev, i, op, ic) >= 0) {
                                        close(op);
                                        if (j < 0 || restore(xev, j, arg, jc) >= 0) continue;
                                    }
                                    close(i);
                                }
                            }
                            if (j >= 0) {
                                fcntl(j, F_DUPFD, arg);
                                close(j);
                            }
                            goto bad;
                        }
                    }
                    break;
            }
        }
    pid = spawnve(vex && vex->pgrp >= 0 ? P_DETACH : P_NOWAIT, path, argv, envv);
#else
    inherit.flags = 0;
    map = 0;
    if (vex) {
        if (m) {
            map = calloc(1, m * sizeof(int));
            if (!map) goto bad;
            for (i = 0; i < m; i++) map[i] = i;
        }
        for (i = 0; i < vex->cur;) {
            op = vex->op[i++].number;
            a = i;
            arg = vex->op[i++].number;
            if (op & 1) i += 2;
            switch (op /= 2) {
                case SPAWN_noop:
                    break;
                case SPAWN_noexec:
                    break;
                case SPAWN_frame:
                    vex->frame = (unsigned int)arg;
                    break;
                case SPAWN_pgrp:
                    inherit.flags |= SPAWN_SETGROUP;
                    inherit.pgroup = arg ? arg : SPAWN_NEWPGROUP;
                    break;
                case SPAWN_sigdef:
                    inherit.flags |= SPAWN_SETSIGDEF;
                    sigemptyset(&inherit.sigdefault);
                    for (j = 1; j < 8 * sizeof(vex->op[a].number); j++)
                        if (vex->op[a].number & (1 << j)) sigaddset(&inherit.sigdefault, j);
                    break;
                case SPAWN_sigmask:
                    inherit.flags |= SPAWN_SETSIGMASK;
                    sigemptyset(&inherit.sigmask);
                    for (j = 1; j < 8 * sizeof(vex->op[a].number); j++)
                        if (vex->op[a].number & (1 << j)) sigaddset(&inherit.sigmask, j);
                    break;
                default:
                    if (op < 0) {
                        errno = EINVAL;
                        goto bad;
                    } else if (arg < 0)
                        map[op] = SPAWN_FDCLOSED;
                    else
                        map[op] = arg;
                    break;
            }
        }
    }
    pid = spawn(path, m, map, &inherit, (const char **)argv, (const char **)envv);
#endif
    if (pid >= 0 && vex) VEXINIT(vex);
bad:
    if (xev) {
        spawnvex_apply(xev, 0, SPAWN_FLUSH | SPAWN_NOCALL);
        spawnvex_close(xev);
    }
#if !_lib_spawn_mode
    if (map) free(map);
#endif
    return pid;

#else

#if _lib_spawnve
    if (!vex || !vex->cur && !vex->flags) return spawnve(path, argv, envv);
#endif
    if (vex && ((vex->set & (0
#if !_lib_posix_spawnattr_setfchdir
                             | VEXFLAG(SPAWN_cwd)
#endif
#if !_lib_posix_spawnattr_setsid
                             | VEXFLAG(SPAWN_sid)
#endif
#if !_lib_posix_spawnattr_setumask
                             | VEXFLAG(SPAWN_umask)
#endif
                                 ))
#if !_lib_posix_spawn
                || !(vex->flags & SPAWN_EXEC)
#endif
                    )) {
        int n;
        int m;
        Spawnvex_noexec_t nx;
        int msg[2];

        if (!envv) envv = environ;
        n = errno;
        if (pipe(msg) < 0) {
            msg[0] = msg[1] = -1;
        } else {
            (void)fcntl(msg[0], F_SETFD, FD_CLOEXEC);
            (void)fcntl(msg[1], F_SETFD, FD_CLOEXEC);
        }
        if (!(flags & SPAWN_FOREGROUND)) sigcritical(SIG_REG_EXEC | SIG_REG_PROC);
        pid = fork();
        if (pid == -1) {
            n = errno;
        } else if (!pid) {
            if (!(flags & SPAWN_FOREGROUND)) sigcritical(SIG_REG_POP);
            if (vex && (n = spawnvex_apply(vex, 0, SPAWN_FRAME | SPAWN_NOCALL))) {
                errno = n;
            } else {
                if (vex && vex->debug >= 0) {
                    error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\"", __LINE__,
                          getpid(), vex, vex->cur, path);
                }
                execve(path, argv, envv);
                if (vex && vex->debug >= 0) {
                    error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\" FAILED",
                          __LINE__, getpid(), vex, vex->cur, path);
                }
                if (vex && (i = vex->noexec) >= 0) {
                    nx.vex = vex;
                    nx.handle = vex->op[i + 3].handle;
                    nx.path = path;
                    nx.argv = argv;
                    nx.envv = envv;
#if _use_spawn_exec
                    /*
                     * setting SPAWN_EXEC here means that it is more efficient to
                     * exec(interpreter) on script than to fork() initialize and
                     * read script -- highly subjective, based on some ksh
                     * implementaions, and probably won't be set unless its a
                     * noticable win
                     */

                    nx.flags |= SPAWN_EXEC;
#endif
                    nx.msgfd = msg[1];
                    errno = (*vex->op[i + 2].callback)(&nx, SPAWN_noexec, errno);
                }
            }
            if (msg[1] != -1) {
                m = errno;
                write(msg[1], &m, sizeof(m));
            }
            _exit(errno == ENOENT ? EXIT_NOTFOUND : EXIT_NOEXEC);
        }
        if (msg[0] != -1) {
            close(msg[1]);
            if (pid != -1) {
                m = 0;
                while (read(msg[0], &m, sizeof(m)) == -1) {
                    if (errno != EINTR) {
                        m = errno;
                        break;
                    }
                }
                if (m) {
                    while (waitpid(pid, &n, 0) && errno == EINTR) {
                        ;
                    }
                    pid = -1;
                    n = m;
                }
            }
            close(msg[0]);
        }
        if (!(flags & SPAWN_FOREGROUND)) sigcritical(SIG_REG_POP);
        if (pid != -1 && vex) VEXINIT(vex);
        errno = n;
        return pid;
    }
    if (vex) {
        err = posix_spawnattr_init(&ax);
        if (err) goto nope;
        err = posix_spawn_file_actions_init(&fx);
        if (err) {
            posix_spawnattr_destroy(&ax);
            goto nope;
        }
        for (i = 0; i < vex->cur;) {
            op = vex->op[i++].number;
            arg = vex->op[i++].number;
            if (op & 1) i += 2;
            switch (op /= 2) {
                case SPAWN_noop:
                    break;
                case SPAWN_noexec:
                    break;
                case SPAWN_frame:
                    break;
#if _lib_posix_spawnattr_setfchdir
                case SPAWN_cwd:
                    err = posix_spawnattr_setfchdir(&ax, arg);
                    if (err) goto bad;
                    break;
#endif
                case SPAWN_pgrp:
                    err = posix_spawnattr_setpgroup(&ax, arg);
                    if (err) goto bad;
                    err = posix_spawnattr_setflags(&ax, POSIX_SPAWN_SETPGROUP);
                    if (err) goto bad;
                    break;
                case SPAWN_resetids:
                    err = posix_spawnattr_setflags(&ax, POSIX_SPAWN_RESETIDS);
                    if (err) goto bad;
                    break;
#if _lib_posix_spawnattr_setsid
                case SPAWN_sid:
                    err = posix_spawnattr_setsid(&ax, arg);
                    if (err) goto bad;
                    break;
#endif
                case SPAWN_sigdef:
                    break;
                case SPAWN_sigmask:
                    break;
#if _lib_posix_spawnattr_setumask
                case SPAWN_umask:
                    if (err = posix_spawnattr_setumask(&ax, arg)) goto bad;
                    break;
#endif
                default:
                    if (op < 0) {
                        err = EINVAL;
                        goto bad;
                    } else if (arg < 0) {
                        err = posix_spawn_file_actions_addclose(&fx, op);
                        if (err) goto bad;
                    } else if (arg == op) {
#ifdef F_DUPFD_CLOEXEC
                        if ((fd = fcntl(op, F_DUPFD_CLOEXEC, 0)) < 0)
#else
                        if ((fd = fcntl(op, F_DUPFD, 0)) < 0 ||
                            fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 && (close(fd), 1))
#endif
                        {
                            err = errno;
                            goto bad;
                        }
                        if (!xev && !(xev = spawnvex_open(0))) goto bad;
                        spawnvex_add(xev, fd, -1, 0, 0);
                        err = posix_spawn_file_actions_adddup2(&fx, fd, op);
                        if (err) goto bad;
                    } else {
                        err = posix_spawn_file_actions_adddup2(&fx, op, arg);
                        if (err) goto bad;
                    }
                    break;
            }
        }

        // Ensure stdin, stdout, stderr are open in the child process.
        // See https://github.com/att/ast/issues/1117.
        for (int fd = 0; fd < 3; ++fd) {
            errno = 0;
            if (fcntl(fd, F_GETFD, NULL) == -1 || errno == EBADF) {
                err = posix_spawn_file_actions_addopen(&fx, fd, "/dev/null", O_RDWR, 0);
                if (err) goto bad;
            }
        }

        err = posix_spawn(&pid, path, &fx, &ax, argv, envv ? envv : environ);
        if (err) goto bad;
        posix_spawnattr_destroy(&ax);
        posix_spawn_file_actions_destroy(&fx);
        if (xev) {
            spawnvex_apply(xev, 0, SPAWN_NOCALL);
            spawnvex_close(xev);
        }
        if (vex->flags & SPAWN_CLEANUP) spawnvex_apply(vex, 0, SPAWN_FRAME | SPAWN_CLEANUP);
        VEXINIT(vex);
    } else {
        err = posix_spawn(&pid, path, NULL, NULL, argv, envv ? envv : environ);
        if (err) goto nope;
    }
    if (vex && vex->debug >= 0) {
        error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\" %8d posix_spawn",
              __LINE__, getpid(), vex, vex->cur, path, pid);
    }
    return pid;
bad:
    posix_spawnattr_destroy(&ax);
    posix_spawn_file_actions_destroy(&fx);
    if (xev) {
        spawnvex_apply(xev, 0, SPAWN_NOCALL);
        spawnvex_close(xev);
    }
nope:
    errno = err;
    if (vex && vex->debug >= 0) {
        error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\" %8d posix_spawn FAILED",
              __LINE__, getpid(), vex, vex->cur, path, -1);
    }
    return -1;
#endif
}
void
ProcessExitEvent::Impl::run()
{
    if (mRunning)
    {
        CLOG(ERROR, "Process") << "ProcessExitEvent::Impl already running";
        throw std::runtime_error("ProcessExitEvent::Impl already running");
    }
    std::lock_guard<std::recursive_mutex> guard(ProcessManagerImpl::gImplsMutex);
    std::vector<std::string> args = split(mCmdLine);
    std::vector<char*> argv;
    for (auto& a : args)
    {
        argv.push_back((char*)a.data());
    }
    argv.push_back(nullptr);
    char* env[1] = {nullptr};
    int pid, err = 0;

    posix_spawn_file_actions_t fileActions;
    if (!mOutFile.empty())
    {
        err = posix_spawn_file_actions_init(&fileActions);
        if (err)
        {
            CLOG(ERROR, "Process")
                << "posix_spawn_file_actions_init() failed: " << strerror(err);
            throw std::runtime_error("posix_spawn_file_actions_init() failed");
        }
        err = posix_spawn_file_actions_addopen(&fileActions, 1, mOutFile.c_str(),
                                               O_RDWR | O_CREAT, 0600);
        if (err)
        {
            CLOG(ERROR, "Process")
                << "posix_spawn_file_actions_addopen() failed: "
                << strerror(err);
            throw std::runtime_error(
                "posix_spawn_file_actions_addopen() failed");
        }
    }

    err = posix_spawnp(&pid, argv[0], mOutFile.empty() ? nullptr : &fileActions,
                       nullptr, // posix_spawnattr_t*
                       argv.data(), env);
    if (err)
    {
        CLOG(ERROR, "Process") << "posix_spawn() failed: " << strerror(err);
        throw std::runtime_error("posix_spawn() failed");
    }

    if (!mOutFile.empty())
    {
        err = posix_spawn_file_actions_destroy(&fileActions);
        if (err)
        {
            CLOG(ERROR, "Process")
                << "posix_spawn_file_actions_destroy() failed: "
                << strerror(err);
            throw std::runtime_error(
                "posix_spawn_file_actions_destroy() failed");
        }
    }
    ProcessManagerImpl::gImpls[pid] = shared_from_this();
    mRunning = true;
}
Beispiel #21
0
int					/* O - Process ID or 0 */
cupsdStartProcess(
    const char  *command,		/* I - Full path to command */
    char        *argv[],		/* I - Command-line arguments */
    char        *envp[],		/* I - Environment */
    int         infd,			/* I - Standard input file descriptor */
    int         outfd,			/* I - Standard output file descriptor */
    int         errfd,			/* I - Standard error file descriptor */
    int         backfd,			/* I - Backchannel file descriptor */
    int         sidefd,			/* I - Sidechannel file descriptor */
    int         root,			/* I - Run as root? */
    void        *profile,		/* I - Security profile to use */
    cupsd_job_t *job,			/* I - Job associated with process */
    int         *pid)			/* O - Process ID */
{
  int		i;			/* Looping var */
  const char	*exec_path = command;	/* Command to be exec'd */
  char		*real_argv[110],	/* Real command-line arguments */
		cups_exec[1024],	/* Path to "cups-exec" program */
		user_str[16],		/* User string */
		group_str[16],		/* Group string */
		nice_str[16];		/* FilterNice string */
  uid_t		user;			/* Command UID */
  cupsd_proc_t	*proc;			/* New process record */
#if USE_POSIX_SPAWN
  posix_spawn_file_actions_t actions;	/* Spawn file actions */
  posix_spawnattr_t attrs;		/* Spawn attributes */
  sigset_t	defsignals;		/* Default signals */
#elif defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
  struct sigaction action;		/* POSIX signal handler */
#endif /* USE_POSIX_SPAWN */
#if defined(__APPLE__)
  char		processPath[1024],	/* CFProcessPath environment variable */
		linkpath[1024];		/* Link path for symlinks... */
  int		linkbytes;		/* Bytes for link path */
#endif /* __APPLE__ */


  *pid = 0;

 /*
  * Figure out the UID for the child process...
  */

  if (RunUser)
    user = RunUser;
  else if (root)
    user = 0;
  else
    user = User;

 /*
  * Check the permissions of the command we are running...
  */

  if (_cupsFileCheck(command, _CUPS_FILE_CHECK_PROGRAM, !RunUser,
                     cupsdLogFCMessage, job ? job->printer : NULL))
    return (0);

#if defined(__APPLE__)
  if (envp)
  {
   /*
    * Add special voodoo magic for OS X - this allows OS X programs to access
    * their bundle resources properly...
    */

    if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0)
    {
     /*
      * Yes, this is a symlink to the actual program, nul-terminate and
      * use it...
      */

      linkpath[linkbytes] = '\0';

      if (linkpath[0] == '/')
	snprintf(processPath, sizeof(processPath), "CFProcessPath=%s",
		 linkpath);
      else
	snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s",
		 dirname((char *)command), linkpath);
    }
    else
      snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", command);

    envp[0] = processPath;		/* Replace <CFProcessPath> string */
  }
#endif	/* __APPLE__ */

 /*
  * Use helper program when we have a sandbox profile...
  */

#if !USE_POSIX_SPAWN
  if (profile)
#endif /* !USE_POSIX_SPAWN */
  {
    snprintf(cups_exec, sizeof(cups_exec), "%s/daemon/cups-exec", ServerBin);
    snprintf(user_str, sizeof(user_str), "%d", user);
    snprintf(group_str, sizeof(group_str), "%d", Group);
    snprintf(nice_str, sizeof(nice_str), "%d", FilterNice);

    real_argv[0] = cups_exec;
    real_argv[1] = (char *)"-g";
    real_argv[2] = group_str;
    real_argv[3] = (char *)"-n";
    real_argv[4] = nice_str;
    real_argv[5] = (char *)"-u";
    real_argv[6] = user_str;
    real_argv[7] = profile ? profile : "none";
    real_argv[8] = (char *)command;

    for (i = 0;
         i < (int)(sizeof(real_argv) / sizeof(real_argv[0]) - 10) && argv[i];
	 i ++)
      real_argv[i + 9] = argv[i];

    real_argv[i + 9] = NULL;

    argv      = real_argv;
    exec_path = cups_exec;
  }

  if (LogLevel == CUPSD_LOG_DEBUG2)
  {
    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Preparing to start \"%s\", arguments:", command);

    for (i = 0; argv[i]; i ++)
      cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: argv[%d] = \"%s\"", i, argv[i]);
  }

#if USE_POSIX_SPAWN
 /*
  * Setup attributes and file actions for the spawn...
  */

  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting spawn attributes.");
  sigemptyset(&defsignals);
  sigaddset(&defsignals, SIGTERM);
  sigaddset(&defsignals, SIGCHLD);
  sigaddset(&defsignals, SIGPIPE);

  posix_spawnattr_init(&attrs);
  posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF);
  posix_spawnattr_setpgroup(&attrs, 0);
  posix_spawnattr_setsigdefault(&attrs, &defsignals);

  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting file actions.");
  posix_spawn_file_actions_init(&actions);
  if (infd != 0)
  {
    if (infd < 0)
      posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDONLY, 0);
    else
      posix_spawn_file_actions_adddup2(&actions, infd, 0);
  }

  if (outfd != 1)
  {
    if (outfd < 0)
      posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0);
    else
      posix_spawn_file_actions_adddup2(&actions, outfd, 1);
  }

  if (errfd != 2)
  {
    if (errfd < 0)
      posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0);
    else
      posix_spawn_file_actions_adddup2(&actions, errfd, 2);
  }

  if (backfd != 3 && backfd >= 0)
    posix_spawn_file_actions_adddup2(&actions, backfd, 3);

  if (sidefd != 4 && sidefd >= 0)
    posix_spawn_file_actions_adddup2(&actions, sidefd, 4);

  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Calling posix_spawn.");

  if (posix_spawn(pid, exec_path, &actions, &attrs, argv, envp ? envp : environ))
  {
    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno));

    *pid = 0;
  }
  else
    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: pid=%d", (int)*pid);

  posix_spawn_file_actions_destroy(&actions);
  posix_spawnattr_destroy(&attrs);

#else
 /*
  * Block signals before forking...
  */

  cupsdHoldSignals();

  if ((*pid = fork()) == 0)
  {
   /*
    * Child process goes here; update stderr as needed...
    */

    if (errfd != 2)
    {
      if (errfd < 0)
        errfd = open("/dev/null", O_WRONLY);

      if (errfd != 2)
      {
        dup2(errfd, 2);
	close(errfd);
      }
    }

   /*
    * Put this process in its own process group so that we can kill any child
    * processes it creates.
    */

#  ifdef HAVE_SETPGID
    if (!RunUser && setpgid(0, 0))
      exit(errno + 100);
#  else
    if (!RunUser && setpgrp())
      exit(errno + 100);
#  endif /* HAVE_SETPGID */

   /*
    * Update the remaining file descriptors as needed...
    */

    if (infd != 0)
    {
      if (infd < 0)
        infd = open("/dev/null", O_RDONLY);

      if (infd != 0)
      {
        dup2(infd, 0);
	close(infd);
      }
    }

    if (outfd != 1)
    {
      if (outfd < 0)
        outfd = open("/dev/null", O_WRONLY);

      if (outfd != 1)
      {
        dup2(outfd, 1);
	close(outfd);
      }
    }

    if (backfd != 3 && backfd >= 0)
    {
      dup2(backfd, 3);
      close(backfd);
      fcntl(3, F_SETFL, O_NDELAY);
    }

    if (sidefd != 4 && sidefd >= 0)
    {
      dup2(sidefd, 4);
      close(sidefd);
      fcntl(4, F_SETFL, O_NDELAY);
    }

   /*
    * Change the priority of the process based on the FilterNice setting.
    * (this is not done for root processes...)
    */

    if (!root)
      nice(FilterNice);

   /*
    * Reset group membership to just the main one we belong to.
    */

    if (!RunUser && setgid(Group))
      exit(errno + 100);

    if (!RunUser && setgroups(1, &Group))
      exit(errno + 100);

   /*
    * Change user to something "safe"...
    */

    if (!RunUser && user && setuid(user))
      exit(errno + 100);

   /*
    * Change umask to restrict permissions on created files...
    */

    umask(077);

   /*
    * Unblock signals before doing the exec...
    */

#  ifdef HAVE_SIGSET
    sigset(SIGTERM, SIG_DFL);
    sigset(SIGCHLD, SIG_DFL);
    sigset(SIGPIPE, SIG_DFL);
#  elif defined(HAVE_SIGACTION)
    memset(&action, 0, sizeof(action));

    sigemptyset(&action.sa_mask);
    action.sa_handler = SIG_DFL;

    sigaction(SIGTERM, &action, NULL);
    sigaction(SIGCHLD, &action, NULL);
    sigaction(SIGPIPE, &action, NULL);
#  else
    signal(SIGTERM, SIG_DFL);
    signal(SIGCHLD, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);
#  endif /* HAVE_SIGSET */

    cupsdReleaseSignals();

   /*
    * Execute the command; if for some reason this doesn't work, log an error
    * exit with a non-zero value...
    */

    if (envp)
      execve(exec_path, argv, envp);
    else
      execv(exec_path, argv);

    exit(errno + 100);
  }
  else if (*pid < 0)
  {
   /*
    * Error - couldn't fork a new process!
    */

    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command,
                    strerror(errno));

    *pid = 0;
  }

  cupsdReleaseSignals();
#endif /* USE_POSIX_SPAWN */

  if (*pid)
  {
    if (!process_array)
      process_array = cupsArrayNew((cups_array_func_t)compare_procs, NULL);

    if (process_array)
    {
      if ((proc = calloc(1, sizeof(cupsd_proc_t) + strlen(command))) != NULL)
      {
        proc->pid    = *pid;
	proc->job_id = job ? job->id : 0;
	_cups_strcpy(proc->name, command);

	cupsArrayAdd(process_array, proc);
      }
    }
  }

  cupsdLogMessage(CUPSD_LOG_DEBUG2,
		  "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, "
		  "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, "
		  "profile=%p, job=%p(%d), pid=%p) = %d",
		  command, argv, envp, infd, outfd, errfd, backfd, sidefd,
		  root, profile, job, job ? job->id : 0, pid, *pid);

  return (*pid);
}
Beispiel #22
0
static void handle_redirection(char *path, int flag, posix_spawn_file_actions_t *fileActions, int fd) {
	int mode = S_IRUSR | S_IWUSR;
	posix_spawn_file_actions_addopen (fileActions, fd, path, flag, mode);
}
Beispiel #23
0
/* Execute a command, optionally redirecting any of the three standard file
   descriptors to /dev/null.  Return its exit code.
   If it didn't terminate correctly, exit if exit_on_error is true, otherwise
   return 127.
   If slave_process is true, the child process will be terminated when its
   creator receives a catchable fatal signal.  */
int
execute (const char *progname,
         const char *prog_path, char **prog_argv,
         bool ignore_sigpipe,
         bool null_stdin, bool null_stdout, bool null_stderr,
         bool slave_process, bool exit_on_error,
         int *termsigp)
{
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__

  /* Native Windows API.  */
  int orig_stdin;
  int orig_stdout;
  int orig_stderr;
  int exitcode;
  int nullinfd;
  int nulloutfd;

  /* FIXME: Need to free memory allocated by prepare_spawn.  */
  prog_argv = prepare_spawn (prog_argv);

  /* Save standard file handles of parent process.  */
  if (null_stdin)
    orig_stdin = dup_safer_noinherit (STDIN_FILENO);
  if (null_stdout)
    orig_stdout = dup_safer_noinherit (STDOUT_FILENO);
  if (null_stderr)
    orig_stderr = dup_safer_noinherit (STDERR_FILENO);
  exitcode = -1;

  /* Create standard file handles of child process.  */
  nullinfd = -1;
  nulloutfd = -1;
  if ((!null_stdin
       || ((nullinfd = open ("NUL", O_RDONLY, 0)) >= 0
           && (nullinfd == STDIN_FILENO
               || (dup2 (nullinfd, STDIN_FILENO) >= 0
                   && close (nullinfd) >= 0))))
      && (!(null_stdout || null_stderr)
          || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
              && (!null_stdout
                  || nulloutfd == STDOUT_FILENO
                  || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
              && (!null_stderr
                  || nulloutfd == STDERR_FILENO
                  || dup2 (nulloutfd, STDERR_FILENO) >= 0)
              && ((null_stdout && nulloutfd == STDOUT_FILENO)
                  || (null_stderr && nulloutfd == STDERR_FILENO)
                  || close (nulloutfd) >= 0))))
    /* Use spawnvpe and pass the environment explicitly.  This is needed if
       the program has modified the environment using putenv() or [un]setenv().
       On Windows, programs have two environments, one in the "environment
       block" of the process and managed through SetEnvironmentVariable(), and
       one inside the process, in the location retrieved by the 'environ'
       macro.  When using spawnvp() without 'e', the child process inherits a
       copy of the environment block - ignoring the effects of putenv() and
       [un]setenv().  */
    {
      exitcode = spawnvpe (P_WAIT, prog_path, (const char **) prog_argv,
                           (const char **) environ);
      if (exitcode < 0 && errno == ENOEXEC)
        {
          /* prog is not a native executable.  Try to execute it as a
             shell script.  Note that prepare_spawn() has already prepended
             a hidden element "sh.exe" to prog_argv.  */
          --prog_argv;
          exitcode = spawnvpe (P_WAIT, prog_argv[0], (const char **) prog_argv,
                               (const char **) environ);
        }
    }
  if (nulloutfd >= 0)
    close (nulloutfd);
  if (nullinfd >= 0)
    close (nullinfd);

  /* Restore standard file handles of parent process.  */
  if (null_stderr)
    undup_safer_noinherit (orig_stderr, STDERR_FILENO);
  if (null_stdout)
    undup_safer_noinherit (orig_stdout, STDOUT_FILENO);
  if (null_stdin)
    undup_safer_noinherit (orig_stdin, STDIN_FILENO);

  if (termsigp != NULL)
    *termsigp = 0;

  if (exitcode == -1)
    {
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, errno,
               _("%s subprocess failed"), progname);
      return 127;
    }

  return exitcode;

#else

  /* Unix API.  */
  /* Note about 127: Some errors during posix_spawnp() cause the function
     posix_spawnp() to return an error code; some other errors cause the
     subprocess to exit with return code 127.  It is implementation
     dependent which error is reported which way.  We treat both cases as
     equivalent.  */
  sigset_t blocked_signals;
  posix_spawn_file_actions_t actions;
  bool actions_allocated;
  posix_spawnattr_t attrs;
  bool attrs_allocated;
  int err;
  pid_t child;

  if (slave_process)
    {
      sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
      block_fatal_signals ();
    }
  actions_allocated = false;
  attrs_allocated = false;
  if ((err = posix_spawn_file_actions_init (&actions)) != 0
      || (actions_allocated = true,
          (null_stdin
            && (err = posix_spawn_file_actions_addopen (&actions,
                                                        STDIN_FILENO,
                                                        "/dev/null", O_RDONLY,
                                                        0))
               != 0)
          || (null_stdout
              && (err = posix_spawn_file_actions_addopen (&actions,
                                                          STDOUT_FILENO,
                                                          "/dev/null", O_RDWR,
                                                          0))
                 != 0)
          || (null_stderr
              && (err = posix_spawn_file_actions_addopen (&actions,
                                                          STDERR_FILENO,
                                                          "/dev/null", O_RDWR,
                                                          0))
                 != 0)
          || (slave_process
              && ((err = posix_spawnattr_init (&attrs)) != 0
                  || (attrs_allocated = true,
                      (err = posix_spawnattr_setsigmask (&attrs,
                                                         &blocked_signals))
                      != 0
                      || (err = posix_spawnattr_setflags (&attrs,
                                                        POSIX_SPAWN_SETSIGMASK))
                         != 0)))
          || (err = posix_spawnp (&child, prog_path, &actions,
                                  attrs_allocated ? &attrs : NULL, prog_argv,
                                  environ))
             != 0))
    {
      if (actions_allocated)
        posix_spawn_file_actions_destroy (&actions);
      if (attrs_allocated)
        posix_spawnattr_destroy (&attrs);
      if (slave_process)
        unblock_fatal_signals ();
      if (termsigp != NULL)
        *termsigp = 0;
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, err,
               _("%s subprocess failed"), progname);
      return 127;
    }
  posix_spawn_file_actions_destroy (&actions);
  if (attrs_allocated)
    posix_spawnattr_destroy (&attrs);
  if (slave_process)
    {
      register_slave_subprocess (child);
      unblock_fatal_signals ();
    }

  return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
                          slave_process, exit_on_error, termsigp);

#endif
}
Beispiel #24
0
void PerfLogThread::loop() {
  json_ref samples;
  char **envp;
  json_ref perf_cmd;
  int64_t sample_batch;

  w_set_thread_name("perflog");

  // Prep some things that we'll need each time we run a command
  {
    uint32_t env_size;
    auto envpht = w_envp_make_ht();
    char *statedir = dirname(strdup(watchman_state_file));
    w_envp_set_cstring(envpht, "WATCHMAN_STATE_DIR", statedir);
    w_envp_set_cstring(envpht, "WATCHMAN_SOCK", get_sock_name());
    envp = w_envp_make_from_ht(envpht, &env_size);
  }

  perf_cmd = cfg_get_json("perf_logger_command");
  if (json_is_string(perf_cmd)) {
    perf_cmd = json_array({perf_cmd});
  }
  if (!json_is_array(perf_cmd)) {
    w_log(
        W_LOG_FATAL,
        "perf_logger_command must be either a string or an array of strings\n");
  }

  sample_batch = cfg_get_int("perf_logger_command_max_samples_per_call", 4);

  while (!w_is_stopping()) {
    {
      auto wlock = samples_.wlock();
      if (!*wlock) {
        cond_.wait(wlock.getUniqueLock());
      }

      samples = nullptr;
      std::swap(samples, *wlock);
    }

    if (samples) {
      while (json_array_size(samples) > 0) {
        int i = 0;
        auto cmd = json_array();
        posix_spawnattr_t attr;
        posix_spawn_file_actions_t actions;
        pid_t pid;
        char **argv = NULL;

        json_array_extend(cmd, perf_cmd);

        while (i < sample_batch && json_array_size(samples) > 0) {
          char *stringy = json_dumps(json_array_get(samples, 0), 0);
          if (stringy) {
            json_array_append_new(
                cmd, typed_string_to_json(stringy, W_STRING_MIXED));
            free(stringy);
          }
          json_array_remove(samples, 0);
          i++;
        }

        argv = w_argv_copy_from_json(cmd, 0);
        if (!argv) {
          char *dumped = json_dumps(cmd, 0);
          w_log(W_LOG_FATAL, "error converting %s to an argv array\n", dumped);
        }

        posix_spawnattr_init(&attr);
#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
        posix_spawnattr_setflags(&attr, POSIX_SPAWN_CLOEXEC_DEFAULT);
#endif
        posix_spawn_file_actions_init(&actions);
        posix_spawn_file_actions_addopen(&actions, STDIN_FILENO, "/dev/null",
                                         O_RDONLY, 0666);
        posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, "/dev/null",
                                         O_WRONLY, 0666);
        posix_spawn_file_actions_addopen(&actions, STDERR_FILENO, "/dev/null",
                                         O_WRONLY, 0666);

        if (posix_spawnp(&pid, argv[0], &actions, &attr, argv, envp) == 0) {
          int status;
          while (waitpid(pid, &status, 0) != pid) {
            if (errno != EINTR) {
              break;
            }
          }
        } else {
          int err = errno;
          w_log(W_LOG_ERR, "failed to spawn %s: %s\n", argv[0],
                strerror(err));
        }

        posix_spawnattr_destroy(&attr);
        posix_spawn_file_actions_destroy(&actions);

        free(argv);
      }
    }
  }
}
Beispiel #25
0
static void *perf_log_thread(void *unused) {
  json_t *samples = NULL;
  char **envp;
  json_t *perf_cmd;
  int64_t sample_batch;

  unused_parameter(unused);

  w_set_thread_name("perflog");

  // Prep some things that we'll need each time we run a command
  {
    uint32_t env_size;
    w_ht_t *envpht = w_envp_make_ht();
    char *statedir = dirname(strdup(watchman_state_file));
    w_envp_set_cstring(envpht, "WATCHMAN_STATE_DIR", statedir);
    w_envp_set_cstring(envpht, "WATCHMAN_SOCK", get_sock_name());
    envp = w_envp_make_from_ht(envpht, &env_size);
  }

  perf_cmd = cfg_get_json(NULL, "perf_logger_command");
  if (json_is_string(perf_cmd)) {
    perf_cmd = json_pack("[O]", perf_cmd);
  }
  if (!json_is_array(perf_cmd)) {
    w_log(
        W_LOG_FATAL,
        "perf_logger_command must be either a string or an array of strings\n");
  }

  sample_batch =
      cfg_get_int(NULL, "perf_logger_command_max_samples_per_call", 4);

  while (true) {
    pthread_mutex_lock(&perf_log_lock);
    if (!perf_log_samples) {
      pthread_cond_wait(&perf_log_cond, &perf_log_lock);
    }
    samples = perf_log_samples;
    perf_log_samples = NULL;

    pthread_mutex_unlock(&perf_log_lock);

    if (samples) {
      while (json_array_size(samples) > 0) {
        int i = 0;
        json_t *cmd = json_array();
        posix_spawnattr_t attr;
        posix_spawn_file_actions_t actions;
        pid_t pid;
        char **argv = NULL;

        json_array_extend(cmd, perf_cmd);

        while (i < sample_batch && json_array_size(samples) > 0) {
          char *stringy = json_dumps(json_array_get(samples, 0), 0);
          json_array_append(cmd, typed_string_to_json(stringy, W_STRING_MIXED));
          free(stringy);
          json_array_remove(samples, 0);
          i++;
        }

        argv = w_argv_copy_from_json(cmd, 0);
        if (!argv) {
          char *dumped = json_dumps(cmd, 0);
          w_log(W_LOG_FATAL, "error converting %s to an argv array\n", dumped);
        }

        posix_spawnattr_init(&attr);
#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
        posix_spawnattr_setflags(&attr, POSIX_SPAWN_CLOEXEC_DEFAULT);
#endif
        posix_spawn_file_actions_init(&actions);
        posix_spawn_file_actions_addopen(&actions, STDIN_FILENO, "/dev/null",
                                         O_RDONLY, 0666);
        posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, "/dev/null",
                                         O_WRONLY, 0666);
        posix_spawn_file_actions_addopen(&actions, STDERR_FILENO, "/dev/null",
                                         O_WRONLY, 0666);

        if (posix_spawnp(&pid, argv[0], &actions, &attr, argv, envp) == 0) {
          // There's no sense waiting here, because w_reap_children is called
          // by the reaper thread.
        } else {
          int err = errno;
          w_log(W_LOG_ERR, "failed to spawn %s: %s\n", argv[0],
                strerror(err));
        }

        posix_spawnattr_destroy(&attr);
        posix_spawn_file_actions_destroy(&actions);

        free(argv);
        json_decref(cmd);
      }
      json_decref(samples);
    }
  }

  return NULL;
}
Beispiel #26
0
static int l_posix_spawn_file_actions_addopen(lua_State *L) {
	int r;
	posix_spawn_file_actions_t *file_actions = luaL_checkudata(L, 1, "posix_spawn_file_actions_t");
	int fd = luaL_checkinteger(L, 2);
	const char *path = luaL_checkstring(L, 3);
	int oflag;
	mode_t mode;
	switch (lua_type(L, 4)) {
	case LUA_TNUMBER:
		oflag = lua_tointeger(L, 4);
		break;
	case LUA_TTABLE:
		oflag = 0;

		lua_getfield(L, 4, "rdwr");
		if (lua_toboolean(L, -1)) oflag |= O_RDWR;
		lua_getfield(L, 4, "wronly");
		if (lua_toboolean(L, -1)) {
			luaL_argcheck(L, !oflag, 4, "must specify one of rdonly, wronly or rdwr");
			oflag |= O_WRONLY;
		}
		lua_getfield(L, 4, "rdonly");
		if (lua_toboolean(L, -1)) {
			luaL_argcheck(L, !oflag, 4, "must specify one of rdonly, wronly or rdwr");
			oflag |= O_RDONLY;
		} else if(!oflag) {
			return luaL_argerror(L, 4, "must specify one of rdonly, wronly or rdwr");
		}
		lua_pop(L, 3);

		/* defined in posix */
		lua_getfield(L, 4, "append");
		if (lua_toboolean(L, -1)) oflag |= O_APPEND;
		lua_getfield(L, 4, "creat");
		if (lua_toboolean(L, -1)) oflag |= O_CREAT;
		lua_getfield(L, 4, "excl");
		if (lua_toboolean(L, -1)) oflag |= O_EXCL;
		lua_getfield(L, 4, "noctty");
		if (lua_toboolean(L, -1)) oflag |= O_NOCTTY;
		lua_getfield(L, 4, "nonblock");
		if (lua_toboolean(L, -1)) oflag |= O_NONBLOCK;
		lua_getfield(L, 4, "trunc");
		if (lua_toboolean(L, -1)) oflag |= O_TRUNC;
		lua_pop(L, 6);

		/* defined in posix, but optional */
#ifdef O_SYNC
		lua_getfield(L, 4, "sync");
		if (lua_toboolean(L, -1)) oflag |= O_SYNC;
		lua_pop(L, 1);
#endif
#ifdef O_DSYNC
		lua_getfield(L, 4, "dsync");
		if (lua_toboolean(L, -1)) oflag |= O_DSYNC;
		lua_pop(L, 1);
#endif
#ifdef O_RSYNC
		lua_getfield(L, 4, "rsync");
		if (lua_toboolean(L, -1)) oflag |= O_RSYNC;
		lua_pop(L, 1);
#endif

#ifdef O_ASYNC
		lua_getfield(L, 4, "async");
		if (lua_toboolean(L, -1)) oflag |= O_ASYNC;
		lua_pop(L, 1);
#endif
#ifdef O_CLOEXEC
		lua_getfield(L, 4, "cloexec");
		if (lua_toboolean(L, -1)) oflag |= O_CLOEXEC;
		lua_pop(L, 1);
#endif
#ifdef O_DIRECT
		lua_getfield(L, 4, "direct");
		if (lua_toboolean(L, -1)) oflag |= O_DIRECT;
		lua_pop(L, 1);
#endif
#ifdef O_DIRECTORY
		lua_getfield(L, 4, "directory");
		if (lua_toboolean(L, -1)) oflag |= O_DIRECTORY;
		lua_pop(L, 1);
#endif
#ifdef O_LARGEFILE
		lua_getfield(L, 4, "largefile");
		if (lua_toboolean(L, -1)) oflag |= O_LARGEFILE;
		lua_pop(L, 1);
#endif
#ifdef O_NDELAY
		lua_getfield(L, 4, "ndelay");
		if (lua_toboolean(L, -1)) oflag |= O_NDELAY;
		lua_pop(L, 1);
#endif
#ifdef O_NOATIME
		lua_getfield(L, 4, "noatime");
		if (lua_toboolean(L, -1)) oflag |= O_NOATIME;
		lua_pop(L, 1);
#endif
#ifdef O_NOFOLLOW
		lua_getfield(L, 4, "nofollow");
		if (lua_toboolean(L, -1)) oflag |= O_NOFOLLOW;
		lua_pop(L, 1);
#endif
#ifdef O_PATH
		lua_getfield(L, 4, "path");
		if (lua_toboolean(L, -1)) oflag |= O_PATH;
		lua_pop(L, 1);
#endif
#ifdef O_TMPFILE
		lua_getfield(L, 4, "tmpfile");
		if (lua_toboolean(L, -1)) oflag |= O_TMPFILE;
		lua_pop(L, 1);
#endif
		break;
	default:
		return luaL_argerror(L, 4, "expected integer or set of options");
	}
	mode = luaL_optinteger(L, 5, 0);
	if (0 != (r = posix_spawn_file_actions_addopen(file_actions, fd, path, oflag, mode))) {
		lua_pushnil(L);
		lua_pushstring(L, strerror(r));
		lua_pushinteger(L, r);
		return 3;
	}
	lua_pushboolean(L, 1);
	return 1;
}
void ObjectFileParser::load_sections() {
#ifdef OFPreadprint
    cout << "Opening " << filename << endl;
#endif
    auto archive_match = filename.find(".a(");
    if (archive_match != string::npos) {
        //archive: /path/to/archive.a(filename.o)
        string archive_filename = filename.substr(0, archive_match+2);
        string file_filename    = filename.substr(archive_match+3, filename.length() -(archive_match+3) -1);

        char templ[] = "/tmp/archived_file_XXXXXX.o";
        auto tmpfd = mkstemps(templ, 2);
        close(tmpfd);
        string tmpfile = string(templ);
        tmpfiles.push_back(tmpfile);
        {
            pid_t pid;
            posix_spawn_file_actions_t act;
            posix_spawn_file_actions_init(&act);
            posix_spawn_file_actions_addopen(&act, 1 /*stdout*/, templ, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
            const char* const args[] = {"ar", "p", archive_filename.c_str(), file_filename.c_str(), NULL};
            // posix_spawnp(&pid, "echo", NULL, NULL, (char* const*)args, environ);
            if (0 != posix_spawnp(&pid, "ar", &act, NULL, (char* const*)args, environ))  {
                perror("Can't start ar");
                cerr << "Can't extract file " << filename << " tmpfile: " << tmpfile << endl;
                exit(15);
            }
            int status;
            if (-1 == waitpid(pid, &status, 0)) {
                perror("waitpid failed");
                exit(16);
            }
            if (WEXITSTATUS(status) != 0) {
                cerr << "ar failed. Can't extract file " << filename << " tmpfile: " << tmpfile
                << " exc: " << WEXITSTATUS(status) << endl;
                exit(17);
            }
        }

        if (!reader->load(tmpfile)) {
            cerr << "Can't find or process ELF file " << filename << " tmpfile: " << tmpfile << endl;
            exit(12);
        }
    } else if (filename.find("linker") != string::npos) {
        // linker stubs
        // ignore for now?
    } else {
        // Regular file
        if (!reader->load(filename)) {
            cerr << "Can't find or process ELF file " << filename << endl;
            exit(11);
        }
    }

    // Sections
    for (auto &psec : reader->sections) {
        if (psec->get_name().substr(0, 3) == ".sr") continue;
        _sections.emplace(psec->get_index(), Section(psec->get_name(), psec->get_address(), psec->get_size(),
                                                     /*exec?*/ 0 != (psec->get_flags() & SHF_EXECINSTR),
                                                     /*alloc?*/ 0 != (psec->get_flags() & SHF_ALLOC)));
        _section_id_by_name.emplace(psec->get_name(), psec->get_index());
    }
}
Beispiel #28
0
static void spawn_command(w_root_t *root,
  struct watchman_trigger_command *cmd,
  w_query_res *res,
  struct w_clockspec *since_spec)
{
  char **envp = NULL;
  uint32_t i = 0;
  int ret;
  int stdin_fd = -1;
  json_t *args;
  char **argv = NULL;
  uint32_t env_size;
  posix_spawn_file_actions_t actions;
  posix_spawnattr_t attr;
  sigset_t mask;
  long arg_max;
  uint32_t argspace_remaining;
  bool file_overflow = false;
  int result_log_level;
  char clockbuf[128];
  const char *cwd = NULL;

  arg_max = sysconf(_SC_ARG_MAX);

  if (arg_max <= 0) {
    argspace_remaining = UINT_MAX;
  } else {
    argspace_remaining = (uint32_t)arg_max;
  }

  // Allow some misc working overhead
  argspace_remaining -= 32;

  stdin_fd = prepare_stdin(cmd, res);

  // Assumption: that only one thread will be executing on a given
  // cmd instance so that mutation of cmd->envht is safe.
  // This is guaranteed in the current architecture.

  if (cmd->max_files_stdin > 0 && res->num_results > cmd->max_files_stdin) {
    file_overflow = true;
  }

  // It is way too much of a hassle to try to recreate the clock value if it's
  // not a relative clock spec, and it's only going to happen on the first run
  // anyway, so just skip doing that entirely.
  if (since_spec && since_spec->tag == w_cs_clock &&
      clock_id_string(since_spec->clock.root_number, since_spec->clock.ticks,
                      clockbuf, sizeof(clockbuf))) {
    w_envp_set_cstring(cmd->envht, "WATCHMAN_SINCE", clockbuf);
  } else {
    w_envp_unset(cmd->envht, "WATCHMAN_SINCE");
  }

  if (clock_id_string(res->root_number, res->ticks,
        clockbuf, sizeof(clockbuf))) {
    w_envp_set_cstring(cmd->envht, "WATCHMAN_CLOCK", clockbuf);
  } else {
    w_envp_unset(cmd->envht, "WATCHMAN_CLOCK");
  }

  if (cmd->query->relative_root) {
    w_envp_set(cmd->envht, "WATCHMAN_RELATIVE_ROOT", cmd->query->relative_root);
  } else {
    w_envp_unset(cmd->envht, "WATCHMAN_RELATIVE_ROOT");
  }

  // Compute args
  args = json_deep_copy(cmd->command);

  if (cmd->append_files) {
    // Measure how much space the base args take up
    for (i = 0; i < json_array_size(args); i++) {
      const char *ele = json_string_value(json_array_get(args, i));

      argspace_remaining -= strlen(ele) + 1 + sizeof(char*);
    }

    // Dry run with env to compute space
    envp = w_envp_make_from_ht(cmd->envht, &env_size);
    free(envp);
    envp = NULL;
    argspace_remaining -= env_size;

    for (i = 0; i < res->num_results; i++) {
      // also: NUL terminator and entry in argv
      uint32_t size = res->results[i].relname->len + 1 + sizeof(char*);

      if (argspace_remaining < size) {
        file_overflow = true;
        break;
      }
      argspace_remaining -= size;

      json_array_append_new(
        args,
        json_string_nocheck(res->results[i].relname->buf)
      );
    }
  }

  argv = w_argv_copy_from_json(args, 0);
  json_decref(args);
  args = NULL;

  w_envp_set_bool(cmd->envht, "WATCHMAN_FILES_OVERFLOW", file_overflow);

  envp = w_envp_make_from_ht(cmd->envht, &env_size);

  posix_spawnattr_init(&attr);
  sigemptyset(&mask);
  posix_spawnattr_setsigmask(&attr, &mask);
  posix_spawnattr_setflags(&attr,
      POSIX_SPAWN_SETSIGMASK|
#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
      // Darwin: close everything except what we put in file actions
      POSIX_SPAWN_CLOEXEC_DEFAULT|
#endif
      POSIX_SPAWN_SETPGROUP);

  posix_spawn_file_actions_init(&actions);

  posix_spawn_file_actions_adddup2(&actions, stdin_fd, STDIN_FILENO);
  if (cmd->stdout_name) {
    posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO,
        cmd->stdout_name, cmd->stdout_flags, 0666);
  } else {
    posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO, STDOUT_FILENO);
  }

  if (cmd->stderr_name) {
    posix_spawn_file_actions_addopen(&actions, STDERR_FILENO,
        cmd->stderr_name, cmd->stderr_flags, 0666);
  } else {
    posix_spawn_file_actions_adddup2(&actions, STDERR_FILENO, STDERR_FILENO);
  }

  pthread_mutex_lock(&spawn_lock);
  if (cmd->query->relative_root) {
    ignore_result(chdir(cmd->query->relative_root->buf));
  } else {
    ignore_result(chdir(root->root_path->buf));
  }

  json_unpack(cmd->definition, "{s:s}", "chdir", &cwd);
  if (cwd) {
    ignore_result(chdir(cwd));
  }

  ret = posix_spawnp(&cmd->current_proc, argv[0], &actions, &attr, argv, envp);
  if (ret == 0) {
    w_root_addref(root);
    insert_running_pid(cmd->current_proc, root);
  } else {
    // On Darwin (at least), posix_spawn can fail but will still populate the
    // pid.  Since we use the pid to gate future spawns, we need to ensure
    // that we clear out the pid on failure, otherwise the trigger would be
    // effectively disabled for the rest of the watch lifetime
    cmd->current_proc = 0;
  }
  ignore_result(chdir("/"));
  pthread_mutex_unlock(&spawn_lock);

  // If failed, we want to make sure we log enough info to figure out why
  result_log_level = res == 0 ? W_LOG_DBG : W_LOG_ERR;

  w_log(result_log_level, "posix_spawnp:\n");
  for (i = 0; argv[i]; i++) {
    w_log(result_log_level, "argv[%d] %s\n", i, argv[i]);
  }
  for (i = 0; envp[i]; i++) {
    w_log(result_log_level, "envp[%d] %s\n", i, envp[i]);
  }

  w_log(result_log_level, "trigger %.*s:%s pid=%d ret=%d %s\n",
      (int)root->root_path->len,
      root->root_path->buf,
      cmd->triggername->buf, (int)cmd->current_proc, ret, strerror(ret));

  free(argv);
  free(envp);

  posix_spawnattr_destroy(&attr);
  posix_spawn_file_actions_destroy(&actions);

  if (stdin_fd != -1) {
    close(stdin_fd);
  }
}
Beispiel #29
0
bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p, const io_chain_t &io_chain)
{
    /* Initialize the output */
    if (posix_spawnattr_init(attr) != 0)
    {
        return false;
    }

    if (posix_spawn_file_actions_init(actions) != 0)
    {
        posix_spawnattr_destroy(attr);
        return false;
    }

    bool should_set_parent_group_id = false;
    int desired_parent_group_id = 0;
    if (job_get_flag(j, JOB_CONTROL))
    {
        should_set_parent_group_id = true;

        // PCA: I'm quite fuzzy on process groups,
        // but I believe that the default value of 0
        // means that the process becomes its own
        // group leader, which is what set_child_group did
        // in this case. So we want this to be 0 if j->pgid is 0.
        desired_parent_group_id = j->pgid;
    }

    /* Set the handling for job control signals back to the default.  */
    bool reset_signal_handlers = true;

    /* Remove all signal blocks */
    bool reset_sigmask = true;

    /* Set our flags */
    short flags = 0;
    if (reset_signal_handlers)
        flags |= POSIX_SPAWN_SETSIGDEF;
    if (reset_sigmask)
        flags |= POSIX_SPAWN_SETSIGMASK;
    if (should_set_parent_group_id)
        flags |= POSIX_SPAWN_SETPGROUP;

    int err = 0;
    if (! err)
        err = posix_spawnattr_setflags(attr, flags);

    if (! err && should_set_parent_group_id)
        err = posix_spawnattr_setpgroup(attr, desired_parent_group_id);

    /* Everybody gets default handlers */
    if (! err && reset_signal_handlers)
    {
        sigset_t sigdefault;
        get_signals_with_handlers(&sigdefault);
        err = posix_spawnattr_setsigdefault(attr, &sigdefault);
    }

    /* No signals blocked */
    sigset_t sigmask;
    sigemptyset(&sigmask);
    if (! err && reset_sigmask)
        err = posix_spawnattr_setsigmask(attr, &sigmask);
    
    for (size_t idx = 0; idx < io_chain.size(); idx++)
    {
        const shared_ptr<const io_data_t> io = io_chain.at(idx);

        if (io->io_mode == IO_FD)
        {
            CAST_INIT(const io_fd_t *, io_fd, io.get());
            if (io->fd == io_fd->old_fd)
                continue;
        }

        switch (io->io_mode)
        {
            case IO_CLOSE:
            {
                if (! err)
                    err = posix_spawn_file_actions_addclose(actions, io->fd);
                break;
            }

            case IO_FILE:
            {
                CAST_INIT(const io_file_t *, io_file, io.get());
                if (! err)
                    err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, io_file->flags /* mode */, OPEN_MASK);
                break;
            }

            case IO_FD:
            {
                CAST_INIT(const io_fd_t *, io_fd, io.get());
                if (! err)
                    err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, io->fd /* to */);
                break;
            }

            case IO_BUFFER:
            case IO_PIPE:
            {
                CAST_INIT(const io_pipe_t *, io_pipe, io.get());
                unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1);
                int from_fd = io_pipe->pipe_fd[write_pipe_idx];
                int to_fd = io->fd;
                if (! err)
                    err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd);


                if (write_pipe_idx > 0)
                {
                    if (! err)
                        err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]);
                    if (! err)
                        err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[1]);
                }
                else
                {
                    if (! err)
                        err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]);

                }
                break;
            }
        }
    }
/* Open a pipe for input from a child process.
 * The child's stdin comes from a file.
 *
 *           read        system                write
 *    parent  <-   fd[0]   <-   STDOUT_FILENO   <-   child
 *
 */
pid_t
create_pipe_in (const char *progname,
		const char *prog_path, char **prog_argv,
		const char *prog_stdin, bool null_stderr,
		bool slave_process, bool exit_on_error,
		int fd[1])
{
#if defined _MSC_VER || defined __MINGW32__

  /* Native Woe32 API.
     This uses _pipe(), dup2(), and spawnv().  It could also be implemented
     using the low-level functions CreatePipe(), DuplicateHandle(),
     CreateProcess() and _open_osfhandle(); see the GNU make and GNU clisp
     and cvs source code.  */
  int ifd[2];
  int orig_stdin;
  int orig_stdout;
  int orig_stderr;
  int child;
  int nulloutfd;
  int stdinfd;

  prog_argv = prepare_spawn (prog_argv);

  if (_pipe (ifd, 4096, O_BINARY | O_NOINHERIT) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           read        system         write
 *    parent  <-  ifd[0]   <-   ifd[1]   <-   child
 */

  /* Save standard file handles of parent process.  */
  if (prog_stdin != NULL)
    orig_stdin = dup_noinherit (STDIN_FILENO);
  orig_stdout = dup_noinherit (STDOUT_FILENO);
  if (null_stderr)
    orig_stderr = dup_noinherit (STDERR_FILENO);
  child = -1;

  /* Create standard file handles of child process.  */
  nulloutfd = -1;
  stdinfd = -1;
  if (dup2 (ifd[1], STDOUT_FILENO) >= 0
      && (!null_stderr
	  || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
	      && (nulloutfd == STDERR_FILENO
		  || (dup2 (nulloutfd, STDERR_FILENO) >= 0
		      && close (nulloutfd) >= 0))))
      && (prog_stdin == NULL
	  || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
	      && (stdinfd == STDIN_FILENO
		  || (dup2 (stdinfd, STDIN_FILENO) >= 0
		      && close (stdinfd) >= 0)))))
    /* The child process doesn't inherit ifd[0] and ifd[1], but it
       inherits all open()ed or dup2()ed file handles (which is what
       we want in the case of STD*_FILENO) and also orig_stdin,
       orig_stdout, orig_stderr (which is not explicitly wanted but
       harmless).  */
    child = spawnvp (P_NOWAIT, prog_path, prog_argv);
  if (stdinfd >= 0)
    close (stdinfd);
  if (nulloutfd >= 0)
    close (nulloutfd);

  /* Restore standard file handles of parent process.  */
  if (null_stderr)
    dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr);
  dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout);
  if (prog_stdin != NULL)
    dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin);

  close (ifd[1]);
  if (child == -1)
    {
      if (exit_on_error || !null_stderr)
	error (exit_on_error ? EXIT_FAILURE : 0, errno,
	       _("%s subprocess failed"), progname);
      close (ifd[0]);
      return -1;
    }

  fd[0] = ifd[0];
  return child;

#else

  /* Unix API.  */
  int ifd[2];
#if HAVE_POSIX_SPAWN
  sigset_t blocked_signals;
  posix_spawn_file_actions_t actions;
  bool actions_allocated;
  posix_spawnattr_t attrs;
  bool attrs_allocated;
  int err;
  pid_t child;
#else
  int child;
#endif

  if (pipe (ifd) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           read        system         write
 *    parent  <-  ifd[0]   <-   ifd[1]   <-   child
 */

#if HAVE_POSIX_SPAWN
  if (slave_process)
    {
      sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
      block_fatal_signals ();
    }
  actions_allocated = false;
  attrs_allocated = false;
  if ((err = posix_spawn_file_actions_init (&actions)) != 0
      || (actions_allocated = true,
	  (err = posix_spawn_file_actions_adddup2 (&actions,
						   ifd[1], STDOUT_FILENO)) != 0
	  || (err = posix_spawn_file_actions_addclose (&actions, ifd[1])) != 0
	  || (err = posix_spawn_file_actions_addclose (&actions, ifd[0])) != 0
	  || (null_stderr
	      && (err = posix_spawn_file_actions_addopen (&actions,
							  STDERR_FILENO,
							  "/dev/null", O_RDWR,
							  0))
		 != 0)
	  || (prog_stdin != NULL
	      && (err = posix_spawn_file_actions_addopen (&actions,
							  STDIN_FILENO,
							  prog_stdin, O_RDONLY,
							  0))
		 != 0)
	  || (slave_process
	      && ((err = posix_spawnattr_init (&attrs)) != 0
		  || (attrs_allocated = true,
		      (err = posix_spawnattr_setsigmask (&attrs,
							 &blocked_signals))
		      != 0
		      || (err = posix_spawnattr_setflags (&attrs,
							POSIX_SPAWN_SETSIGMASK))
			 != 0)))
	  || (err = posix_spawnp (&child, prog_path, &actions,
				  attrs_allocated ? &attrs : NULL, prog_argv,
				  environ))
	     != 0))
    {
      if (actions_allocated)
	posix_spawn_file_actions_destroy (&actions);
      if (attrs_allocated)
	posix_spawnattr_destroy (&attrs);
      if (slave_process)
	unblock_fatal_signals ();
      if (exit_on_error || !null_stderr)
	error (exit_on_error ? EXIT_FAILURE : 0, err,
	       _("%s subprocess failed"), progname);
      close (ifd[0]);
      close (ifd[1]);
      return -1;
    }
  posix_spawn_file_actions_destroy (&actions);
  if (attrs_allocated)
    posix_spawnattr_destroy (&attrs);
#else
  if (slave_process)
    block_fatal_signals ();
  /* Use vfork() instead of fork() for efficiency.  */
  if ((child = vfork ()) == 0)
    {
      /* Child process code.  */
      int nulloutfd;
      int stdinfd;

      if (dup2 (ifd[1], STDOUT_FILENO) >= 0
	  && close (ifd[1]) >= 0
	  && close (ifd[0]) >= 0
	  && (!null_stderr
	      || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
		  && (nulloutfd == STDERR_FILENO
		      || (dup2 (nulloutfd, STDERR_FILENO) >= 0
			  && close (nulloutfd) >= 0))))
	  && (prog_stdin == NULL
	      || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
		  && (stdinfd == STDIN_FILENO
		      || (dup2 (stdinfd, STDIN_FILENO) >= 0
			  && close (stdinfd) >= 0))))
	  && (!slave_process || (unblock_fatal_signals (), true)))
	execvp (prog_path, prog_argv);
      _exit (127);
    }
  if (child == -1)
    {
      if (slave_process)
	unblock_fatal_signals ();
      if (exit_on_error || !null_stderr)
	error (exit_on_error ? EXIT_FAILURE : 0, errno,
	       _("%s subprocess failed"), progname);
      close (ifd[0]);
      close (ifd[1]);
      return -1;
    }
#endif
  if (slave_process)
    {
      register_slave_subprocess (child);
      unblock_fatal_signals ();
    }
  close (ifd[1]);

  fd[0] = ifd[0];
  return child;

#endif
}