Exemplo n.º 1
0
static int run(char* const commands[], pid_t* pid, bool include_stderr) {
    if(commands == NULL) { return 0; }
    if(pid == NULL) { die("Given NULL pid"); }

    int fds[2];
    if(pipe(fds)) {
        die("Failed to open pipe");
    }

    posix_spawn_file_actions_t action;
    posix_spawn_file_actions_init(&action);
    posix_spawn_file_actions_addclose(&action, fds[0]);
    posix_spawn_file_actions_adddup2(&action, fds[1], 1);
    posix_spawn_file_actions_addclose(&action, fds[1]);

    if(include_stderr) {
        posix_spawn_file_actions_adddup2(&action, 1, 2);
    }

    int status = posix_spawn(pid, commands[0],
                             &action,
                             NULL,
                             commands,
                             NULL);
    if(status != 0) {
        die("Failed to spawn process");
    }
    close(fds[1]);

    return fds[0];
}
Exemplo n.º 2
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);
}
Exemplo n.º 3
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(""));
	}
}
Exemplo n.º 4
0
void pipeline(const char *const *argv, struct pipeline *pl)
{
  posix_spawn_file_actions_t file_acts;

  int pipefds[2];
  if (pipe(pipefds)) {
    die_errno(errno, "pipe");
  }

  die_errno(posix_spawn_file_actions_init(&file_acts),
            "posix_spawn_file_actions_init");
  die_errno(posix_spawn_file_actions_adddup2(&file_acts, pipefds[0], 0),
            "posix_spawn_file_actions_adddup2");
  die_errno(posix_spawn_file_actions_addclose(&file_acts, pipefds[0]),
            "posix_spawn_file_actions_addclose");
  die_errno(posix_spawn_file_actions_addclose(&file_acts, pipefds[1]),
            "posix_spawn_file_actions_addclose");

  die_errno(posix_spawnp(&pl->pid, argv[0], &file_acts, NULL,
                         (char * const *)argv, environ),
            "posix_spawnp: %s", argv[0]);

  die_errno(posix_spawn_file_actions_destroy(&file_acts),
            "posix_spawn_file_actions_destroy");

  if (close(pipefds[0])) {
    die_errno(errno, "close");
  }

  pl->infd = pipefds[1];
}
Exemplo n.º 5
0
int main(int argc, char* argv[])
{
	char** newargv;
	int status;
	argv0 = argv[0];
	argc--; argv++;

	if(!argc)
	{
		usage(1);
	}

	struct child {
		int in[2];
		int out[2];
	} child;

	if(pipe(child.in) < 0)
		perror("pipe");
	if(pipe(child.out) < 0)
		perror("pipe");

	pid_t pid;

	posix_spawn_file_actions_t action;
	posix_spawn_file_actions_init(&action);
	posix_spawn_file_actions_adddup2(&action, child.in[0], STDIN_FILENO);
	posix_spawn_file_actions_addclose(&action, child.in[0]);
	posix_spawn_file_actions_adddup2(&action, child.out[1], STDOUT_FILENO);
	posix_spawn_file_actions_addclose(&action, child.out[1]);

	if(posix_spawnp(&pid, argv[0], &action, NULL, argv, NULL) < 0)
	{
		// TODO: manage errors here.
	}

	int i;
	char c[2];
	c[1] = '\0';
	for(i = 0; i < 5; i++)
	{
		c[0] = '0' + i;
		write(child.in[1], "echo ", 5);
		write(child.in[1], c, 1);
		write(child.in[1], "\n", 1);
		c[0] = '\0';
		read(child.out[0], c, 1);
		printf("Output was: %s\n", c);
		read(child.out[0], c, 1); // read the newline
	}

	write(child.in[1], "exit\n", 5);

	waitpid(pid, &status, 0);

	printf("%s exited with status %d\n", argv[0], status);
	return 0;
}
Exemplo n.º 6
0
/*
 * Spawn a process to run "sh -c <cmd>".  Return the child's pid (in
 * *pidp), and a file descriptor (in *fdp) for reading or writing to the
 * child process, depending on the 'write' argument.
 * Return 0 on success; otherwise return an error code.
 */
int
posix_spawn_pipe_np(pid_t *pidp, int *fdp,
    const char *cmd, boolean_t write,
    posix_spawn_file_actions_t *fact, posix_spawnattr_t *attr)
{
	int	p[2];
	int	myside, yourside, stdio;
	const char *shpath = _PATH_BSHELL;
	const char *argvec[4] = { "sh", "-c", cmd, NULL };
	int	error;

	if (pipe(p) < 0)
		return (errno);

	if (access(shpath, X_OK))	/* XPG4 Requirement: */
		shpath = "";		/* force child to fail immediately */

	if (write) {
		/*
		 * Data is read from p[0] and written to p[1].
		 * 'stdio' is the fd in the child process that should be
		 * connected to the pipe.
		 */
		myside = p[1];
		yourside = p[0];
		stdio = STDIN_FILENO;
	} else {
		myside = p[0];
		yourside = p[1];
		stdio = STDOUT_FILENO;
	}

	error = posix_spawn_file_actions_addclose(fact, myside);
	if (yourside != stdio) {
		if (error == 0) {
			error = posix_spawn_file_actions_adddup2(fact,
			    yourside, stdio);
		}
		if (error == 0) {
			error = posix_spawn_file_actions_addclose(fact,
			    yourside);
		}
	}

	if (error)
		return (error);
	error = posix_spawn(pidp, shpath, fact, attr,
	    (char *const *)argvec, (char *const *)_environ);
	(void) close(yourside);
	if (error) {
		(void) close(myside);
		return (error);
	}

	*fdp = myside;
	return (0);
}
Exemplo n.º 7
0
/* Set up file actions for posix_spawn.
   *std__fd is FD_FORWARD, FD_CLOSE, FD_PIPE etc or a file descriptor #
   pipe[2] is the pipe created for FD_PIPE
   childfd is 0 (for stdin) 1 (for stdout) 2 (for stderr)
   input_to_child is 1 for stdin, 0 otherwise
   *hasactions is set on output if we added any file actions
   */
static qioerr setup_actions(
    posix_spawn_file_actions_t * actions,
    int* std__fd, int pipe[2], int childfd,
    int input_to_child,
    bool * hasactions)
{
  int pipe_parent_end;
  int pipe_child_end;
  int rc;

  assert(childfd == 0 || childfd == 1 || childfd == 2);

  // for stdin, the parent end is pipe[1], child end is pipe[0];
  // for everything else, the parent end is pipe[0], child end is pipe[1].
  if( input_to_child ) {
    pipe_parent_end = pipe[1];
    pipe_child_end = pipe[0];
  } else {
    pipe_parent_end = pipe[0];
    pipe_child_end = pipe[1];
  }

  if( *std__fd == QIO_FD_FORWARD ) {
    // Do nothing. Assume file descriptor childfd does not have close-on-exec.
  } else if( *std__fd == QIO_FD_PIPE || *std__fd == QIO_FD_BUFFERED_PIPE ) {
    // child can't write to the parent end of the pipe.
    rc = posix_spawn_file_actions_addclose(actions, pipe_parent_end);
    if( rc ) return qio_int_to_err(errno);

    // child needs to use its end of the pipe as fd childfd.
    rc = posix_spawn_file_actions_adddup2(actions, pipe_child_end, childfd);
    if( rc ) return qio_int_to_err(errno);

    // then close the pipe we dup'd since it is now known by another
    // name (e.g. fd 0).
    posix_spawn_file_actions_addclose(actions, pipe_child_end);
    *hasactions = true;
  } else if( *std__fd == QIO_FD_CLOSE ) {
    // close stdin.
    rc = posix_spawn_file_actions_addclose(actions, childfd);
    if( rc ) return qio_int_to_err(errno);
    *hasactions = true;
  } else if( *std__fd == QIO_FD_TO_STDOUT ) {
    // Do nothing.

  } else {
    // Use a given file descriptor for childfd (e.g. stdin).
    rc = posix_spawn_file_actions_adddup2(actions, *std__fd, childfd);
    if( rc ) return qio_int_to_err(errno);
    // then close the pipe we dup'd
    rc = posix_spawn_file_actions_addclose(actions, *std__fd);
    if( rc ) return qio_int_to_err(errno);
    *hasactions = true;
  }

  return 0;
}
Exemplo n.º 8
0
static void *
tf (void *arg)
{
    xpthread_barrier_wait (&b);

    posix_spawn_file_actions_t a;
    if (posix_spawn_file_actions_init (&a) != 0)
    {
        puts ("error: spawn_file_actions_init failed");
        exit (1);
    }

    if (posix_spawn_file_actions_adddup2 (&a, pipefd[1], STDOUT_FILENO) != 0)
    {
        puts ("error: spawn_file_actions_adddup2 failed");
        exit (1);
    }

    if (posix_spawn_file_actions_addclose (&a, pipefd[0]) != 0)
    {
        puts ("error: spawn_file_actions_addclose");
        exit (1);
    }

    char *argv[] = { (char *) _PATH_BSHELL, (char *) "-c", (char *) "echo $$",
                     NULL
                   };
    if (posix_spawn (&pid, _PATH_BSHELL, &a, NULL, argv, NULL) != 0)
    {
        puts ("error: spawn failed");
        exit (1);
    }

    return NULL;
}
Exemplo n.º 9
0
static void CatFileToString(posix_spawnattr_t* sa, const char* path, std::string* content) {
  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]));

  ExecTestHelper eth;
  eth.SetArgs({"cat", path, nullptr});
  pid_t pid;
  ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), &fa, sa, eth.GetArgs(), nullptr));
  ASSERT_EQ(0, posix_spawn_file_actions_destroy(&fa));

  ASSERT_EQ(0, close(fds[1]));
  ASSERT_TRUE(android::base::ReadFdToString(fds[0], content));
  ASSERT_EQ(0, close(fds[0]));
  AssertChildExited(pid, 0);
}
Exemplo n.º 10
0
static int l_posix_spawn_file_actions_addclose(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);
	if (0 != (r = posix_spawn_file_actions_addclose(file_actions, fd))) {
		lua_pushnil(L);
		lua_pushstring(L, strerror(r));
		lua_pushinteger(L, r);
		return 3;
	}
	lua_pushboolean(L, 1);
	return 1;
}
Exemplo n.º 11
0
pid_t child_spawn1_internal (char const *prog, char const *const *argv, char const *const *envp, int *p, int to)
{
  posix_spawn_file_actions_t actions ;
  posix_spawnattr_t attr ;
  int e ;
  pid_t pid ;
  int haspath = !!env_get("PATH") ;
  if (coe(p[!(to & 1)]) < 0) { e = errno ; goto err ; }
  e = posix_spawnattr_init(&attr) ;
  if (e) goto err ;
  {
    sigset_t set ;
    sigemptyset(&set) ;
    e = posix_spawnattr_setsigmask(&attr, &set) ;
    if (e) goto errattr ;
    e = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK) ;
    if (e) goto errattr ;
  }
  e = posix_spawn_file_actions_init(&actions) ;
  if (e) goto errattr ;
  e = posix_spawn_file_actions_adddup2(&actions, p[to & 1], to & 1) ;
  if (e) goto erractions ;
  e = posix_spawn_file_actions_addclose(&actions, p[to & 1]) ;
  if (e) goto erractions ;
  if (to & 2)
  {
    e = posix_spawn_file_actions_adddup2(&actions, to & 1, !(to & 1)) ;
    if (e) goto erractions ;
  }
  if (!haspath && (setenv("PATH", SKALIBS_DEFAULTPATH, 0) < 0)) { e = errno ; goto erractions ; }
  e = posix_spawnp(&pid, prog, &actions, &attr, (char *const *)argv, (char *const *)envp) ;
  if (!haspath) unsetenv("PATH") ;
  posix_spawn_file_actions_destroy(&actions) ;
  posix_spawnattr_destroy(&attr) ;
  fd_close(p[to & 1]) ;
  if (e) goto errp ;
  return pid ;

 erractions:
  posix_spawn_file_actions_destroy(&actions) ;
 errattr:
  posix_spawnattr_destroy(&attr) ;
 err:
  fd_close(p[to & 1]) ;
 errp:
  fd_close(p[!(to & 1)]) ;
  errno = e ;
  return 0 ;
}
Exemplo n.º 12
0
/*
 * Spawn a sh(1) command. Writes the resulting process ID to the pid_t pointed
 * at by `pid'. Returns a file descriptor (int) suitable for writing data to
 * the spawned command (data written to file descriptor is seen as standard-in
 * by the spawned sh(1) command). Returns `-1' if unable to spawn command.
 *
 * If cmd contains a single "%s" sequence, replace it with label if non-NULL.
 */
