11facb048SJean-Baptiste Boric /*
21facb048SJean-Baptiste Boric * Taken from newlib/libc/posix/posix_spawn.c
31facb048SJean-Baptiste Boric */
41facb048SJean-Baptiste Boric
51facb048SJean-Baptiste Boric /*-
61facb048SJean-Baptiste Boric * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
71facb048SJean-Baptiste Boric * All rights reserved.
81facb048SJean-Baptiste Boric *
91facb048SJean-Baptiste Boric * Redistribution and use in source and binary forms, with or without
101facb048SJean-Baptiste Boric * modification, are permitted provided that the following conditions
111facb048SJean-Baptiste Boric * are met:
121facb048SJean-Baptiste Boric * 1. Redistributions of source code must retain the above copyright
131facb048SJean-Baptiste Boric * notice, this list of conditions and the following disclaimer.
141facb048SJean-Baptiste Boric * 2. Redistributions in binary form must reproduce the above copyright
151facb048SJean-Baptiste Boric * notice, this list of conditions and the following disclaimer in the
161facb048SJean-Baptiste Boric * documentation and/or other materials provided with the distribution.
171facb048SJean-Baptiste Boric *
181facb048SJean-Baptiste Boric * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
191facb048SJean-Baptiste Boric * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201facb048SJean-Baptiste Boric * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211facb048SJean-Baptiste Boric * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
221facb048SJean-Baptiste Boric * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231facb048SJean-Baptiste Boric * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241facb048SJean-Baptiste Boric * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251facb048SJean-Baptiste Boric * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261facb048SJean-Baptiste Boric * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271facb048SJean-Baptiste Boric * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281facb048SJean-Baptiste Boric * SUCH DAMAGE.
291facb048SJean-Baptiste Boric */
301facb048SJean-Baptiste Boric
311facb048SJean-Baptiste Boric #include <sys/cdefs.h>
321facb048SJean-Baptiste Boric
331facb048SJean-Baptiste Boric #include <sys/queue.h>
341facb048SJean-Baptiste Boric #include <sys/wait.h>
351facb048SJean-Baptiste Boric
361facb048SJean-Baptiste Boric #include <errno.h>
371facb048SJean-Baptiste Boric #include <fcntl.h>
381facb048SJean-Baptiste Boric #include <sched.h>
391facb048SJean-Baptiste Boric #include <spawn.h>
401facb048SJean-Baptiste Boric #include <signal.h>
411facb048SJean-Baptiste Boric #include <stdlib.h>
421facb048SJean-Baptiste Boric #include <string.h>
431facb048SJean-Baptiste Boric #include <unistd.h>
441facb048SJean-Baptiste Boric
451facb048SJean-Baptiste Boric extern char **environ;
461facb048SJean-Baptiste Boric
471facb048SJean-Baptiste Boric /* Only deal with a pointer to environ, to work around subtle bugs with shared
481facb048SJean-Baptiste Boric libraries and/or small data systems where the user declares his own
491facb048SJean-Baptiste Boric 'environ'. */
501facb048SJean-Baptiste Boric static char ***p_environ = &environ;
511facb048SJean-Baptiste Boric
521facb048SJean-Baptiste Boric /*
531facb048SJean-Baptiste Boric * Spawn routines
541facb048SJean-Baptiste Boric */
551facb048SJean-Baptiste Boric
561facb048SJean-Baptiste Boric static int
process_spawnattr(const posix_spawnattr_t * sa)571facb048SJean-Baptiste Boric process_spawnattr(const posix_spawnattr_t * sa)
581facb048SJean-Baptiste Boric {
591facb048SJean-Baptiste Boric struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
601facb048SJean-Baptiste Boric int i;
611facb048SJean-Baptiste Boric
621facb048SJean-Baptiste Boric /*
631facb048SJean-Baptiste Boric * POSIX doesn't really describe in which order everything
641facb048SJean-Baptiste Boric * should be set. We'll just set them in the order in which they
651facb048SJean-Baptiste Boric * are mentioned.
661facb048SJean-Baptiste Boric */
671facb048SJean-Baptiste Boric
681facb048SJean-Baptiste Boric /* Set process group */
691facb048SJean-Baptiste Boric if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
701facb048SJean-Baptiste Boric if (setpgid(0, sa->sa_pgroup) != 0)
711facb048SJean-Baptiste Boric return errno;
721facb048SJean-Baptiste Boric }
731facb048SJean-Baptiste Boric
741facb048SJean-Baptiste Boric /* Set scheduler policy */
751facb048SJean-Baptiste Boric /* XXX: We don't have scheduler policy for now */
761facb048SJean-Baptiste Boric #if 0
771facb048SJean-Baptiste Boric if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
781facb048SJean-Baptiste Boric if (sched_setscheduler(0, sa->sa_schedpolicy,
791facb048SJean-Baptiste Boric &sa->sa_schedparam) != 0)
801facb048SJean-Baptiste Boric return errno;
811facb048SJean-Baptiste Boric } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
821facb048SJean-Baptiste Boric if (sched_setparam(0, &sa->sa_schedparam) != 0)
831facb048SJean-Baptiste Boric return errno;
841facb048SJean-Baptiste Boric }
851facb048SJean-Baptiste Boric #endif
861facb048SJean-Baptiste Boric
871facb048SJean-Baptiste Boric /* Reset user ID's */
881facb048SJean-Baptiste Boric if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
891facb048SJean-Baptiste Boric if (setegid(getgid()) != 0)
901facb048SJean-Baptiste Boric return errno;
911facb048SJean-Baptiste Boric if (seteuid(getuid()) != 0)
921facb048SJean-Baptiste Boric return errno;
931facb048SJean-Baptiste Boric }
941facb048SJean-Baptiste Boric
951facb048SJean-Baptiste Boric /* Set signal masks/defaults */
961facb048SJean-Baptiste Boric if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
971facb048SJean-Baptiste Boric sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
981facb048SJean-Baptiste Boric }
991facb048SJean-Baptiste Boric
1001facb048SJean-Baptiste Boric if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
1011facb048SJean-Baptiste Boric for (i = 1; i < NSIG; i++) {
1021facb048SJean-Baptiste Boric if (sigismember(&sa->sa_sigdefault, i))
1031facb048SJean-Baptiste Boric if (sigaction(i, &sigact, NULL) != 0)
1041facb048SJean-Baptiste Boric return errno;
1051facb048SJean-Baptiste Boric }
1061facb048SJean-Baptiste Boric }
1071facb048SJean-Baptiste Boric
1081facb048SJean-Baptiste Boric return 0;
1091facb048SJean-Baptiste Boric }
1101facb048SJean-Baptiste Boric
1111facb048SJean-Baptiste Boric static int
move_fd_up(int * statusfd)1121facb048SJean-Baptiste Boric move_fd_up(int * statusfd)
1131facb048SJean-Baptiste Boric {
1141facb048SJean-Baptiste Boric /*
1151facb048SJean-Baptiste Boric * Move given file descriptor on a higher fd number.
1161facb048SJean-Baptiste Boric *
1171facb048SJean-Baptiste Boric * This is used to hide the status file descriptor from the application
1181facb048SJean-Baptiste Boric * by pushing it out of the way if it tries to use its number.
1191facb048SJean-Baptiste Boric */
1201facb048SJean-Baptiste Boric int newstatusfd;
1211facb048SJean-Baptiste Boric
1221facb048SJean-Baptiste Boric newstatusfd = fcntl(*statusfd, F_DUPFD, *statusfd+1);
1231facb048SJean-Baptiste Boric if (newstatusfd == -1)
1241facb048SJean-Baptiste Boric return -1;
1251facb048SJean-Baptiste Boric
1261facb048SJean-Baptiste Boric close(*statusfd);
1271facb048SJean-Baptiste Boric *statusfd = newstatusfd;
1281facb048SJean-Baptiste Boric return 0;
1291facb048SJean-Baptiste Boric }
1301facb048SJean-Baptiste Boric
1311facb048SJean-Baptiste Boric static int
process_file_actions_entry(posix_spawn_file_actions_entry_t * fae,int * statusfd)1321facb048SJean-Baptiste Boric process_file_actions_entry(posix_spawn_file_actions_entry_t * fae,
1331facb048SJean-Baptiste Boric int * statusfd)
1341facb048SJean-Baptiste Boric {
1351facb048SJean-Baptiste Boric int fd;
1361facb048SJean-Baptiste Boric
1371facb048SJean-Baptiste Boric switch (fae->fae_action) {
1381facb048SJean-Baptiste Boric case FAE_OPEN:
1391facb048SJean-Baptiste Boric /* Perform an open(), make it use the right fd */
1401facb048SJean-Baptiste Boric fd = open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
1411facb048SJean-Baptiste Boric if (fd < 0)
1421facb048SJean-Baptiste Boric return errno;
1431facb048SJean-Baptiste Boric if (fd != fae->fae_fildes) {
1441facb048SJean-Baptiste Boric if (fae->fae_fildes == *statusfd) {
1451facb048SJean-Baptiste Boric /* Move the status fd out of the way */
1461facb048SJean-Baptiste Boric if (move_fd_up(statusfd) == -1)
1471facb048SJean-Baptiste Boric return errno;
1481facb048SJean-Baptiste Boric }
1491facb048SJean-Baptiste Boric if (dup2(fd, fae->fae_fildes) == -1)
1501facb048SJean-Baptiste Boric return errno;
1511facb048SJean-Baptiste Boric if (close(fd) != 0) {
1521facb048SJean-Baptiste Boric if (errno == EBADF)
1531facb048SJean-Baptiste Boric return EBADF;
1541facb048SJean-Baptiste Boric }
1551facb048SJean-Baptiste Boric }
1561facb048SJean-Baptiste Boric if (fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
1571facb048SJean-Baptiste Boric return errno;
1581facb048SJean-Baptiste Boric break;
1591facb048SJean-Baptiste Boric
1601facb048SJean-Baptiste Boric case FAE_DUP2:
1611facb048SJean-Baptiste Boric if (fae->fae_fildes == *statusfd) {
1621facb048SJean-Baptiste Boric /* Nice try */
1631facb048SJean-Baptiste Boric return EBADF;
1641facb048SJean-Baptiste Boric }
1651facb048SJean-Baptiste Boric if (fae->fae_newfildes == *statusfd) {
1661facb048SJean-Baptiste Boric /* Move the status file descriptor out of the way */
1671facb048SJean-Baptiste Boric if (move_fd_up(statusfd) == -1)
1681facb048SJean-Baptiste Boric return errno;
1691facb048SJean-Baptiste Boric }
1701facb048SJean-Baptiste Boric /* Perform a dup2() */
1711facb048SJean-Baptiste Boric if (dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
1721facb048SJean-Baptiste Boric return errno;
1731facb048SJean-Baptiste Boric if (fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
1741facb048SJean-Baptiste Boric return errno;
1751facb048SJean-Baptiste Boric break;
1761facb048SJean-Baptiste Boric
1771facb048SJean-Baptiste Boric case FAE_CLOSE:
1781facb048SJean-Baptiste Boric /* Perform a close(), do not fail if already closed */
1791facb048SJean-Baptiste Boric if (fae->fae_fildes != *statusfd)
1801facb048SJean-Baptiste Boric (void)close(fae->fae_fildes);
1811facb048SJean-Baptiste Boric break;
1821facb048SJean-Baptiste Boric }
1831facb048SJean-Baptiste Boric return 0;
1841facb048SJean-Baptiste Boric }
1851facb048SJean-Baptiste Boric
1861facb048SJean-Baptiste Boric static int
process_file_actions(const posix_spawn_file_actions_t * fa,int * statusfd)1871facb048SJean-Baptiste Boric process_file_actions(const posix_spawn_file_actions_t * fa, int * statusfd)
1881facb048SJean-Baptiste Boric {
1891facb048SJean-Baptiste Boric posix_spawn_file_actions_entry_t *fae;
1901facb048SJean-Baptiste Boric int error;
1911facb048SJean-Baptiste Boric
1921facb048SJean-Baptiste Boric /* Replay all file descriptor modifications */
1931facb048SJean-Baptiste Boric for (unsigned i = 0; i < fa->len; i++) {
1941facb048SJean-Baptiste Boric fae = &fa->fae[i];
1951facb048SJean-Baptiste Boric error = process_file_actions_entry(fae, statusfd);
1961facb048SJean-Baptiste Boric if (error)
1971facb048SJean-Baptiste Boric return error;
1981facb048SJean-Baptiste Boric }
1991facb048SJean-Baptiste Boric return 0;
2001facb048SJean-Baptiste Boric }
2011facb048SJean-Baptiste Boric
2021facb048SJean-Baptiste Boric int
posix_spawn(pid_t * __restrict pid,const char * __restrict path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * __restrict sa,char * const * __restrict argv,char * const * __restrict envp)2031facb048SJean-Baptiste Boric posix_spawn(pid_t * __restrict pid, const char * __restrict path,
2041facb048SJean-Baptiste Boric const posix_spawn_file_actions_t * fa,
2051facb048SJean-Baptiste Boric const posix_spawnattr_t * __restrict sa,
2061facb048SJean-Baptiste Boric char * const * __restrict argv, char * const * __restrict envp)
2071facb048SJean-Baptiste Boric {
2081facb048SJean-Baptiste Boric pid_t p;
2091facb048SJean-Baptiste Boric int r, error, pfd[2];
2101facb048SJean-Baptiste Boric
2111facb048SJean-Baptiste Boric /*
2121facb048SJean-Baptiste Boric * Due to the lack of vfork() in Minix, an alternative solution with
2131facb048SJean-Baptiste Boric * pipes is used. The writing end is set to close on exec() and the
2141facb048SJean-Baptiste Boric * parent performs a read() on it.
2151facb048SJean-Baptiste Boric *
2161facb048SJean-Baptiste Boric * On success, a successful 0-length read happens.
2171facb048SJean-Baptiste Boric * On failure, the child writes the errno to the pipe before exiting,
2181facb048SJean-Baptiste Boric * the error is thus transmitted to the parent.
2191facb048SJean-Baptiste Boric *
2201facb048SJean-Baptiste Boric * This solution was taken from stackoverflow.com question 3703013.
2211facb048SJean-Baptiste Boric */
2221facb048SJean-Baptiste Boric if (pipe(pfd) == -1)
2231facb048SJean-Baptiste Boric return errno;
2241facb048SJean-Baptiste Boric
2251facb048SJean-Baptiste Boric p = fork();
2261facb048SJean-Baptiste Boric switch (p) {
2271facb048SJean-Baptiste Boric case -1:
2281facb048SJean-Baptiste Boric close(pfd[0]);
2291facb048SJean-Baptiste Boric close(pfd[1]);
2301facb048SJean-Baptiste Boric
2311facb048SJean-Baptiste Boric return errno;
2321facb048SJean-Baptiste Boric
2331facb048SJean-Baptiste Boric case 0:
2341facb048SJean-Baptiste Boric close(pfd[0]);
2351facb048SJean-Baptiste Boric
2361facb048SJean-Baptiste Boric if (fcntl(pfd[1], F_SETFD, FD_CLOEXEC) != 0) {
2371facb048SJean-Baptiste Boric error = errno;
2381facb048SJean-Baptiste Boric break;
2391facb048SJean-Baptiste Boric }
2401facb048SJean-Baptiste Boric
2411facb048SJean-Baptiste Boric if (sa != NULL) {
2421facb048SJean-Baptiste Boric error = process_spawnattr(sa);
2431facb048SJean-Baptiste Boric if (error)
2441facb048SJean-Baptiste Boric break;
2451facb048SJean-Baptiste Boric }
2461facb048SJean-Baptiste Boric if (fa != NULL) {
2471facb048SJean-Baptiste Boric error = process_file_actions(fa, &pfd[1]);
2481facb048SJean-Baptiste Boric if (error)
2491facb048SJean-Baptiste Boric break;
2501facb048SJean-Baptiste Boric }
2511facb048SJean-Baptiste Boric
2521facb048SJean-Baptiste Boric (void)execve(path, argv, envp != NULL ? envp : *p_environ);
2531facb048SJean-Baptiste Boric
2541facb048SJean-Baptiste Boric error = errno;
2551facb048SJean-Baptiste Boric break;
2561facb048SJean-Baptiste Boric
2571facb048SJean-Baptiste Boric default:
2581facb048SJean-Baptiste Boric close(pfd[1]);
2591facb048SJean-Baptiste Boric
2601facb048SJean-Baptiste Boric /* Retrieve child process status through pipe. */
2611facb048SJean-Baptiste Boric r = read(pfd[0], &error, sizeof(error));
2621facb048SJean-Baptiste Boric if (r == 0)
2631facb048SJean-Baptiste Boric error = 0;
2641facb048SJean-Baptiste Boric else if (r == -1)
2651facb048SJean-Baptiste Boric error = errno;
2661facb048SJean-Baptiste Boric close(pfd[0]);
2671facb048SJean-Baptiste Boric
268*1bb466ddSDavid van Moolenbroek if (error != 0)
269*1bb466ddSDavid van Moolenbroek (void)waitpid(p, NULL, 0);
270*1bb466ddSDavid van Moolenbroek
2711facb048SJean-Baptiste Boric if (pid != NULL)
2721facb048SJean-Baptiste Boric *pid = p;
2731facb048SJean-Baptiste Boric return error;
2741facb048SJean-Baptiste Boric }
2751facb048SJean-Baptiste Boric
2761facb048SJean-Baptiste Boric /* Child failed somewhere, propagate error through pipe and exit. */
2771facb048SJean-Baptiste Boric write(pfd[1], &error, sizeof(error));
2781facb048SJean-Baptiste Boric close(pfd[1]);
2791facb048SJean-Baptiste Boric _exit(127);
2801facb048SJean-Baptiste Boric }
281