xref: /minix3/minix/lib/libc/sys/posix_spawn.c (revision 1bb466dd36c0591122401dfd685782ec28865fc3)
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