int
shell_spawn_pipecmd(const char *cmd, const char *label, pid_t *pid)
{
	int error;
	int len;
	posix_spawn_file_actions_t action;
#if SHELL_SPAWN_DEBUG
	unsigned int i;
#endif
	int stdin_pipe[2] = { -1, -1 };

	/* Populate argument array */
	if (label != NULL && fmtcheck(cmd, "%s") == cmd)
		len = snprintf(cmdbuf, CMDBUFMAX, cmd, label);
	else
		len = snprintf(cmdbuf, CMDBUFMAX, "%s", cmd);
	if (len >= CMDBUFMAX) {
		warnx("%s:%d:%s: cmdbuf[%u] too small to hold cmd argument",
		    __FILE__, __LINE__, __func__, CMDBUFMAX);
		return (-1);
	}

	/* Open a pipe to communicate with [X]dialog(1) */
	if (pipe(stdin_pipe) < 0)
		err(EXIT_FAILURE, "%s: pipe(2)", __func__);

	/* Fork sh(1) process */
#if SHELL_SPAWN_DEBUG
	fprintf(stderr, "%s: spawning `", __func__);
	for (i = 0; shellcmd_argv[i] != NULL; i++) {
		if (i == 0)
			fprintf(stderr, "%s", shellcmd_argv[i]);
		else if (i == 2)
			fprintf(stderr, " '%s'", shellcmd_argv[i]);
		else
			fprintf(stderr, " %s", shellcmd_argv[i]);
	}
	fprintf(stderr, "'\n");
#endif
	posix_spawn_file_actions_init(&action);
	posix_spawn_file_actions_adddup2(&action, stdin_pipe[0], STDIN_FILENO);
	posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
	error = posix_spawnp(pid, shellcmd, &action,
	    (const posix_spawnattr_t *)NULL, shellcmd_argv, environ);
	if (error != 0)
		err(EXIT_FAILURE, "%s: posix_spawnp(3)", __func__);

	return stdin_pipe[1];
}
Exemplo n.º 13
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();
}
Exemplo n.º 14
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
}
Exemplo n.º 15
0
int main(int argc, char *argv[]) {
	static const int yes = 1;
	char *s;
	struct sockaddr_in server;
	unsigned int port;
	int sock_listen, sock_accept;
	pid_t pid;

	if(argc < 3) {
		fprintf(stderr, "usage: %s [ipaddr:]port program "
				"[argv0 argv1 ...]\n", argv[0]);
		return 1;
	}
	
	memset(&server, 0, sizeof(server));
	server.sin_family = AF_INET;
	assert(argv[1] != NULL);
	s = strchr(argv[1], ':');
	if(NULL == s)
		s = argv[1];
	else {
		*s++ = '\0';
		if(0 == inet_aton(argv[1], &server.sin_addr)) {
			fprintf(stderr, "not an ip address: %s\n", argv[1]);
			return 1;
		}
	}
	if((1 != sscanf(s, "%u", &port)) ||
			port != (unsigned int)(uint16_t)port) {
		fprintf(stderr, "not a valid port: %s\n", s);
		return 1;
	}
	server.sin_port = htons(port);
	if((sock_listen = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket creation failed");
		return 1;
	}
	if(setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))
			== -1) { 
		perror("failed to set SO_REUSEADDR on socket");
	}
	if(bind(sock_listen, (struct sockaddr*)&server, sizeof(server)) < 0) {
		perror("failed to bind socket");
		close(sock_listen);
		return 1;
	}
	if(listen(sock_listen, 5) != 0) {
		perror("failed to listen on the socket");
		close(sock_listen);
		return 1;
	}

	/* We do *not* care about childs */
	signal(SIGCHLD, SIG_IGN);

	while((sock_accept = accept(sock_listen, NULL, NULL)) != -1) {
		posix_spawn_file_actions_t action;

		posix_spawn_file_actions_init(&action);
		posix_spawn_file_actions_addclose(&action, sock_listen);
		posix_spawn_file_actions_adddup2(&action, sock_accept, 0);
		posix_spawn_file_actions_adddup2(&action, sock_accept, 1);
		posix_spawn_file_actions_addclose(&action, sock_accept);

		if(0 == posix_spawnp(&pid, argv[2],
			&action,
			NULL,
			argv + 3, NULL)) {
			close(sock_accept);
		} else {
			perror("vfork failed");
			close(sock_listen);
			return 1;
		}
	}
	perror("accept failed");
	close(sock_listen);
	return 1;
}
Exemplo n.º 16
0
pid_t h2o_spawnp(const char *cmd, char *const *argv, const int *mapped_fds, int cloexec_mutex_is_locked)
{
#if defined(__linux__)

    /* posix_spawnp of Linux does not return error if the executable does not exist, see
     * https://gist.github.com/kazuho/0c233e6f86d27d6e4f09
     */
    extern char **environ;
    int pipefds[2] = {-1, -1}, errnum;
    pid_t pid;

    /* create pipe, used for sending error codes */
    if (pipe2(pipefds, O_CLOEXEC) != 0)
        goto Error;

    /* fork */
    if (!cloexec_mutex_is_locked)
        pthread_mutex_lock(&cloexec_mutex);
    if ((pid = fork()) == 0) {
        /* in child process, map the file descriptors and execute; return the errnum through pipe if exec failed */
        if (mapped_fds != NULL) {
            for (; *mapped_fds != -1; mapped_fds += 2) {
                if (mapped_fds[1] != -1)
                    dup2(mapped_fds[0], mapped_fds[1]);
                close(mapped_fds[0]);
            }
        }
        char **env = build_spawn_env();
        if (env != NULL)
            environ = env;
        execvp(cmd, argv);
        errnum = errno;
        write(pipefds[1], &errnum, sizeof(errnum));
        _exit(EX_SOFTWARE);
    }
    if (!cloexec_mutex_is_locked)
        pthread_mutex_unlock(&cloexec_mutex);
    if (pid == -1)
        goto Error;

    /* parent process */
    close(pipefds[1]);
    pipefds[1] = -1;
    ssize_t rret;
    errnum = 0;
    while ((rret = read(pipefds[0], &errnum, sizeof(errnum))) == -1 && errno == EINTR)
        ;
    if (rret != 0) {
        /* spawn failed */
        while (waitpid(pid, NULL, 0) != pid)
            ;
        pid = -1;
        errno = errnum;
        goto Error;
    }

    /* spawn succeeded */
    close(pipefds[0]);
    return pid;

Error:
    errnum = errno;
    if (pipefds[0] != -1)
        close(pipefds[0]);
    if (pipefds[1] != -1)
        close(pipefds[1]);
    errno = errnum;
    return -1;

#elif defined _MSC_VER //-- : process : https://msdn.microsoft.com/en-us/library/20y988d2.aspx
	pid_t pid;
	extern char** environ;
	uv_loop_t *loop;
	uv_process_t child_req;
	uv_process_options_t options = { 0 }; //-- Default initialization must be 0
	loop = uv_default_loop();
	//-- Container for file descruptor
	uv_stdio_container_t child_stdio[3];
	child_stdio[0].flags = UV_IGNORE;
	child_stdio[1].flags = UV_INHERIT_FD; //STD_OUT should be redirected to calling function
	child_stdio[2].flags = UV_IGNORE;

	if (mapped_fds != NULL) {
		for (; *mapped_fds != -1; mapped_fds += 2) {
			if (mapped_fds[1] != -1)
				//dup2(mapped_fds[0], mapped_fds[1]); //Equivalent to dup2() in *inx systems.
				child_stdio[1].data.fd = mapped_fds[0]; //2 or 1?
														//close(mapped_fds[0]); //add or delete a close or open action to a spawn file actions object
		}
	}

	if (!cloexec_mutex_is_locked)
		uv_mutex_lock(&cloexec_mutex);

	options.stdio = child_stdio;
	options.exit_cb = waitpid; //This function will be executed when child exists.
	options.file = cmd;
	options.args = argv;
	options.env = environ;
	//If the process is successfully spawned, this function will return 0.
	errno = uv_spawn(loop, &child_req, &options);
	pid = child_req.pid;


	if (!cloexec_mutex_is_locked)
		uv_mutex_unlock(&cloexec_mutex);
	if (errno != 0)
		return -1;


	return pid;
#else
    posix_spawn_file_actions_t file_actions;
    pid_t pid;
    extern char **environ;
    char **env = build_spawn_env();
    posix_spawn_file_actions_init(&file_actions);
    if (mapped_fds != NULL) {
        for (; *mapped_fds != -1; mapped_fds += 2) {
            if (mapped_fds[1] != -1)
                posix_spawn_file_actions_adddup2(&file_actions, mapped_fds[0], mapped_fds[1]);
            posix_spawn_file_actions_addclose(&file_actions, mapped_fds[0]);
        }
    }
    if (!cloexec_mutex_is_locked)
        pthread_mutex_lock(&cloexec_mutex);
    errno = posix_spawnp(&pid, cmd, &file_actions, NULL, argv, env != NULL ? env : environ);
    if (!cloexec_mutex_is_locked)
        pthread_mutex_unlock(&cloexec_mutex);
    free(env);
    if (errno != 0)
        return -1;

    return pid;

#endif
}
Exemplo n.º 17
0
bool Process::start() {

#ifdef WIN32

	handle_IN_Rd = NULL;
	handle_IN_Wr = NULL;
	handle_OUT_Rd = NULL;
	handle_OUT_Wr = NULL;
	handle_ERR_Rd = NULL;
	handle_ERR_Wr = NULL;

	p_stdin = -1;
	p_stdout = -1;
	p_stderr = -1;

	SECURITY_ATTRIBUTES saAttr; 

	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
	saAttr.bInheritHandle = TRUE; 
	saAttr.lpSecurityDescriptor = NULL; 

	// Create a pipe for the child process's STDOUT. 
   if ( ! CreatePipe(&handle_OUT_Rd, &handle_OUT_Wr, &saAttr, 0) ) 
      return false; 

	// Create a pipe for the child process's STDERR. 
   if ( ! CreatePipe(&handle_ERR_Rd, &handle_ERR_Wr, &saAttr, 0) ) 
      return false; 

    // Create a pipe for the child process's STDIN. 
    if (! CreatePipe(&handle_IN_Rd, &handle_IN_Wr, &saAttr, 0)) 
        return false; 

    if (explicit_mode) {
 
        if ( ! SetHandleInformation(handle_IN_Rd, HANDLE_FLAG_INHERIT, 1) )
            return false; 

        if ( ! SetHandleInformation(handle_OUT_Wr, HANDLE_FLAG_INHERIT, 1) )
            return false; 

    } 

    // Ensure the write handle to the pipe for STDIN is not inherited.  
    if ( ! SetHandleInformation(handle_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
        return false; 

    // Ensure the read handle to the pipe for STDOUT is not inherited.
    if ( ! SetHandleInformation(handle_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
        return false; 

    // Ensure the read handle to the pipe for STDERR is not inherited.
    if ( ! SetHandleInformation(handle_ERR_Rd, HANDLE_FLAG_INHERIT, 0) )
        return false; 


	STARTUPINFO siStartInfo;
	BOOL bSuccess = FALSE; 
 
	// Set up members of the PROCESS_INFORMATION structure. 
 
	ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
 

	stringstream cmdbuffer;
	int curargument = 0;
	while (arguments[curargument]) {
		cmdbuffer << "\"" << arguments[curargument] << "\" ";
		curargument++;
	}


	stringstream envbuffer;
    map<string, string>::iterator iter;
    for (iter = env.begin(); iter != env.end(); ++iter) {
        envbuffer << iter->first << string("=") << iter->second << '\0';
    }

	// Set up members of the STARTUPINFO structure. 
	// This structure specifies the STDIN and STDOUT handles for redirection.
	ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
	siStartInfo.cb = sizeof(STARTUPINFO); 
    if (!explicit_mode) {
	    siStartInfo.hStdError = handle_ERR_Wr;
	    siStartInfo.hStdOutput = handle_OUT_Wr;
	    siStartInfo.hStdInput = handle_IN_Rd;
        siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
    } else {
	    siStartInfo.hStdError = handle_ERR_Wr;
		HANDLE pHandle = GetCurrentProcess();
		HANDLE handle_IN_Rd2, handle_OUT_Wr2;
		DuplicateHandle(pHandle, handle_IN_Rd, pHandle, &handle_IN_Rd2, DUPLICATE_SAME_ACCESS, true, DUPLICATE_SAME_ACCESS);
		DuplicateHandle(pHandle, handle_OUT_Wr, pHandle, &handle_OUT_Wr2, DUPLICATE_SAME_ACCESS, true, DUPLICATE_SAME_ACCESS);
        envbuffer << string("TRAX_IN=") << handle_IN_Rd2 << '\0';
        envbuffer << string("TRAX_OUT=") << handle_OUT_Wr2 << '\0';
    }
	
    envbuffer << '\0';
	
	LPCSTR curdir = directory.empty() ? NULL : directory.c_str();

	if (!CreateProcess(NULL, (char *) cmdbuffer.str().c_str(), NULL, NULL, true, 0,
		(void *)envbuffer.str().c_str(),
		curdir, &siStartInfo, &piProcInfo )) {

		std::cout << "Error: " << GetLastError()  << std::endl;
		cleanup();
		return false;
	}

	int wrfd = _open_osfhandle((intptr_t)handle_IN_Wr, 0);
	int rdfd = _open_osfhandle((intptr_t)handle_OUT_Rd, _O_RDONLY);
	int erfd = _open_osfhandle((intptr_t)handle_ERR_Rd, _O_RDONLY);

	if (wrfd == -1 || rdfd == -1) {
		stop();
		return false;
	}

    p_stdin = wrfd;
    p_stdout = rdfd;
    p_stderr = erfd;

	if (!p_stdin || !p_stdout) {
		stop();
		return false;
	}

#else

    if (pid) return false;

    pipe(out);
    pipe(in);
    pipe(err);

    vector<string> vars;

    map<string, string>::iterator iter;
    for (iter = env.begin(); iter != env.end(); ++iter) {
       // if (iter->first == "PWD") continue;
        vars.push_back(iter->first + string("=") + iter->second);
    }

    posix_spawn_file_actions_init(&action);
    posix_spawn_file_actions_addclose(&action, out[1]);
    posix_spawn_file_actions_addclose(&action, in[0]);
    posix_spawn_file_actions_addclose(&action, err[0]);

    if (!explicit_mode) {
        posix_spawn_file_actions_adddup2(&action, out[0], 0);
        posix_spawn_file_actions_adddup2(&action, in[1], 1);
        posix_spawn_file_actions_adddup2(&action, err[1], 2);
    } else {
        posix_spawn_file_actions_adddup2(&action, err[1], 2);
        vars.push_back(string("TRAX_OUT=") + int_to_string(in[1]));
        vars.push_back(string("TRAX_IN=") + int_to_string(out[0]));
    }

    std::vector<char *> vars_c(vars.size() + 1); 

    for (std::size_t i = 0; i != vars.size(); ++i) {
        vars_c[i] = &vars[i][0];
    }

    vars_c[vars.size()] = NULL;

    string cwd = __getcwd();

    if (directory.size() > 0)
        chdir(directory.c_str());

    if (posix_spawnp(&pid, program, &action, NULL, arguments, vars_c.data())) {
        cleanup();
        pid = 0;
        if (directory.size() > 0) chdir(cwd.c_str());
        return false;
    }

    if (directory.size() > 0) chdir(cwd.c_str());

    p_stdin = out[1];
    p_stdout = in[0];
    p_stderr = err[0];

#endif

    return true;

}
Exemplo n.º 18
0
int swift_posix_spawn_file_actions_addclose(
    posix_spawn_file_actions_t *file_actions, int filedes) {
  return posix_spawn_file_actions_addclose(file_actions, filedes);
}
BatteryOutput TestRunner::executeBinary(Logger * logger,
                                        const std::string & objectInfo,
                                        const std::string & binaryPath,
                                        const std::string & arguments,
                                        const std::string & input) {
    int stdin_pipe[2];
    int stdout_pipe[2];
    int stderr_pipe[2];

    pid_t pid = 0;
    posix_spawn_file_actions_t actions;

    if(pipe(stdin_pipe) || pipe(stdout_pipe) || pipe(stderr_pipe)) {
        logger->warn(objectInfo + ": pipe creation failed. Test won't be executed.");
        {
            /* Remove thread from list of threads without child, notify and end */
            std::lock_guard<std::mutex> l (withoutChild_mux);
            --withoutChild;
        }
        withoutChild_cv.notify_one();
        return {};
    }

    /* Pipes will be mapped to I/O after process start */
    /* Unused ends are closed */
    posix_spawn_file_actions_init(&actions);
    /* Standard input */
    posix_spawn_file_actions_addclose(&actions , stdin_pipe[1]);
    posix_spawn_file_actions_adddup2(&actions , stdin_pipe[0] , 0);
    posix_spawn_file_actions_addclose(&actions , stdin_pipe[0]);
    /* Standard output */
    posix_spawn_file_actions_addclose(&actions , stdout_pipe[0]);
    posix_spawn_file_actions_adddup2(&actions , stdout_pipe[1] , 1);
    posix_spawn_file_actions_addclose(&actions , stdout_pipe[1]);
    /* Standard error output */
    posix_spawn_file_actions_addclose(&actions , stderr_pipe[0]);
    posix_spawn_file_actions_adddup2(&actions , stderr_pipe[1] , 2);
    posix_spawn_file_actions_addclose(&actions , stderr_pipe[1]);

    int argc = 0;
    char ** args = buildArgv(arguments , &argc);

    /* Creating locks but not locking them yet */
    std::unique_lock<std::mutex> finishedPid_lock(finishedPid_mux , std::defer_lock);
    std::unique_lock<std::mutex> withoutChild_lock(withoutChild_mux , std::defer_lock);
    std::unique_lock<std::mutex> waitingForChild_lock(waitingForChild_mux , std::defer_lock);
    std::unique_lock<std::mutex> childReceived_lock(childReceived_mux , std::defer_lock);
    std::unique_lock<std::mutex> threadsReady_lock(threadState_mux , std::defer_lock);

    /* Need both locks, if they cannot be acquired
     * main thread is waiting for process to end */
    std::lock(finishedPid_lock , waitingForChild_lock);

    /* Starting child process of this thread */
    logger->info(objectInfo + ": spawning child process with arguments " + arguments);
    int status = posix_spawn(&pid , binaryPath.c_str() ,
                             &actions , NULL , args , NULL);

    if(status == 0) {
        /* Process was started without problems, proceed */
        logger->info(objectInfo + ": child process has pid " + Utils::itostr(pid));
        /* Closing pipes */
        close(stdin_pipe[0]);
        close(stdout_pipe[1]);
        close(stderr_pipe[1]);
        if(!input.empty())
            write(stdin_pipe[1] , input.c_str() , input.length());
        /* Start thread for output reading. Thread will be mostly in blocked wait.
         * Also will be essentially over as soon as the process finishes.
         * With this, pipes won't be filled and won't block underlying process. */
        BatteryOutput output;
        std::thread reader(readOutput , std::ref(output),
                           stdout_pipe , stderr_pipe);

        /* Incrementing number of threads waiting.
         * Only if this variable is same as withoutChild
         * main thread can start reaping child processes. */
        ++waitingForChild;
        waitingForChild_lock.unlock();
        waitingForChild_cv.notify_one();

        /* Waiting for PID. PID is annouced by main thread and thread
         * will work with his associated process only
         * (because the thread owns the pipes to this process) */
        auto now = std::chrono::system_clock::now();
        /* Don't know why, but if I use TIMEOUT constant directly in argument it won't compile... */
        int vArgumenteToJebeChybu = PROCESS_TIMEOUT_SECONDS;
        if(!finishedPid_cv.wait_until(finishedPid_lock ,
                                      now + std::chrono::seconds(vArgumenteToJebeChybu) ,
                                      [&] { return finishedPid == pid; })) {
            /* If thread goes into this branch, its process timeouted.
             * Send kill signal to it, main thread will then reap it.
             * That's why this go into wait once more (usually it will instantly return) */
            logger->warn(objectInfo + ": child process with pid " + Utils::itostr(pid) + " timeouted."
                         " Process will be killed now.");
            kill(pid , SIGKILL);
            finishedPid_cv.wait(finishedPid_lock , [&] { return finishedPid == pid; });
        }
        /* Obtaining all possible mutexes, decrementing variables that
         * track waiting and active threads, setting childReceived to true
         * so main thread will be notified */
        std::lock(withoutChild_lock , waitingForChild_lock , childReceived_lock);
        logger->info(objectInfo + ": child process with pid " + Utils::itostr(pid) + " finished");
        childReceived = true;
        --waitingForChild;
        --withoutChild;
        /* Unlock mutexes, this thread no longer needs them */
        finishedPid_lock.unlock();
        waitingForChild_lock.unlock();
        withoutChild_lock.unlock();
        childReceived_lock.unlock();
        /* Notify threadCreator that number of active threads changed.
         * He will spawn new threads if there is a need to. */
        withoutChild_cv.notify_one();

        /* This thread need to wait for notification from threadMaker
         * that it is ok to continue and after that, main thread can
         * be notified that child process was received and is being
         * processed.*/
        threadsReady_lock.lock();
        threadState_cv.wait(threadsReady_lock ,
                            []{ return (threadState == THREAD_STATE_DONE) ||
                                       (threadState == THREAD_STATE_READY); });
        if(threadState == THREAD_STATE_READY) {
            /* Flip threadState so next thread must wait too. */
            threadState = THREAD_STATE_PENDING;
        }
        threadsReady_lock.unlock();

        /* Notify main thread that it can continue in reaping processes */
        childReceived_cv.notify_one();
        /* This thread now completed all communication with other threads.
         *  DO YOUR WORK SLAVE!!! */
        reader.join();

        return output;
    } else {
        /* Some nasty error happened at execution. Report and end thread */
        logger->warn(objectInfo + ": can't execute child process. "
                     "This never happened during development.");
        {
            /* Remove thread from list of threads without child, notify and end */
            std::lock_guard<std::mutex> l (withoutChild_mux);
            --withoutChild;
        }
        withoutChild_cv.notify_one();
        return {};
    }
    /* All locks go out of scope, so everything will be unlocked here */
}
static int
do_test (void)
{
  int fd[2];
  if (pipe (fd) != 0)
    {
      puts ("pipe failed");
      exit (1);
    }

  /* Not interested in knowing when the pipe is closed.  */
  if (sigignore (SIGPIPE) != 0)
    {
      puts ("sigignore failed");
      exit (1);
    }

  posix_spawn_file_actions_t a;
  if (posix_spawn_file_actions_init (&a) != 0)
    {
      puts ("spawn_file_actions_init failed");
      exit (1);
    }

  if (posix_spawn_file_actions_adddup2 (&a, fd[1], STDOUT_FILENO) != 0)
    {
      puts ("spawn_file_actions_adddup2 failed");
      exit (1);
    }

  if (posix_spawn_file_actions_addclose (&a, fd[0]) != 0)
    {
      puts ("spawn_file_actions_addclose");
      exit (1);
    }

  pthread_t th;
  if (pthread_create (&th, NULL, tf, (void *) pthread_self ()) != 0)
    {
      puts ("create failed");
      exit (1);
    }

  pid_t pid;
  char *argv[] = { (char *) _PATH_BSHELL, (char *) "-c", (char *) "echo $$",
		   NULL };
  if (posix_spawn (&pid, _PATH_BSHELL, &a, NULL, argv, NULL) != 0)
    {
      puts ("spawn failed");
      exit (1);
    }

  close (fd[1]);

  char buf[200];
  ssize_t n;
  bool seen_pid = false;
  while (TEMP_FAILURE_RETRY ((n = read (fd[0], buf, sizeof (buf)))) > 0)
    {
      /* We only expect to read the PID.  */
      char *endp;
      long int rpid = strtol (buf, &endp, 10);

      if (*endp != '\n')
	{
	  printf ("didn't parse whole line: \"%s\"\n", buf);
	  exit (1);
	}
      if (endp == buf)
	{
	  puts ("read empty line");
	  exit (1);
	}

      if (rpid != pid)
	{
	  printf ("found \"%s\", expected PID %ld\n", buf, (long int) pid);
	  exit (1);
	}

      if (seen_pid)
	{
	  puts ("found more than one PID line");
	  exit (1);
	}

      seen_pid = true;
    }

  close (fd[0]);

  int status;
  int err = waitpid (pid, &status, 0);
  if (err != pid)
    {
      puts ("waitpid failed");
      exit (1);
    }

  if (!seen_pid)
    {
      puts ("didn't get PID");
      exit (1);
    }

  puts ("read correct PID");

  return 0;
}
Exemplo n.º 21
0
/**
 * A redirecting test command handler.
 *
 * @param context    The Exit context.
 * @param address    The environment name.
 * @param command    The command name and arguments.
 * @param ioContext  The IO Redirector context.
 */
RexxObjectPtr RexxEntry ioCommandHandler(RexxExitContext *context, RexxStringObject address, RexxStringObject command, RexxIORedirectorContext *ioContext)
{
    int pid;
    int status;
    char* argv[MAX_COMMAND_ARGS + 1];
    // If SYSSHELLPATH could ever grow longer than 128 chars
    // then this array size will need to be increased.
    char shell[128 + 1 + 256 + 1]; // SYSSHELLPATH plus "/" plus environment name

    CSTRING environment = context->CString(address);
    CSTRING commandString = context->CString(command);

    if (Utilities::strCaselessCompare("path", environment) == 0)
    {   // For the no-shell "direct" environment we need to break the command
        // string into a command name and separate arguments each (we have no
        // shell to do this for us).
        if (!scan_cmd(commandString, argv))
        {   // too many arguments
            return ErrorFailure(context, commandString);
        }
        // posix_spawnp() will fault if argv[0] is NULL.  Fix this.
        if (argv[0] == NULL)
        {
            argv[0] = (char*)"";
            argv[1] = NULL;
        }
    }
    else
    {   // Set up the full path to the requested shell.
        strcpy(shell, SYSSHELLPATH);
        if (shell[strlen(shell) - 1] != '/')
        {   // append slash if we don't have one yet
            strcat(shell, "/");
        }
        if (strlen(environment) == 0 || Utilities::strCaselessCompare("command", environment) == 0)
        {   // for environments "" and "command" we use "sh" as shell
            strcat(shell, "sh");
        }
        else
        {   // for all other environment names we use this name in lower case
            strcat(shell, environment);
            Utilities::strlower(shell);
        }

        argv[0] = (char*)shell;
        argv[1] = (char*)"-c";
        argv[2] = (char*)commandString;
        argv[3] = NULL;
    }

    if (ioContext->IsRedirectionRequested())
    {
        InputWriterThread inputThread; // separate thread if we need to write INPUT
        ErrorReaderThread errorThread; // separate thread if we need to read ERROR
        bool need_to_write_input = false;
        bool need_to_read_output = false;
        bool need_to_read_error = false;
        int input[2], output[2], error[2];

        posix_spawn_file_actions_t action;
        posix_spawn_file_actions_init(&action);

        // ignore SIGPIPE so that we receive EPIPE for a write() operation on a
        // pipe which has closed its read end.
        if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
        {
            return ErrorRedirection(context, errno);
        }

        // Create stdin, stdout, and stderr pipes as requested.  pipe() returns
        // two file descriptors: [0] is the pipe read end, [1] is the pipe
        // write end.  The child process (i. e. the command to be run) will
        // inherit both the read and write end of all created pipes.  Of each
        // end *we* will only ever use one end, and the child the other end.
        // Therefore we and the child will close any unused ends.  In addition,
        // we also prepare any file actions that are to be be done by the child.
        // These actions are collected with posix_spawn_file_actions()

        // is stdin redirection requested?
        if (ioContext->IsInputRedirected())
        {
            if (pipe(input) != 0) // create stdin pipe
            {
                return ErrorRedirection(context, errno);
            }
            posix_spawn_file_actions_adddup2(&action, input[0], 0); // stdin reads from pipe
            posix_spawn_file_actions_addclose(&action, input[1]); // close unused write end in child
            need_to_write_input = true;
        }

        // is stdout redirection requested?
        if (ioContext->IsOutputRedirected())
        {
            if (pipe(output) != 0) // create stdout pipe
            {
                return ErrorRedirection(context, errno);
            }
            // do we want interleaved stdout and stderr redirection?
            // this is a special case .. we just redirect stderr to stdout upfront
            if (ioContext->AreOutputAndErrorSameTarget())
            {
                posix_spawn_file_actions_adddup2(&action, output[1], 2); //stderr writes to pipe
            }
            posix_spawn_file_actions_adddup2(&action, output[1], 1); //stdout writes to pipe
            posix_spawn_file_actions_addclose(&action, output[0]); // close unused read end in child
            need_to_read_output = true;
        }

        // now stderr redirection
        // if both stdout and stderr are to be redirected to the same object, then
        // everything was already set up in the previous stdout redirection step
        if (ioContext->IsErrorRedirected() && !ioContext->AreOutputAndErrorSameTarget())
        {
            if (pipe(error) != 0) // create stderr pipe
            {
                return ErrorRedirection(context, errno);
            }
            posix_spawn_file_actions_adddup2(&action, error[1], 2); // stderr writes to pipe
            posix_spawn_file_actions_addclose(&action, error[0]); // close unused read end in child
            need_to_read_error = true;
        }

        // ok, everything is set up accordingly, let's fork the redirected command
        if (posix_spawnp(&pid, argv[0], &action, NULL, argv, getEnvironment()) != 0)
            {
                return ErrorFailure(context, commandString);
            }

        // Close all unneeded read- and write ends
        if (need_to_write_input)
        {
            close(input[0]); // we close our unused stdout pipe write end
        }
        if (need_to_read_output)
        {
            close(output[1]); // we close our unused stdout pipe write end
        }
        if (need_to_read_error)
        {
            close(error[1]); // we close our unused stderr pipe write end
        }

        if (need_to_write_input)
        {
            ioContext->ReadInputBuffer(&inputThread.inputBuffer, &inputThread.bufferLength);
            // we start a separate thread to write INPUT data to the input pipe
            inputThread.start(input[1]);
        }
        if (need_to_read_error)
        {
            // we start a separate thread to read ERROR data from the error pipe
            errorThread.start(error[0]);
        }

        // do we need to read OUTPUT from the stdout pipe?
        // we run this in the main thread instead of starting another new thread
        // if we have stdout and sterr interleaved, we'll read both of them here
        if (need_to_read_output)
        {
            // According to POSIX.1 PIPE_BUF is at least 512 bytes; on Linux
            // it's 4096 bytes.
            char outputBuffer[PIPE_BUF];
            ssize_t outputLength;

            while ((outputLength = read(output[0], &outputBuffer, PIPE_BUF)) > 0)
            {
                ioContext->WriteOutputBuffer(outputBuffer, outputLength);
            }
            if (outputLength != 0) // 0 is EOF, success
            {
                return ErrorRedirection(context, errno);
            }
            close(output[0]);
        }

        // did we start an ERROR thrad?
        if (need_to_read_error)
        {
            // wait for the ERROR thread to finish
            errorThread.waitForTermination();
            if (errorThread.bufferLength > 0)
            {   // return what the ERROR thread read from its pipe
                ioContext->WriteErrorBuffer(errorThread.errorBuffer, errorThread.bufferLength);
            }
            // the ERROR thread may have encountered an error .. raise it now
            if (errorThread.error != 0)
            {
                return ErrorRedirection(context, errorThread.error);
            }
        }

        // if we started an INPUT thread, wait until it finishes
        if (need_to_write_input)
        {
            inputThread.waitForTermination();
            // the INPUT thread may have encountered an error .. raise it now
            if (inputThread.error != 0)
            {
                return ErrorRedirection(context, inputThread.error);
            }
        }

        posix_spawn_file_actions_destroy(&action);
    }
    else // no redirection requested
    {
        // try to handle special commands like 'cd' or 'export' internally
        RexxObjectPtr rc = NULLOBJECT;

        if (handleCommandInternally(context, (char*)commandString, rc))
        {   // the command was handled in this thread
            return rc;
        }
        else
        {   // to run the command we spawn another thread
            if (posix_spawnp(&pid, argv[0], NULL, NULL, argv, getEnvironment()) != 0)
            {
                return ErrorFailure(context, commandString);
            }
        }
    }

    // wait for our child, the forked command
    waitpid(pid, &status, 0);
    int rc;
    if (WIFEXITED(status)) // child process ended normal
    {
        rc = WEXITSTATUS(status); // set return code
    }
    else
    {   // child process died
        rc = -(WTERMSIG(status));
        if (rc == 1) // process was stopped
        {
            rc = -1;
        }
    }

    // unknown command code?
    // a FAILURE will be raised for any command returning 127
    if (rc == UNKNOWN_COMMAND)
    {
        return ErrorFailure(context, commandString);
    }
    else if (rc != 0)
    {
        // for any non-zero return code we raise an ERROR condition
        context->RaiseCondition("ERROR", context->String(commandString),
          NULL, context->WholeNumberToObject(rc));
        return NULLOBJECT;
    }
    return context->False(); // zero return code
}
Exemplo n.º 22
0
static int dropbox_update(void *data)
{
	struct dropbox_section *section = data;
	char *argv[] = {"dropbox.py", "status", NULL};
	posix_spawn_file_actions_t file_actions;
	posix_spawnattr_t attr;
	int status = -1;
	sigset_t mask;
	int pipefd[2] = {-1, -1};
	pid_t pid;
	int ret;

	section->running = false;

	errno = posix_spawn_file_actions_init(&file_actions);
	if (errno) {
		perror("posix_spawn_file_actions_init");
		return -1;
	}

	errno = posix_spawnattr_init(&attr);
	if (errno) {
		perror("posix_spawnattr_init");
		goto out_file_actions;
	}

	ret = pipe(pipefd);
	if (ret) {
		perror("pipe2");
		goto out_spawnattr;
	}

	errno = posix_spawn_file_actions_addclose(&file_actions, pipefd[0]);
	if (errno) {
		perror("posix_spawn_file_actions_addclose");
		goto out;
	}

	errno = posix_spawn_file_actions_adddup2(&file_actions, pipefd[1],
						 STDOUT_FILENO);
	if (errno) {
		perror("posix_spawn_file_actions_adddup2");
		goto out;
	}

	sigemptyset(&mask);
	errno = posix_spawnattr_setsigmask(&attr, &mask);
	if (errno) {
		perror("posix_spawnattr_setsigmask");
		goto out;
	}

	errno = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK);
	if (errno) {
		perror("posix_spawnattr_setflags");
		goto out;
	}

	errno = posix_spawnp(&pid, "dropbox-cli", &file_actions, &attr, argv,
			     environ);
	if (errno) {
		perror("posix_spawnp");
		status = 0;
		goto out;
	}

	close(pipefd[1]);
	ret = read_all_output(section, pid, pipefd[0]);
	if (ret) {
		if (errno == ENOMEM)
			status = -1;
		else
			status = 0;
		goto out;
	}

	if (strcmp(section->status.buf, "Dropbox isn't running!\n") != 0)
		section->running = true;

	if (strcmp(section->status.buf, "Up to date\n") == 0 ||
	    strcmp(section->status.buf, "Idle\n") == 0)
		section->uptodate = true;
	else
		section->uptodate = false;

	status = 0;
out:
	close(pipefd[0]);
	close(pipefd[1]);
out_spawnattr:
	posix_spawnattr_destroy(&attr);
out_file_actions:
	posix_spawn_file_actions_destroy(&file_actions);
	return status;
}
Exemplo n.º 23
0
pid_t spawn (boost::asio::io_service& io_service,
		const child_options& opt,
		std::map<stream_id, boost::shared_ptr<stream_descriptor> >& streams,
		boost::system::error_code& ec)
{
	typedef	std::map<stream_id, child_options::stream_behaviour> beh_t;

	posix_spawn_file_actions_t file_actions;
	posix_spawn_file_actions_init(&file_actions);

	streams.clear();

	std::vector<int> close_later;

	const beh_t& behaviours = opt.get_stream_behaviours();
	for (beh_t::const_iterator iter = behaviours.begin(); iter != behaviours.end(); ++iter)
	{
		switch (iter->second)
		{
			case child_options::close:
			{
				posix_spawn_file_actions_addclose(&file_actions, iter->first.native());
				break;
			}

			case child_options::inherit:
			{
				posix_spawn_file_actions_addinherit_np(&file_actions, iter->first.native());
				break;
			}

			case child_options::pipe_read:
			{
				posix::pipe p;
				close_later.push_back(p.read_end());
				posix_spawn_file_actions_adddup2(&file_actions,
						p.steal_read_end(),
						iter->first.native());
				streams.insert(
							std::make_pair(
								iter->first,
								boost::shared_ptr<stream_descriptor>(
									new stream_descriptor(io_service, p.steal_write_end()))
							)
						);
				break;
			}

			case child_options::pipe_write:
			{
				posix::pipe p;
				close_later.push_back(p.write_end());
				posix_spawn_file_actions_adddup2(&file_actions,
						p.steal_write_end(),
						iter->first.native());
				streams.insert(
							std::make_pair(
								iter->first,
								boost::shared_ptr<stream_descriptor>(
									new stream_descriptor(io_service, p.steal_read_end()))
							)
						);
				break;
			}
		}
	}

	zero_terminated_list_of_strings argv;
	argv.add(opt.get_command());
	argv.add(opt.get_args());

	pid_t child_pid = -1;

	const char* _path = opt.get_command().c_str();
	char* const* _argv = argv.data();
	int result = posix_spawn(&child_pid,
							 _path,
							 &file_actions,
							 NULL,
							 _argv,
							 NULL);

	posix_spawn_file_actions_destroy(&file_actions);

	std::for_each(close_later.begin(), close_later.end(), ::close);

	ec.assign(result, boost::system::generic_category());
	return child_pid;
}
Exemplo n.º 24
0
Arquivo: spawnvex.c Projeto: att/ast
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
}
Exemplo n.º 25
0
RTR3DECL(int)   RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
                               PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
                               const char *pszPassword, PRTPROCESS phProcess)
{
    int rc;

    /*
     * Input validation
     */
    AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
    AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
    AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
    AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
    AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
    AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
    AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
    AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
    AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
    AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
#if defined(RT_OS_OS2)
    if (fFlags & RTPROC_FLAGS_DETACHED)
        return VERR_PROC_DETACH_NOT_SUPPORTED;
#endif

    /*
     * Get the file descriptors for the handles we've been passed.
     */
    PCRTHANDLE  paHandles[3] = { phStdIn, phStdOut, phStdErr };
    int         aStdFds[3]   = {      -1,       -1,       -1 };
    for (int i = 0; i < 3; i++)
    {
        if (paHandles[i])
        {
            AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
            switch (paHandles[i]->enmType)
            {
                case RTHANDLETYPE_FILE:
                    aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
                               ? (int)RTFileToNative(paHandles[i]->u.hFile)
                               : -2 /* close it */;
                    break;

                case RTHANDLETYPE_PIPE:
                    aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
                               ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
                               : -2 /* close it */;
                    break;

                case RTHANDLETYPE_SOCKET:
                    aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
                               ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
                               : -2 /* close it */;
                    break;

                default:
                    AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
            }
            /** @todo check the close-on-execness of these handles?  */
        }
    }

    for (int i = 0; i < 3; i++)
        if (aStdFds[i] == i)
            aStdFds[i] = -1;

    for (int i = 0; i < 3; i++)
        AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
                        ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
                        VERR_NOT_SUPPORTED);

    /*
     * Resolve the user id if specified.
     */
    uid_t uid = ~(uid_t)0;
    gid_t gid = ~(gid_t)0;
    if (pszAsUser)
    {
        rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid);
        if (RT_FAILURE(rc))
            return rc;
    }

    /*
     * Create the child environment if either RTPROC_FLAGS_PROFILE or
     * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect.
     */
    RTENV hEnvToUse = hEnv;
    if (   (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE))
        && (   (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
            || hEnv == RTENV_DEFAULT) )
    {
        if (fFlags & RTPROC_FLAGS_PROFILE)
            rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser);
        else
            rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
        if (RT_SUCCESS(rc))
        {
            if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT)
                rc = RTEnvApplyChanges(hEnvToUse, hEnv);
            if (RT_FAILURE(rc))
                RTEnvDestroy(hEnvToUse);
        }
        if (RT_FAILURE(rc))
            return rc;
    }

    /*
     * Check for execute access to the file.
     */
    char szRealExec[RTPATH_MAX];
    if (access(pszExec, X_OK))
    {
        rc = errno;
        if (   !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
            || rc != ENOENT
            || RTPathHavePath(pszExec) )
            rc = RTErrConvertFromErrno(rc);
        else
        {
            /* search */
            char *pszPath = RTEnvDupEx(hEnvToUse, "PATH");
            rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
            RTStrFree(pszPath);
            if (RT_SUCCESS(rc))
                pszExec = szRealExec;
            else
                rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc;
        }

        if (RT_FAILURE(rc))
            return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv);
    }

    pid_t pid = -1;
    const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse);
    AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv));


    /*
     * Take care of detaching the process.
     *
     * HACK ALERT! Put the process into a new process group with pgid = pid
     * to make sure it differs from that of the parent process to ensure that
     * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide
     * waits. setsid() includes the setpgid() functionality.
     * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt.
     */
