xref: /freebsd/lib/libc/gen/posix_spawn.c (revision 2ce23b1f)
1947aa542SDavid Xu /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3d915a14eSPedro F. Giffuni  *
4d1b2bd21SEd Schouten  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
5947aa542SDavid Xu  * All rights reserved.
6947aa542SDavid Xu  *
7947aa542SDavid Xu  * Redistribution and use in source and binary forms, with or without
8947aa542SDavid Xu  * modification, are permitted provided that the following conditions
9947aa542SDavid Xu  * are met:
10947aa542SDavid Xu  * 1. Redistributions of source code must retain the above copyright
11947aa542SDavid Xu  *    notice, this list of conditions and the following disclaimer.
12947aa542SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
13947aa542SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
14947aa542SDavid Xu  *    documentation and/or other materials provided with the distribution.
15947aa542SDavid Xu  *
16947aa542SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17947aa542SDavid Xu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18947aa542SDavid Xu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19947aa542SDavid Xu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20947aa542SDavid Xu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21947aa542SDavid Xu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22947aa542SDavid Xu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23947aa542SDavid Xu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24947aa542SDavid Xu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25947aa542SDavid Xu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26947aa542SDavid Xu  * SUCH DAMAGE.
27947aa542SDavid Xu  */
28947aa542SDavid Xu 
29947aa542SDavid Xu #include "namespace.h"
30f0fbdf1fSKyle Evans #include <sys/param.h>
31822042fdSKonstantin Belousov #include <sys/procctl.h>
32947aa542SDavid Xu #include <sys/queue.h>
339ea47cacSJilles Tjoelker #include <sys/wait.h>
34947aa542SDavid Xu 
35947aa542SDavid Xu #include <errno.h>
36947aa542SDavid Xu #include <fcntl.h>
37947aa542SDavid Xu #include <sched.h>
38947aa542SDavid Xu #include <spawn.h>
39947aa542SDavid Xu #include <signal.h>
40947aa542SDavid Xu #include <stdlib.h>
41947aa542SDavid Xu #include <string.h>
42947aa542SDavid Xu #include <unistd.h>
43947aa542SDavid Xu #include "un-namespace.h"
44c605eea9SEd Schouten #include "libc_private.h"
45947aa542SDavid Xu 
46947aa542SDavid Xu struct __posix_spawnattr {
47947aa542SDavid Xu 	short			sa_flags;
48947aa542SDavid Xu 	pid_t			sa_pgroup;
49947aa542SDavid Xu 	struct sched_param	sa_schedparam;
50947aa542SDavid Xu 	int			sa_schedpolicy;
51947aa542SDavid Xu 	sigset_t		sa_sigdefault;
52947aa542SDavid Xu 	sigset_t		sa_sigmask;
53947aa542SDavid Xu };
54947aa542SDavid Xu 
55947aa542SDavid Xu struct __posix_spawn_file_actions {
56947aa542SDavid Xu 	STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list;
57947aa542SDavid Xu };
58947aa542SDavid Xu 
59947aa542SDavid Xu typedef struct __posix_spawn_file_actions_entry {
60947aa542SDavid Xu 	STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list;
61adbaf1b4SKonstantin Belousov 	enum {
62adbaf1b4SKonstantin Belousov 		FAE_OPEN,
63adbaf1b4SKonstantin Belousov 		FAE_DUP2,
64adbaf1b4SKonstantin Belousov 		FAE_CLOSE,
6525cda42aSKonstantin Belousov 		FAE_CHDIR,
6625cda42aSKonstantin Belousov 		FAE_FCHDIR,
67a18ddf77SKonstantin Belousov 		FAE_CLOSEFROM,
68adbaf1b4SKonstantin Belousov 	} fae_action;
69947aa542SDavid Xu 
70947aa542SDavid Xu 	int fae_fildes;
71947aa542SDavid Xu 	union {
72947aa542SDavid Xu 		struct {
73947aa542SDavid Xu 			char *path;
74947aa542SDavid Xu #define fae_path	fae_data.open.path
75947aa542SDavid Xu 			int oflag;
76947aa542SDavid Xu #define fae_oflag	fae_data.open.oflag
77947aa542SDavid Xu 			mode_t mode;
78947aa542SDavid Xu #define fae_mode	fae_data.open.mode
79947aa542SDavid Xu 		} open;
80947aa542SDavid Xu 		struct {
81947aa542SDavid Xu 			int newfildes;
82947aa542SDavid Xu #define fae_newfildes	fae_data.dup2.newfildes
83947aa542SDavid Xu 		} dup2;
84947aa542SDavid Xu 	} fae_data;
85947aa542SDavid Xu } posix_spawn_file_actions_entry_t;
86947aa542SDavid Xu 
87947aa542SDavid Xu /*
88947aa542SDavid Xu  * Spawn routines
89947aa542SDavid Xu  */
90947aa542SDavid Xu 
91947aa542SDavid Xu static int
process_spawnattr(const posix_spawnattr_t sa)92947aa542SDavid Xu process_spawnattr(const posix_spawnattr_t sa)
93947aa542SDavid Xu {
94947aa542SDavid Xu 	struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
95822042fdSKonstantin Belousov 	int aslr, i;
96947aa542SDavid Xu 
97947aa542SDavid Xu 	/*
98947aa542SDavid Xu 	 * POSIX doesn't really describe in which order everything
99947aa542SDavid Xu 	 * should be set. We'll just set them in the order in which they
100947aa542SDavid Xu 	 * are mentioned.
101947aa542SDavid Xu 	 */
102947aa542SDavid Xu 
103947aa542SDavid Xu 	/* Set process group */
104947aa542SDavid Xu 	if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
105947aa542SDavid Xu 		if (setpgid(0, sa->sa_pgroup) != 0)
106947aa542SDavid Xu 			return (errno);
107947aa542SDavid Xu 	}
108947aa542SDavid Xu 
109947aa542SDavid Xu 	/* Set scheduler policy */
110947aa542SDavid Xu 	if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
111947aa542SDavid Xu 		if (sched_setscheduler(0, sa->sa_schedpolicy,
112947aa542SDavid Xu 		    &sa->sa_schedparam) != 0)
113947aa542SDavid Xu 			return (errno);
114947aa542SDavid Xu 	} else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
115947aa542SDavid Xu 		if (sched_setparam(0, &sa->sa_schedparam) != 0)
116947aa542SDavid Xu 			return (errno);
117947aa542SDavid Xu 	}
1188e9a8a6cSDavid Xu 
1198e9a8a6cSDavid Xu 	/* Reset user ID's */
1208e9a8a6cSDavid Xu 	if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
1218e9a8a6cSDavid Xu 		if (setegid(getgid()) != 0)
1228e9a8a6cSDavid Xu 			return (errno);
1238e9a8a6cSDavid Xu 		if (seteuid(getuid()) != 0)
1248e9a8a6cSDavid Xu 			return (errno);
1258e9a8a6cSDavid Xu 	}
1268e9a8a6cSDavid Xu 
127bd6060a1SKonstantin Belousov 	/*
128bd6060a1SKonstantin Belousov 	 * Set signal masks/defaults.
129bd6060a1SKonstantin Belousov 	 * Use unwrapped syscall, libthr is in undefined state after vfork().
130bd6060a1SKonstantin Belousov 	 */
1318e9a8a6cSDavid Xu 	if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
1323ea91783SKonstantin Belousov 		__sys_sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
1338e9a8a6cSDavid Xu 	}
1348e9a8a6cSDavid Xu 
1358e9a8a6cSDavid Xu 	if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
1368e9a8a6cSDavid Xu 		for (i = 1; i <= _SIG_MAXSIG; i++) {
1378e9a8a6cSDavid Xu 			if (sigismember(&sa->sa_sigdefault, i))
1383ea91783SKonstantin Belousov 				if (__sys_sigaction(i, &sigact, NULL) != 0)
1398e9a8a6cSDavid Xu 					return (errno);
1408e9a8a6cSDavid Xu 		}
1418e9a8a6cSDavid Xu 	}
1428e9a8a6cSDavid Xu 
143822042fdSKonstantin Belousov 	/* Disable ASLR. */
144822042fdSKonstantin Belousov 	if ((sa->sa_flags & POSIX_SPAWN_DISABLE_ASLR_NP) != 0) {
145822042fdSKonstantin Belousov 		aslr = PROC_ASLR_FORCE_DISABLE;
146822042fdSKonstantin Belousov 		if (procctl(P_PID, 0, PROC_ASLR_CTL, &aslr) != 0)
147822042fdSKonstantin Belousov 			return (errno);
148822042fdSKonstantin Belousov 	}
149822042fdSKonstantin Belousov 
150947aa542SDavid Xu 	return (0);
151947aa542SDavid Xu }
152947aa542SDavid Xu 
153947aa542SDavid Xu static int
process_file_actions_entry(posix_spawn_file_actions_entry_t * fae)154947aa542SDavid Xu process_file_actions_entry(posix_spawn_file_actions_entry_t *fae)
155947aa542SDavid Xu {
1569b842193SDon Lewis 	int fd, saved_errno;
157947aa542SDavid Xu 
158947aa542SDavid Xu 	switch (fae->fae_action) {
159947aa542SDavid Xu 	case FAE_OPEN:
160947aa542SDavid Xu 		/* Perform an open(), make it use the right fd */
161947aa542SDavid Xu 		fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
162947aa542SDavid Xu 		if (fd < 0)
163947aa542SDavid Xu 			return (errno);
164947aa542SDavid Xu 		if (fd != fae->fae_fildes) {
1659b842193SDon Lewis 			if (_dup2(fd, fae->fae_fildes) == -1) {
1669b842193SDon Lewis 				saved_errno = errno;
1679b842193SDon Lewis 				(void)_close(fd);
1689b842193SDon Lewis 				return (saved_errno);
1699b842193SDon Lewis 			}
170947aa542SDavid Xu 			if (_close(fd) != 0) {
171947aa542SDavid Xu 				if (errno == EBADF)
172947aa542SDavid Xu 					return (EBADF);
173947aa542SDavid Xu 			}
174947aa542SDavid Xu 		}
175947aa542SDavid Xu 		if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
176947aa542SDavid Xu 			return (errno);
177947aa542SDavid Xu 		break;
178947aa542SDavid Xu 	case FAE_DUP2:
179947aa542SDavid Xu 		/* Perform a dup2() */
180947aa542SDavid Xu 		if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
181947aa542SDavid Xu 			return (errno);
182947aa542SDavid Xu 		if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
183947aa542SDavid Xu 			return (errno);
184947aa542SDavid Xu 		break;
185947aa542SDavid Xu 	case FAE_CLOSE:
186562b2882SJilles Tjoelker 		/* Perform a close(), do not fail if already closed */
187562b2882SJilles Tjoelker 		(void)_close(fae->fae_fildes);
188947aa542SDavid Xu 		break;
18925cda42aSKonstantin Belousov 	case FAE_CHDIR:
19025cda42aSKonstantin Belousov 		if (chdir(fae->fae_path) != 0)
19125cda42aSKonstantin Belousov 			return (errno);
19225cda42aSKonstantin Belousov 		break;
19325cda42aSKonstantin Belousov 	case FAE_FCHDIR:
19425cda42aSKonstantin Belousov 		if (fchdir(fae->fae_fildes) != 0)
19525cda42aSKonstantin Belousov 			return (errno);
19625cda42aSKonstantin Belousov 		break;
197a18ddf77SKonstantin Belousov 	case FAE_CLOSEFROM:
198a18ddf77SKonstantin Belousov 		closefrom(fae->fae_fildes);
199a18ddf77SKonstantin Belousov 		break;
200947aa542SDavid Xu 	}
201947aa542SDavid Xu 	return (0);
202947aa542SDavid Xu }
203947aa542SDavid Xu 
204947aa542SDavid Xu static int
process_file_actions(const posix_spawn_file_actions_t fa)205947aa542SDavid Xu process_file_actions(const posix_spawn_file_actions_t fa)
206947aa542SDavid Xu {
207947aa542SDavid Xu 	posix_spawn_file_actions_entry_t *fae;
208947aa542SDavid Xu 	int error;
209947aa542SDavid Xu 
210947aa542SDavid Xu 	/* Replay all file descriptor modifications */
211947aa542SDavid Xu 	STAILQ_FOREACH(fae, &fa->fa_list, fae_list) {
212947aa542SDavid Xu 		error = process_file_actions_entry(fae);
213947aa542SDavid Xu 		if (error)
214947aa542SDavid Xu 			return (error);
215947aa542SDavid Xu 	}
216947aa542SDavid Xu 	return (0);
217947aa542SDavid Xu }
218947aa542SDavid Xu 
219c34a5f16SKyle Evans struct posix_spawn_args {
220c34a5f16SKyle Evans 	const char *path;
221c34a5f16SKyle Evans 	const posix_spawn_file_actions_t *fa;
222c34a5f16SKyle Evans 	const posix_spawnattr_t *sa;
223c34a5f16SKyle Evans 	char * const * argv;
224c34a5f16SKyle Evans 	char * const * envp;
225c34a5f16SKyle Evans 	int use_env_path;
226a1fa478bSKyle Evans 	volatile int error;
227c34a5f16SKyle Evans };
228c34a5f16SKyle Evans 
229f0fbdf1fSKyle Evans #define	PSPAWN_STACK_ALIGNMENT	16
230f0fbdf1fSKyle Evans #define	PSPAWN_STACK_ALIGNBYTES	(PSPAWN_STACK_ALIGNMENT - 1)
231f0fbdf1fSKyle Evans #define	PSPAWN_STACK_ALIGN(sz) \
232f0fbdf1fSKyle Evans 	(((sz) + PSPAWN_STACK_ALIGNBYTES) & ~PSPAWN_STACK_ALIGNBYTES)
233f0fbdf1fSKyle Evans 
234c34a5f16SKyle Evans #if defined(__i386__) || defined(__amd64__)
235f0fbdf1fSKyle Evans /*
236f0fbdf1fSKyle Evans  * Below we'll assume that _RFORK_THREAD_STACK_SIZE is appropriately aligned for
2378ccd0b87SBrooks Davis  * the posix_spawn() case where we do not end up calling execvpe and won't ever
238f0fbdf1fSKyle Evans  * try to allocate space on the stack for argv[].
239f0fbdf1fSKyle Evans  */
240c34a5f16SKyle Evans #define	_RFORK_THREAD_STACK_SIZE	4096
241f0fbdf1fSKyle Evans _Static_assert((_RFORK_THREAD_STACK_SIZE % PSPAWN_STACK_ALIGNMENT) == 0,
242f0fbdf1fSKyle Evans     "Inappropriate stack size alignment");
243c34a5f16SKyle Evans #endif
244c34a5f16SKyle Evans 
245c34a5f16SKyle Evans static int
_posix_spawn_thr(void * data)246c34a5f16SKyle Evans _posix_spawn_thr(void *data)
247c34a5f16SKyle Evans {
248c34a5f16SKyle Evans 	struct posix_spawn_args *psa;
249c34a5f16SKyle Evans 	char * const *envp;
250c34a5f16SKyle Evans 
251c34a5f16SKyle Evans 	psa = data;
252c34a5f16SKyle Evans 	if (psa->sa != NULL) {
253c34a5f16SKyle Evans 		psa->error = process_spawnattr(*psa->sa);
254c34a5f16SKyle Evans 		if (psa->error)
255c34a5f16SKyle Evans 			_exit(127);
256c34a5f16SKyle Evans 	}
257c34a5f16SKyle Evans 	if (psa->fa != NULL) {
258c34a5f16SKyle Evans 		psa->error = process_file_actions(*psa->fa);
259c34a5f16SKyle Evans 		if (psa->error)
260c34a5f16SKyle Evans 			_exit(127);
261c34a5f16SKyle Evans 	}
262c34a5f16SKyle Evans 	envp = psa->envp != NULL ? psa->envp : environ;
263c34a5f16SKyle Evans 	if (psa->use_env_path)
2648ccd0b87SBrooks Davis 		execvpe(psa->path, psa->argv, envp);
265c34a5f16SKyle Evans 	else
266c34a5f16SKyle Evans 		_execve(psa->path, psa->argv, envp);
267c34a5f16SKyle Evans 	psa->error = errno;
268c34a5f16SKyle Evans 
269c34a5f16SKyle Evans 	/* This is called in such a way that it must not exit. */
270c34a5f16SKyle Evans 	_exit(127);
271c34a5f16SKyle Evans }
272c34a5f16SKyle Evans 
273947aa542SDavid Xu static int
do_posix_spawn(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * sa,char * const argv[],char * const envp[],int use_env_path)274947aa542SDavid Xu do_posix_spawn(pid_t *pid, const char *path,
275947aa542SDavid Xu     const posix_spawn_file_actions_t *fa,
276947aa542SDavid Xu     const posix_spawnattr_t *sa,
277947aa542SDavid Xu     char * const argv[], char * const envp[], int use_env_path)
278947aa542SDavid Xu {
279c34a5f16SKyle Evans 	struct posix_spawn_args psa;
280947aa542SDavid Xu 	pid_t p;
281c34a5f16SKyle Evans #ifdef _RFORK_THREAD_STACK_SIZE
282c34a5f16SKyle Evans 	char *stack;
283f0fbdf1fSKyle Evans 	size_t cnt, stacksz;
284947aa542SDavid Xu 
285f0fbdf1fSKyle Evans 	stacksz = _RFORK_THREAD_STACK_SIZE;
286f0fbdf1fSKyle Evans 	if (use_env_path) {
287f0fbdf1fSKyle Evans 		/*
288f0fbdf1fSKyle Evans 		 * We need to make sure we have enough room on the stack for the
289f0fbdf1fSKyle Evans 		 * potential alloca() in execvPe if it gets kicked back an
290f0fbdf1fSKyle Evans 		 * ENOEXEC from execve(2), plus the original buffer we gave
291f0fbdf1fSKyle Evans 		 * ourselves; this protects us in the event that the caller
292f0fbdf1fSKyle Evans 		 * intentionally or inadvertently supplies enough arguments to
293f0fbdf1fSKyle Evans 		 * make us blow past the stack we've allocated from it.
294f0fbdf1fSKyle Evans 		 */
295f0fbdf1fSKyle Evans 		for (cnt = 0; argv[cnt] != NULL; ++cnt)
296f0fbdf1fSKyle Evans 			;
297f0fbdf1fSKyle Evans 		stacksz += MAX(3, cnt + 2) * sizeof(char *);
298f0fbdf1fSKyle Evans 		stacksz = PSPAWN_STACK_ALIGN(stacksz);
299f0fbdf1fSKyle Evans 	}
300ebff66b3SKyle Evans 
301ebff66b3SKyle Evans 	/*
302ebff66b3SKyle Evans 	 * aligned_alloc is not safe to use here, because we can't guarantee
303ebff66b3SKyle Evans 	 * that aligned_alloc and free will be provided by the same
304ebff66b3SKyle Evans 	 * implementation.  We've actively hit at least one application that
305ebff66b3SKyle Evans 	 * will provide its own malloc/free but not aligned_alloc leading to
306ebff66b3SKyle Evans 	 * a free by the wrong allocator.
307ebff66b3SKyle Evans 	 */
308ebff66b3SKyle Evans 	stack = malloc(stacksz);
309c34a5f16SKyle Evans 	if (stack == NULL)
310c34a5f16SKyle Evans 		return (ENOMEM);
311ebff66b3SKyle Evans 	stacksz = (((uintptr_t)stack + stacksz) & ~PSPAWN_STACK_ALIGNBYTES) -
312ebff66b3SKyle Evans 	    (uintptr_t)stack;
313c34a5f16SKyle Evans #endif
314c34a5f16SKyle Evans 	psa.path = path;
315c34a5f16SKyle Evans 	psa.fa = fa;
316c34a5f16SKyle Evans 	psa.sa = sa;
317c34a5f16SKyle Evans 	psa.argv = argv;
318c34a5f16SKyle Evans 	psa.envp = envp;
319c34a5f16SKyle Evans 	psa.use_env_path = use_env_path;
320c34a5f16SKyle Evans 	psa.error = 0;
321c34a5f16SKyle Evans 
322c34a5f16SKyle Evans 	/*
323c34a5f16SKyle Evans 	 * Passing RFSPAWN to rfork(2) gives us effectively a vfork that drops
324c34a5f16SKyle Evans 	 * non-ignored signal handlers.  We'll fall back to the slightly less
325c34a5f16SKyle Evans 	 * ideal vfork(2) if we get an EINVAL from rfork -- this should only
326c34a5f16SKyle Evans 	 * happen with newer libc on older kernel that doesn't accept
327c34a5f16SKyle Evans 	 * RFSPAWN.
3282ce23b1fSKonstantin Belousov 	 *
3292ce23b1fSKonstantin Belousov 	 * Combination of vfork() (or its equivalent rfork() form) and
3302ce23b1fSKonstantin Belousov 	 * a special property of the libthr rtld locks ensure that
3312ce23b1fSKonstantin Belousov 	 * rtld is operational in the child.  In particular, libthr
3322ce23b1fSKonstantin Belousov 	 * rtld locks do not store owner' tid into the lock word.
333c34a5f16SKyle Evans 	 */
334c34a5f16SKyle Evans #ifdef _RFORK_THREAD_STACK_SIZE
335c34a5f16SKyle Evans 	/*
336c34a5f16SKyle Evans 	 * x86 stores the return address on the stack, so rfork(2) cannot work
337ebaf9071SKonstantin Belousov 	 * as-is because the child would clobber the return address of the
338c34a5f16SKyle Evans 	 * parent.  Because of this, we must use rfork_thread instead while
339c34a5f16SKyle Evans 	 * almost every other arch stores the return address in a register.
340c34a5f16SKyle Evans 	 */
341f0fbdf1fSKyle Evans 	p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa);
342c34a5f16SKyle Evans 	free(stack);
343c34a5f16SKyle Evans #else
344c34a5f16SKyle Evans 	p = rfork(RFSPAWN);
345c34a5f16SKyle Evans 	if (p == 0)
346c34a5f16SKyle Evans 		/* _posix_spawn_thr does not return */
347c34a5f16SKyle Evans 		_posix_spawn_thr(&psa);
348c34a5f16SKyle Evans #endif
349c34a5f16SKyle Evans 	/*
350c34a5f16SKyle Evans 	 * The above block should leave us in a state where we've either
351c34a5f16SKyle Evans 	 * succeeded and we're ready to process the results, or we need to
352c34a5f16SKyle Evans 	 * fallback to vfork() if the kernel didn't like RFSPAWN.
353c34a5f16SKyle Evans 	 */
354c34a5f16SKyle Evans 
355c34a5f16SKyle Evans 	if (p == -1 && errno == EINVAL) {
356947aa542SDavid Xu 		p = vfork();
357c34a5f16SKyle Evans 		if (p == 0)
358c34a5f16SKyle Evans 			/* _posix_spawn_thr does not return */
359c34a5f16SKyle Evans 			_posix_spawn_thr(&psa);
360c34a5f16SKyle Evans 	}
361c34a5f16SKyle Evans 	if (p == -1)
362947aa542SDavid Xu 		return (errno);
363c34a5f16SKyle Evans 	if (psa.error != 0)
364c34a5f16SKyle Evans 		/* Failed; ready to reap */
3659ea47cacSJilles Tjoelker 		_waitpid(p, NULL, WNOHANG);
3669ea47cacSJilles Tjoelker 	else if (pid != NULL)
367c34a5f16SKyle Evans 		/* exec succeeded */
368947aa542SDavid Xu 		*pid = p;
369c34a5f16SKyle Evans 	return (psa.error);
370947aa542SDavid Xu }
371947aa542SDavid Xu 
372947aa542SDavid Xu int
posix_spawn(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * sa,char * const argv[],char * const envp[])373947aa542SDavid Xu posix_spawn(pid_t *pid, const char *path,
374947aa542SDavid Xu     const posix_spawn_file_actions_t *fa,
375947aa542SDavid Xu     const posix_spawnattr_t *sa,
376947aa542SDavid Xu     char * const argv[], char * const envp[])
377947aa542SDavid Xu {
378b239cc20SKonstantin Belousov 	return (do_posix_spawn(pid, path, fa, sa, argv, envp, 0));
379947aa542SDavid Xu }
380947aa542SDavid Xu 
381947aa542SDavid Xu int
posix_spawnp(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * sa,char * const argv[],char * const envp[])382947aa542SDavid Xu posix_spawnp(pid_t *pid, const char *path,
383947aa542SDavid Xu     const posix_spawn_file_actions_t *fa,
384947aa542SDavid Xu     const posix_spawnattr_t *sa,
385947aa542SDavid Xu     char * const argv[], char * const envp[])
386947aa542SDavid Xu {
387b239cc20SKonstantin Belousov 	return (do_posix_spawn(pid, path, fa, sa, argv, envp, 1));
388947aa542SDavid Xu }
389947aa542SDavid Xu 
390947aa542SDavid Xu /*
391947aa542SDavid Xu  * File descriptor actions
392947aa542SDavid Xu  */
393947aa542SDavid Xu 
394947aa542SDavid Xu int
posix_spawn_file_actions_init(posix_spawn_file_actions_t * ret)395947aa542SDavid Xu posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret)
396947aa542SDavid Xu {
397947aa542SDavid Xu 	posix_spawn_file_actions_t fa;
398947aa542SDavid Xu 
399947aa542SDavid Xu 	fa = malloc(sizeof(struct __posix_spawn_file_actions));
400947aa542SDavid Xu 	if (fa == NULL)
401947aa542SDavid Xu 		return (-1);
402947aa542SDavid Xu 
403947aa542SDavid Xu 	STAILQ_INIT(&fa->fa_list);
404947aa542SDavid Xu 	*ret = fa;
405947aa542SDavid Xu 	return (0);
406947aa542SDavid Xu }
407947aa542SDavid Xu 
408947aa542SDavid Xu int
posix_spawn_file_actions_destroy(posix_spawn_file_actions_t * fa)409947aa542SDavid Xu posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
410947aa542SDavid Xu {
411947aa542SDavid Xu 	posix_spawn_file_actions_entry_t *fae;
412947aa542SDavid Xu 
413947aa542SDavid Xu 	while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) {
414947aa542SDavid Xu 		/* Remove file action entry from the queue */
415947aa542SDavid Xu 		STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list);
416947aa542SDavid Xu 
417947aa542SDavid Xu 		/* Deallocate file action entry */
41825cda42aSKonstantin Belousov 		if (fae->fae_action == FAE_OPEN ||
41925cda42aSKonstantin Belousov 		    fae->fae_action == FAE_CHDIR)
420947aa542SDavid Xu 			free(fae->fae_path);
421947aa542SDavid Xu 		free(fae);
422947aa542SDavid Xu 	}
423947aa542SDavid Xu 
424947aa542SDavid Xu 	free(*fa);
425947aa542SDavid Xu 	return (0);
426947aa542SDavid Xu }
427947aa542SDavid Xu 
428947aa542SDavid Xu int
posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,int fildes,const char * __restrict path,int oflag,mode_t mode)429947aa542SDavid Xu posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,
430947aa542SDavid Xu     int fildes, const char * __restrict path, int oflag, mode_t mode)
431947aa542SDavid Xu {
432947aa542SDavid Xu 	posix_spawn_file_actions_entry_t *fae;
433947aa542SDavid Xu 	int error;
434947aa542SDavid Xu 
435947aa542SDavid Xu 	if (fildes < 0)
436947aa542SDavid Xu 		return (EBADF);
437947aa542SDavid Xu 
438947aa542SDavid Xu 	/* Allocate object */
439947aa542SDavid Xu 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
440947aa542SDavid Xu 	if (fae == NULL)
441947aa542SDavid Xu 		return (errno);
442947aa542SDavid Xu 
443947aa542SDavid Xu 	/* Set values and store in queue */
444947aa542SDavid Xu 	fae->fae_action = FAE_OPEN;
445947aa542SDavid Xu 	fae->fae_path = strdup(path);
446947aa542SDavid Xu 	if (fae->fae_path == NULL) {
447947aa542SDavid Xu 		error = errno;
448947aa542SDavid Xu 		free(fae);
449947aa542SDavid Xu 		return (error);
450947aa542SDavid Xu 	}
451947aa542SDavid Xu 	fae->fae_fildes = fildes;
452947aa542SDavid Xu 	fae->fae_oflag = oflag;
453947aa542SDavid Xu 	fae->fae_mode = mode;
454947aa542SDavid Xu 
455947aa542SDavid Xu 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
456947aa542SDavid Xu 	return (0);
457947aa542SDavid Xu }
458947aa542SDavid Xu 
459947aa542SDavid Xu int
posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t * fa,int fildes,int newfildes)460947aa542SDavid Xu posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa,
461947aa542SDavid Xu     int fildes, int newfildes)
462947aa542SDavid Xu {
463947aa542SDavid Xu 	posix_spawn_file_actions_entry_t *fae;
464947aa542SDavid Xu 
465947aa542SDavid Xu 	if (fildes < 0 || newfildes < 0)
466947aa542SDavid Xu 		return (EBADF);
467947aa542SDavid Xu 
468947aa542SDavid Xu 	/* Allocate object */
469947aa542SDavid Xu 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
470947aa542SDavid Xu 	if (fae == NULL)
471947aa542SDavid Xu 		return (errno);
472947aa542SDavid Xu 
473947aa542SDavid Xu 	/* Set values and store in queue */
474947aa542SDavid Xu 	fae->fae_action = FAE_DUP2;
475947aa542SDavid Xu 	fae->fae_fildes = fildes;
476947aa542SDavid Xu 	fae->fae_newfildes = newfildes;
477947aa542SDavid Xu 
478947aa542SDavid Xu 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
479947aa542SDavid Xu 	return (0);
480947aa542SDavid Xu }
481947aa542SDavid Xu 
482fdbeb80aSDavid Xu int
posix_spawn_file_actions_addclose(posix_spawn_file_actions_t * fa,int fildes)483fdbeb80aSDavid Xu posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa,
484947aa542SDavid Xu     int fildes)
485947aa542SDavid Xu {
486947aa542SDavid Xu 	posix_spawn_file_actions_entry_t *fae;
487947aa542SDavid Xu 
488947aa542SDavid Xu 	if (fildes < 0)
489947aa542SDavid Xu 		return (EBADF);
490947aa542SDavid Xu 
491947aa542SDavid Xu 	/* Allocate object */
492947aa542SDavid Xu 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
493947aa542SDavid Xu 	if (fae == NULL)
494947aa542SDavid Xu 		return (errno);
495947aa542SDavid Xu 
496947aa542SDavid Xu 	/* Set values and store in queue */
497947aa542SDavid Xu 	fae->fae_action = FAE_CLOSE;
498947aa542SDavid Xu 	fae->fae_fildes = fildes;
499947aa542SDavid Xu 
500947aa542SDavid Xu 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
501947aa542SDavid Xu 	return (0);
502947aa542SDavid Xu }
503947aa542SDavid Xu 
50425cda42aSKonstantin Belousov int
posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t * __restrict fa,const char * __restrict path)50525cda42aSKonstantin Belousov posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *
50625cda42aSKonstantin Belousov     __restrict fa, const char *__restrict path)
50725cda42aSKonstantin Belousov {
50825cda42aSKonstantin Belousov 	posix_spawn_file_actions_entry_t *fae;
50925cda42aSKonstantin Belousov 	int error;
51025cda42aSKonstantin Belousov 
51125cda42aSKonstantin Belousov 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
51225cda42aSKonstantin Belousov 	if (fae == NULL)
51325cda42aSKonstantin Belousov 		return (errno);
51425cda42aSKonstantin Belousov 
51525cda42aSKonstantin Belousov 	fae->fae_action = FAE_CHDIR;
51625cda42aSKonstantin Belousov 	fae->fae_path = strdup(path);
51725cda42aSKonstantin Belousov 	if (fae->fae_path == NULL) {
51825cda42aSKonstantin Belousov 		error = errno;
51925cda42aSKonstantin Belousov 		free(fae);
52025cda42aSKonstantin Belousov 		return (error);
52125cda42aSKonstantin Belousov 	}
52225cda42aSKonstantin Belousov 
52325cda42aSKonstantin Belousov 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
52425cda42aSKonstantin Belousov 	return (0);
52525cda42aSKonstantin Belousov }
52625cda42aSKonstantin Belousov 
52725cda42aSKonstantin Belousov int
posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t * __restrict fa,int fildes)52825cda42aSKonstantin Belousov posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *__restrict fa,
52925cda42aSKonstantin Belousov     int fildes)
53025cda42aSKonstantin Belousov {
53125cda42aSKonstantin Belousov 	posix_spawn_file_actions_entry_t *fae;
53225cda42aSKonstantin Belousov 
53325cda42aSKonstantin Belousov 	if (fildes < 0)
53425cda42aSKonstantin Belousov 		return (EBADF);
53525cda42aSKonstantin Belousov 
53625cda42aSKonstantin Belousov 	/* Allocate object */
53725cda42aSKonstantin Belousov 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
53825cda42aSKonstantin Belousov 	if (fae == NULL)
53925cda42aSKonstantin Belousov 		return (errno);
54025cda42aSKonstantin Belousov 
54125cda42aSKonstantin Belousov 	fae->fae_action = FAE_FCHDIR;
54225cda42aSKonstantin Belousov 	fae->fae_fildes = fildes;
54325cda42aSKonstantin Belousov 
54425cda42aSKonstantin Belousov 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
54525cda42aSKonstantin Belousov 	return (0);
54625cda42aSKonstantin Belousov }
54725cda42aSKonstantin Belousov 
548a18ddf77SKonstantin Belousov int
posix_spawn_file_actions_addclosefrom_np(posix_spawn_file_actions_t * __restrict fa,int from)549a18ddf77SKonstantin Belousov posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t *
550a18ddf77SKonstantin Belousov     __restrict fa, int from)
551a18ddf77SKonstantin Belousov {
552a18ddf77SKonstantin Belousov 	posix_spawn_file_actions_entry_t *fae;
553a18ddf77SKonstantin Belousov 
554a18ddf77SKonstantin Belousov 	if (from < 0)
555a18ddf77SKonstantin Belousov 		return (EBADF);
556a18ddf77SKonstantin Belousov 
557a18ddf77SKonstantin Belousov 	/* Allocate object */
558a18ddf77SKonstantin Belousov 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
559a18ddf77SKonstantin Belousov 	if (fae == NULL)
560a18ddf77SKonstantin Belousov 		return (errno);
561a18ddf77SKonstantin Belousov 
562a18ddf77SKonstantin Belousov 	fae->fae_action = FAE_CLOSEFROM;
563a18ddf77SKonstantin Belousov 	fae->fae_fildes = from;
564a18ddf77SKonstantin Belousov 
565a18ddf77SKonstantin Belousov 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
566a18ddf77SKonstantin Belousov 	return (0);
567a18ddf77SKonstantin Belousov }
568a18ddf77SKonstantin Belousov 
569947aa542SDavid Xu /*
570947aa542SDavid Xu  * Spawn attributes
571947aa542SDavid Xu  */
572947aa542SDavid Xu 
573947aa542SDavid Xu int
posix_spawnattr_init(posix_spawnattr_t * ret)574947aa542SDavid Xu posix_spawnattr_init(posix_spawnattr_t *ret)
575947aa542SDavid Xu {
576947aa542SDavid Xu 	posix_spawnattr_t sa;
577947aa542SDavid Xu 
578947aa542SDavid Xu 	sa = calloc(1, sizeof(struct __posix_spawnattr));
579947aa542SDavid Xu 	if (sa == NULL)
580947aa542SDavid Xu 		return (errno);
581947aa542SDavid Xu 
582947aa542SDavid Xu 	/* Set defaults as specified by POSIX, cleared above */
583947aa542SDavid Xu 	*ret = sa;
584947aa542SDavid Xu 	return (0);
585947aa542SDavid Xu }
586947aa542SDavid Xu 
587947aa542SDavid Xu int
posix_spawnattr_destroy(posix_spawnattr_t * sa)588947aa542SDavid Xu posix_spawnattr_destroy(posix_spawnattr_t *sa)
589947aa542SDavid Xu {
590947aa542SDavid Xu 	free(*sa);
591947aa542SDavid Xu 	return (0);
592947aa542SDavid Xu }
593947aa542SDavid Xu 
594947aa542SDavid Xu int
posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,short * __restrict flags)595947aa542SDavid Xu posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,
596947aa542SDavid Xu     short * __restrict flags)
597947aa542SDavid Xu {
598947aa542SDavid Xu 	*flags = (*sa)->sa_flags;
599947aa542SDavid Xu 	return (0);
600947aa542SDavid Xu }
601947aa542SDavid Xu 
602947aa542SDavid Xu int
posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,pid_t * __restrict pgroup)603947aa542SDavid Xu posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,
604947aa542SDavid Xu     pid_t * __restrict pgroup)
605947aa542SDavid Xu {
606947aa542SDavid Xu 	*pgroup = (*sa)->sa_pgroup;
607947aa542SDavid Xu 	return (0);
608947aa542SDavid Xu }
609947aa542SDavid Xu 
610947aa542SDavid Xu int
posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,struct sched_param * __restrict schedparam)611947aa542SDavid Xu posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,
612947aa542SDavid Xu     struct sched_param * __restrict schedparam)
613947aa542SDavid Xu {
614947aa542SDavid Xu 	*schedparam = (*sa)->sa_schedparam;
615947aa542SDavid Xu 	return (0);
616947aa542SDavid Xu }
617947aa542SDavid Xu 
618947aa542SDavid Xu int
posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,int * __restrict schedpolicy)619947aa542SDavid Xu posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,
620947aa542SDavid Xu     int * __restrict schedpolicy)
621947aa542SDavid Xu {
622947aa542SDavid Xu 	*schedpolicy = (*sa)->sa_schedpolicy;
623947aa542SDavid Xu 	return (0);
624947aa542SDavid Xu }
625947aa542SDavid Xu 
626947aa542SDavid Xu int
posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,sigset_t * __restrict sigdefault)627947aa542SDavid Xu posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,
628947aa542SDavid Xu     sigset_t * __restrict sigdefault)
629947aa542SDavid Xu {
630947aa542SDavid Xu 	*sigdefault = (*sa)->sa_sigdefault;
631947aa542SDavid Xu 	return (0);
632947aa542SDavid Xu }
633947aa542SDavid Xu 
634947aa542SDavid Xu int
posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,sigset_t * __restrict sigmask)635947aa542SDavid Xu posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,
636947aa542SDavid Xu     sigset_t * __restrict sigmask)
637947aa542SDavid Xu {
638947aa542SDavid Xu 	*sigmask = (*sa)->sa_sigmask;
639947aa542SDavid Xu 	return (0);
640947aa542SDavid Xu }
641947aa542SDavid Xu 
642947aa542SDavid Xu int
posix_spawnattr_setflags(posix_spawnattr_t * sa,short flags)643947aa542SDavid Xu posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags)
644947aa542SDavid Xu {
64580ac36c3SKonstantin Belousov 	if ((flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP |
64680ac36c3SKonstantin Belousov 	    POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER |
647822042fdSKonstantin Belousov 	    POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
648822042fdSKonstantin Belousov 	    POSIX_SPAWN_DISABLE_ASLR_NP)) != 0)
64980ac36c3SKonstantin Belousov 		return (EINVAL);
650947aa542SDavid Xu 	(*sa)->sa_flags = flags;
651947aa542SDavid Xu 	return (0);
652947aa542SDavid Xu }
653947aa542SDavid Xu 
654947aa542SDavid Xu int
posix_spawnattr_setpgroup(posix_spawnattr_t * sa,pid_t pgroup)655947aa542SDavid Xu posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup)
656947aa542SDavid Xu {
657947aa542SDavid Xu 	(*sa)->sa_pgroup = pgroup;
658947aa542SDavid Xu 	return (0);
659947aa542SDavid Xu }
660947aa542SDavid Xu 
661947aa542SDavid Xu int
posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,const struct sched_param * __restrict schedparam)662994f9863SRoman Divacky posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,
663947aa542SDavid Xu     const struct sched_param * __restrict schedparam)
664947aa542SDavid Xu {
665947aa542SDavid Xu 	(*sa)->sa_schedparam = *schedparam;
666947aa542SDavid Xu 	return (0);
667947aa542SDavid Xu }
668947aa542SDavid Xu 
669947aa542SDavid Xu int
posix_spawnattr_setschedpolicy(posix_spawnattr_t * sa,int schedpolicy)670947aa542SDavid Xu posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy)
671947aa542SDavid Xu {
672947aa542SDavid Xu 	(*sa)->sa_schedpolicy = schedpolicy;
673947aa542SDavid Xu 	return (0);
674947aa542SDavid Xu }
675947aa542SDavid Xu 
676947aa542SDavid Xu int
posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,const sigset_t * __restrict sigdefault)677947aa542SDavid Xu posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,
678947aa542SDavid Xu     const sigset_t * __restrict sigdefault)
679947aa542SDavid Xu {
680947aa542SDavid Xu 	(*sa)->sa_sigdefault = *sigdefault;
681947aa542SDavid Xu 	return (0);
682947aa542SDavid Xu }
683947aa542SDavid Xu 
684947aa542SDavid Xu int
posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,const sigset_t * __restrict sigmask)685947aa542SDavid Xu posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,
686947aa542SDavid Xu     const sigset_t * __restrict sigmask)
687947aa542SDavid Xu {
688947aa542SDavid Xu 	(*sa)->sa_sigmask = *sigmask;
689947aa542SDavid Xu 	return (0);
690947aa542SDavid Xu }
691