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