#ifndef RT_OS_OS2
    if (fFlags & RTPROC_FLAGS_DETACHED)
    {
# ifdef RT_OS_SOLARIS
        int templateFd = -1;
        if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
        {
            templateFd = rtSolarisContractPreFork();
            if (templateFd == -1)
                return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
        }
# endif /* RT_OS_SOLARIS */
        pid = fork();
        if (!pid)
        {
# ifdef RT_OS_SOLARIS
            if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
                rtSolarisContractPostForkChild(templateFd);
# endif
            setsid(); /* see comment above */

            pid = -1;
            /* Child falls through to the actual spawn code below. */
        }
        else
        {
# ifdef RT_OS_SOLARIS
            if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
                rtSolarisContractPostForkParent(templateFd, pid);
# endif
            if (pid > 0)
            {
                /* Must wait for the temporary process to avoid a zombie. */
                int status = 0;
                pid_t pidChild = 0;

                /* Restart if we get interrupted. */
                do
                {
                    pidChild = waitpid(pid, &status, 0);
                } while (   pidChild == -1
                         && errno == EINTR);

                /* Assume that something wasn't found. No detailed info. */
                if (status)
                    return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv);
                if (phProcess)
                    *phProcess = 0;
                return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
            }
            return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
        }
    }
#endif

    /*
     * Spawn the child.
     *
     * Any spawn code MUST not execute any atexit functions if it is for a
     * detached process. It would lead to running the atexit functions which
     * make only sense for the parent. libORBit e.g. gets confused by multiple
     * execution. Remember, there was only a fork() so far, and until exec()
     * is successfully run there is nothing which would prevent doing anything
     * silly with the (duplicated) file descriptors.
     */
#ifdef HAVE_POSIX_SPAWN
    /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize.  */
    if (   uid == ~(uid_t)0
        && gid == ~(gid_t)0)
    {
        /* Spawn attributes. */
        posix_spawnattr_t Attr;
        rc = posix_spawnattr_init(&Attr);
        if (!rc)
        {
            /* Indicate that process group and signal mask are to be changed,
               and that the child should use default signal actions. */
            rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
            Assert(rc == 0);

            /* The child starts in its own process group. */
            if (!rc)
            {
                rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
                Assert(rc == 0);
            }

            /* Unmask all signals. */
            if (!rc)
            {
                sigset_t SigMask;
                sigemptyset(&SigMask);
                rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0);
            }

            /* File changes. */
            posix_spawn_file_actions_t  FileActions;
            posix_spawn_file_actions_t *pFileActions = NULL;
            if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc)
            {
                rc = posix_spawn_file_actions_init(&FileActions);
                if (!rc)
                {
                    pFileActions = &FileActions;
                    for (int i = 0; i < 3; i++)
                    {
                        int fd = aStdFds[i];
                        if (fd == -2)
                            rc = posix_spawn_file_actions_addclose(&FileActions, i);
                        else if (fd >= 0 && fd != i)
                        {
                            rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
                            if (!rc)
                            {
                                for (int j = i + 1; j < 3; j++)
                                    if (aStdFds[j] == fd)
                                    {
                                        fd = -1;
                                        break;
                                    }
                                if (fd >= 0)
                                    rc = posix_spawn_file_actions_addclose(&FileActions, fd);
                            }
                        }
                        if (rc)
                            break;
                    }
                }
            }

            if (!rc)
                rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
                                 (char * const *)papszEnv);

            /* cleanup */
            int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
            if (pFileActions)
            {
                rc2 = posix_spawn_file_actions_destroy(pFileActions);
                Assert(rc2 == 0);
            }

            /* return on success.*/
            if (!rc)
            {
                /* For a detached process this happens in the temp process, so
                 * it's not worth doing anything as this process must exit. */
                if (fFlags & RTPROC_FLAGS_DETACHED)
                    _Exit(0);
                if (phProcess)
                    *phProcess = pid;
                return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
            }
        }
        /* For a detached process this happens in the temp process, so
         * it's not worth doing anything as this process must exit. */
        if (fFlags & RTPROC_FLAGS_DETACHED)
            _Exit(124);
    }
    else
#endif
    {
#ifdef RT_OS_SOLARIS
        int templateFd = -1;
        if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
        {
            templateFd = rtSolarisContractPreFork();
            if (templateFd == -1)
                return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
        }
#endif /* RT_OS_SOLARIS */
        pid = fork();
        if (!pid)
        {
#ifdef RT_OS_SOLARIS
            if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
                rtSolarisContractPostForkChild(templateFd);
#endif /* RT_OS_SOLARIS */
            if (!(fFlags & RTPROC_FLAGS_DETACHED))
                setpgid(0, 0); /* see comment above */

            /*
             * Change group and user if requested.
             */
#if 1 /** @todo This needs more work, see suplib/hardening. */
            if (pszAsUser)
            {
                int ret = initgroups(pszAsUser, gid);
                if (ret)
                {
                    if (fFlags & RTPROC_FLAGS_DETACHED)
                        _Exit(126);
                    else
                        exit(126);
                }
            }
            if (gid != ~(gid_t)0)
            {
                if (setgid(gid))
                {
                    if (fFlags & RTPROC_FLAGS_DETACHED)
                        _Exit(126);
                    else
                        exit(126);
                }
            }

            if (uid != ~(uid_t)0)
            {
                if (setuid(uid))
                {
                    if (fFlags & RTPROC_FLAGS_DETACHED)
                        _Exit(126);
                    else
                        exit(126);
                }
            }
#endif

            /*
             * Some final profile environment tweaks, if running as user.
             */
            if (   (fFlags & RTPROC_FLAGS_PROFILE)
                && pszAsUser
                && (   (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
                    || hEnv == RTENV_DEFAULT) )
            {
                rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv);
                papszEnv = RTEnvGetExecEnvP(hEnvToUse);
                if (RT_FAILURE(rc) || !papszEnv)
                {
                    if (fFlags & RTPROC_FLAGS_DETACHED)
                        _Exit(126);
                    else
                        exit(126);
                }
            }

            /*
             * Unset the signal mask.
             */
            sigset_t SigMask;
            sigemptyset(&SigMask);
            rc = sigprocmask(SIG_SETMASK, &SigMask, NULL);
            Assert(rc == 0);

            /*
             * Apply changes to the standard file descriptor and stuff.
             */
            for (int i = 0; i < 3; i++)
            {
                int fd = aStdFds[i];
                if (fd == -2)
                    close(i);
                else if (fd >= 0)
                {
                    int rc2 = dup2(fd, i);
                    if (rc2 != i)
                    {
                        if (fFlags & RTPROC_FLAGS_DETACHED)
                            _Exit(125);
                        else
                            exit(125);
                    }
                    for (int j = i + 1; j < 3; j++)
                        if (aStdFds[j] == fd)
                        {
                            fd = -1;
                            break;
                        }
                    if (fd >= 0)
                        close(fd);
                }
            }

            /*
             * Finally, execute the requested program.
             */
            rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
            if (errno == ENOEXEC)
            {
                /* This can happen when trying to start a shell script without the magic #!/bin/sh */
                RTAssertMsg2Weak("Cannot execute this binary format!\n");
            }
            else
                RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno);
            RTAssertReleasePanic();
            if (fFlags & RTPROC_FLAGS_DETACHED)
                _Exit(127);
            else
                exit(127);
        }
#ifdef RT_OS_SOLARIS
        if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
            rtSolarisContractPostForkParent(templateFd, pid);
#endif /* RT_OS_SOLARIS */
        if (pid > 0)
        {
            /* For a detached process this happens in the temp process, so
             * it's not worth doing anything as this process must exit. */
            if (fFlags & RTPROC_FLAGS_DETACHED)
                _Exit(0);
            if (phProcess)
                *phProcess = pid;
            return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
        }
        /* For a detached process this happens in the temp process, so
         * it's not worth doing anything as this process must exit. */
        if (fFlags & RTPROC_FLAGS_DETACHED)
            _Exit(124);
        return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
    }

    return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv);
}
Exemplo n.º 26
0
/**
 * Runs an executable in another process (or fork the current process)
 * and return immediately.
 *
 * This function launches an executable and returns immediately, while letting
 * the sub-process to run asynchronously.
 *
 * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
 * on other platforms.
 *
 * On Unix, a pointer of function can be provided to run in the child process,
 * without exec()'ing a new executable.
 *
 * @param pfnMain the function to run in the child process (Unix only).
 * @param papszArgv argument list of the executable to run. papszArgv[0] is the
 *                  name of the executable.
 * @param bCreateInputPipe set to TRUE to create a pipe for the child input stream.
 * @param bCreateOutputPipe set to TRUE to create a pipe for the child output stream.
 * @param bCreateErrorPipe set to TRUE to create a pipe for the child error stream.
 *
 * @return a handle, that must be freed with CPLSpawnAsyncFinish()
 *
 * @since GDAL 1.10.0
 */
CPLSpawnedProcess* CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
                                 const char * const papszArgv[],
                                 int bCreateInputPipe,
                                 int bCreateOutputPipe,
                                 int bCreateErrorPipe,
                                 CPL_UNUSED char** papszOptions)
{
    int pipe_in[2] = { -1, -1 };
    int pipe_out[2] = { -1, -1 };
    int pipe_err[2] = { -1, -1 };
    bool bDup2In = CPL_TO_BOOL(bCreateInputPipe);
    bool bDup2Out = CPL_TO_BOOL(bCreateOutputPipe);
    bool bDup2Err = CPL_TO_BOOL(bCreateErrorPipe);

    if ((bCreateInputPipe && pipe(pipe_in)) ||
        (bCreateOutputPipe && pipe(pipe_out)) ||
        (bCreateErrorPipe && pipe(pipe_err)))
    {
        CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
        return NULL;
    }

    char** papszArgvDup = CSLDuplicate( const_cast<char **>( papszArgv ) );

    /* If we don't do any file actions, posix_spawnp() might be implemented */
    /* efficiently as a vfork()/exec() pair (or if it is not available, we */
    /* can use vfork()/exec()), so if the child is cooperative */
    /* we pass the pipe handles as commandline arguments */
    if( papszArgv != NULL )
    {
        for( int i=0; papszArgvDup[i] != NULL; i++ )
        {
            if( bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0 )
            {
                CPLFree(papszArgvDup[i]);
                papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
                    pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
                bDup2In = false;
            }
            else if( bCreateOutputPipe && strcmp(papszArgvDup[i], "{pipe_out}") == 0 )
            {
                CPLFree(papszArgvDup[i]);
                papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
                    pipe_out[OUT_FOR_PARENT], pipe_out[IN_FOR_PARENT]));
                bDup2Out = false;
            }
            else if( bCreateErrorPipe && strcmp(papszArgvDup[i], "{pipe_err}") == 0 )
            {
                CPLFree(papszArgvDup[i]);
                papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
                    pipe_err[OUT_FOR_PARENT], pipe_err[IN_FOR_PARENT]));
                bDup2Err = false;
            }
        }
    }

#ifdef HAVE_POSIX_SPAWNP
    if( papszArgv != NULL )
    {
        bool bHasActions = false;
        posix_spawn_file_actions_t actions;

        if( bDup2In )
        {
            if( !bHasActions ) posix_spawn_file_actions_init(&actions);
            posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT], fileno(stdin));
            posix_spawn_file_actions_addclose(&actions, pipe_in[OUT_FOR_PARENT]);
            bHasActions = true;
        }

        if( bDup2Out )
        {
            if( !bHasActions ) posix_spawn_file_actions_init(&actions);
            posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT], fileno(stdout));
            posix_spawn_file_actions_addclose(&actions, pipe_out[IN_FOR_PARENT]);
            bHasActions = true;
        }

        if( bDup2Err )
        {
            if( !bHasActions ) posix_spawn_file_actions_init(&actions);
            posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT], fileno(stderr));
            posix_spawn_file_actions_addclose(&actions, pipe_err[IN_FOR_PARENT]);
            bHasActions = true;
        }

        pid_t pid;
        if( posix_spawnp(&pid, papszArgvDup[0],
                         bHasActions ? &actions : NULL,
                         NULL,
                         reinterpret_cast<char* const*>( papszArgvDup ),
                         environ) != 0 )
        {
            if( bHasActions )
                posix_spawn_file_actions_destroy(&actions);
            CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
            goto err;
        }

        CSLDestroy(papszArgvDup);

        /* Close unused end of pipe */
        if( bCreateInputPipe )
            close(pipe_in[IN_FOR_PARENT]);
        if( bCreateOutputPipe )
            close(pipe_out[OUT_FOR_PARENT]);
        if( bCreateErrorPipe )
            close(pipe_err[OUT_FOR_PARENT]);

        /* Ignore SIGPIPE */
    #ifdef SIGPIPE
        std::signal( SIGPIPE, SIG_IGN );
    #endif
        CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
            CPLMalloc( sizeof(CPLSpawnedProcess) ) );
        if( bHasActions )
            memcpy(&p->actions, &actions, sizeof(actions));
        p->bFreeActions = bHasActions;
        p->pid = pid;
        p->fin = pipe_out[IN_FOR_PARENT];
        p->fout = pipe_in[OUT_FOR_PARENT];
        p->ferr = pipe_err[IN_FOR_PARENT];
        return p;
    }
#endif // #ifdef HAVE_POSIX_SPAWNP

    pid_t pid;
#ifdef HAVE_VFORK
    /* coverity[dead_error_line] */
    if( papszArgv != NULL && !bDup2In && !bDup2Out && !bDup2Err )
    {
        /* Workaround clang static analyzer warning about unsafe use of vfork */
        pid_t (*p_vfork)(void) = vfork;
        pid = p_vfork();
    }
    else
#endif
        pid = fork();
    if (pid == 0)
    {
        /* Close unused end of pipe */
        if( bDup2In )
            close(pipe_in[OUT_FOR_PARENT]);
        if( bDup2Out )
            close(pipe_out[IN_FOR_PARENT]);
        if( bDup2Err )
            close(pipe_err[IN_FOR_PARENT]);

#ifndef HAVE_POSIX_SPAWNP
        if( papszArgv != NULL )
        {
            if( bDup2In )
                dup2(pipe_in[IN_FOR_PARENT], fileno(stdin));
            if( bDup2Out )
                dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout));
            if( bDup2Err )
                dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr));

            execvp(papszArgvDup[0], (char* const*) papszArgvDup);

            _exit(1);
        }
        else
#endif // HAVE_POSIX_SPAWNP
        {
            if( bCreateErrorPipe )
                close(pipe_err[OUT_FOR_PARENT]);

            int nRet = 0;
            if (pfnMain != NULL)
                nRet = pfnMain((bCreateInputPipe) ? pipe_in[IN_FOR_PARENT] : fileno(stdin),
                               (bCreateOutputPipe) ? pipe_out[OUT_FOR_PARENT] : fileno(stdout));
            _exit(nRet);
        }
    }
    else if( pid > 0 )
    {
        CSLDestroy(papszArgvDup);

        /* Close unused end of pipe */
        if( bCreateInputPipe )
            close(pipe_in[IN_FOR_PARENT]);
        if( bCreateOutputPipe )
            close(pipe_out[OUT_FOR_PARENT]);
        if( bCreateErrorPipe )
            close(pipe_err[OUT_FOR_PARENT]);

        /* Ignore SIGPIPE */
#ifdef SIGPIPE
        std::signal( SIGPIPE, SIG_IGN );
#endif
        CPLSpawnedProcess* p = static_cast<CPLSpawnedProcess *>(
            CPLMalloc( sizeof(CPLSpawnedProcess) ) );
#ifdef HAVE_POSIX_SPAWNP
        p->bFreeActions = FALSE;
#endif
        p->pid = pid;
        p->fin = pipe_out[IN_FOR_PARENT];
        p->fout = pipe_in[OUT_FOR_PARENT];
        p->ferr = pipe_err[IN_FOR_PARENT];
        return p;
    }
    else
    {
        CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
        goto err;
    }

err:
    CSLDestroy(papszArgvDup);
    for( int i = 0; i < 2; i++ )
    {
        if (pipe_in[i] >= 0)
            close(pipe_in[i]);
        if (pipe_out[i] >= 0)
            close(pipe_out[i]);
        if (pipe_err[i] >= 0)
            close(pipe_err[i]);
    }

    return NULL;
}
Exemplo n.º 27
0
int
main ()
{
  char *argv[3] = { "/bin/sh", CHILD_PROGRAM_FILENAME, NULL };
  int ofd[2];
  sigset_t blocked_signals;
  sigset_t fatal_signal_set;
  posix_spawn_file_actions_t actions;
  bool actions_allocated;
  posix_spawnattr_t attrs;
  bool attrs_allocated;
  int err;
  pid_t child;
  int fd;
  FILE *fp;
  int written;
  int status;
  int exitstatus;

  if (pipe (ofd) < 0 || (ofd[1] = fd_safer (ofd[1])) < 0)
    {
      perror ("cannot create pipe");
      exit (1);
    }
  sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
  sigemptyset (&fatal_signal_set);
  sigaddset (&fatal_signal_set, SIGINT);
  sigaddset (&fatal_signal_set, SIGTERM);
  sigaddset (&fatal_signal_set, SIGHUP);
  sigaddset (&fatal_signal_set, SIGPIPE);
  sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
  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, ofd[0], STDIN_FILENO)) != 0
          || (err = posix_spawn_file_actions_addclose (&actions, ofd[0])) != 0
          || (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) != 0
          || (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, "/bin/sh", &actions, &attrs, argv, environ)) != 0))
    {
      if (actions_allocated)
        posix_spawn_file_actions_destroy (&actions);
      if (attrs_allocated)
        posix_spawnattr_destroy (&attrs);
      sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
      errno = err;
      perror ("subprocess failed");
      exit (1);
    }
  posix_spawn_file_actions_destroy (&actions);
  posix_spawnattr_destroy (&attrs);
  sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
  close (ofd[0]);
  fd = ofd[1];
  fp = fdopen (fd, "w");
  if (fp == NULL)
    {
      fprintf (stderr, "fdopen() failed\n");
      exit (1);
    }
  written = fwrite ("Halle Potta\n", 1, 12, fp);
  if (written < 12)
    {
      fprintf (stderr, "could not write input\n");
      exit (1);
    }
  fclose (fp);
  status = 0;
  while (waitpid (child, &status, 0) != child)
    ;
  if (!WIFEXITED (status))
    {
      fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
      exit (1);
    }
  exitstatus = WEXITSTATUS (status);
  if (exitstatus != 0)
    {
      fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
      exit (1);
    }
  return 0;
}
Exemplo n.º 28
0
int
unix_system (array<string> arg,
	     array<int> fd_in, array<string> str_in,
	     array<int> fd_out, array<string*> str_out) {
  // Run command arg[0] with arguments arg[i], i >= 1.
  // str_in[i] is sent to the file descriptor fd_in[i].
  // str_out[i] is filled from the file descriptor fd_out[i].
  // If str_in[i] is -1 then $$i automatically replaced by a valid
  // file descriptor in arg.
  if (N(arg) == 0) return 0;
  string which= recompose (arg, " ");
  int n_in= N(fd_in), n_out= N(fd_out);
  ASSERT(N(str_in)  == n_in, "size mismatch");
  ASSERT(N(str_out) == n_out, "size mismatch");
  array<_pipe_t> pp_in (n_in), pp_out (n_out);
  _file_actions_t file_actions;
  for (int i= 0; i < n_in; i++) {
    if (posix_spawn_file_actions_addclose
	(&file_actions.rep, pp_in[i].out ()) != 0) return -1;
    if (fd_in[i] >= 0) {
      if (posix_spawn_file_actions_adddup2
	  (&file_actions.rep, pp_in[i].in (), fd_in[i]) != 0) return -1;
      if (posix_spawn_file_actions_addclose
	  (&file_actions.rep, pp_in[i].in ()) != 0) return -1; } }
  for (int i= 0; i < n_out; i++) {
    if (posix_spawn_file_actions_addclose
	(&file_actions.rep, pp_out[i].in ()) != 0) return -1;
    if (posix_spawn_file_actions_adddup2
	(&file_actions.rep, pp_out[i].out (), fd_out[i]) != 0) return -1;
    if (posix_spawn_file_actions_addclose
	(&file_actions.rep, pp_out[i].out ()) != 0) return -1; }
  array<string> arg_= arg;
  for (int j= 0; j < N(arg); j++)
    for (int i= 0; i < n_in; i++)
      if (fd_in[i] < 0)
        arg_[j]= replace (arg_[j], "$$" * as_string (i),
	  	          as_string (pp_in[i].in ()));
  if (DEBUG_IO)
    debug_io << "unix_system, launching: " << arg_ << "\n"; 
  array<char*> _arg;
  for (int j= 0; j < N(arg_); j++)
    _arg << as_charp (arg_[j]);
  _arg << (char*) NULL;
  pid_t pid;
  int status= posix_spawnp (&pid, _arg[0], &file_actions.rep, NULL,
			    A(_arg), environ);
  for (int j= 0; j < N(arg_); j++)
    tm_delete_array (_arg[j]);
  if (status != 0) {
    if (DEBUG_IO) debug_io << "unix_system, failed" << "\n";
    return -1;
  }
  if (DEBUG_IO)
    debug_io << "unix_system, succeeded to create pid "
	     << pid << "\n";

  // close useless ports
  for (int i= 0; i < n_in ; i++) close (pp_in[i].in ());
  for (int i= 0; i < n_out; i++) close (pp_out[i].out ());

  // write to spawn process
  array<_channel> channels_in (n_in);
  array<pthread_t> threads_write (n_in);
  for (int i= 0; i < n_in; i++) {
    channels_in[i]._init_in (pp_in[i].out (), str_in[i], 1 << 12);
    if (pthread_create (&threads_write[i], NULL /* &attr */,
			_background_write_task,
			(void *) &(channels_in[i])))
      return -1;
  }

  // read from spawn process
  array<_channel> channels_out (n_out);
  array<pthread_t> threads_read (n_out);
  for (int i= 0; i < n_out; i++) {
    channels_out[i]._init_out (pp_out[i].in (), 1 << 12); 
    if (pthread_create (&threads_read[i], NULL /* &attr */,
			_background_read_task,
			(void *) &(channels_out[i])))
      return -1;
  }

  int wret;
  time_t last_wait_time= texmacs_time ();
  while ((wret= waitpid (pid, &status, WNOHANG)) == 0) {
    usleep (100);
    if (texmacs_time () - last_wait_time > 5000) {
      last_wait_time= texmacs_time ();
      _unix_system_warn (pid, which, "waiting spawn process");
    }
  }
  if (DEBUG_IO)
    debug_io << "unix_system, pid " << pid << " terminated" << "\n"; 

  // wait for terminating threads
  void* exit_status;
  int thread_status= 0;
  for (int i= 0; i < n_in; i++) {
    pthread_join (threads_write[i], &exit_status);
    if (channels_in[i].status < 0) thread_status= -1;
  }
  for (int i= 0; i < n_out; i++) {
    pthread_join (threads_read[i], &exit_status);
    *(str_out[i])= string (channels_out[i].data.data (),
                           channels_out[i].data.length ());
    if (channels_out[i].status < 0) thread_status= -1;
  }

  if (thread_status < 0) return thread_status;
  if (wret < 0 || WIFEXITED(status) == 0) return -1;
  return WEXITSTATUS(status);
}
Exemplo n.º 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
}