1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "namespace.h"
30 #include <sys/param.h>
31 #include <sys/procctl.h>
32 #include <sys/queue.h>
33 #include <sys/wait.h>
34
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sched.h>
38 #include <spawn.h>
39 #include <signal.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include "un-namespace.h"
44 #include "libc_private.h"
45
46 struct __posix_spawnattr {
47 short sa_flags;
48 pid_t sa_pgroup;
49 struct sched_param sa_schedparam;
50 int sa_schedpolicy;
51 sigset_t sa_sigdefault;
52 sigset_t sa_sigmask;
53 };
54
55 struct __posix_spawn_file_actions {
56 STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list;
57 };
58
59 typedef struct __posix_spawn_file_actions_entry {
60 STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list;
61 enum {
62 FAE_OPEN,
63 FAE_DUP2,
64 FAE_CLOSE,
65 FAE_CHDIR,
66 FAE_FCHDIR,
67 FAE_CLOSEFROM,
68 } fae_action;
69
70 int fae_fildes;
71 union {
72 struct {
73 char *path;
74 #define fae_path fae_data.open.path
75 int oflag;
76 #define fae_oflag fae_data.open.oflag
77 mode_t mode;
78 #define fae_mode fae_data.open.mode
79 } open;
80 struct {
81 int newfildes;
82 #define fae_newfildes fae_data.dup2.newfildes
83 } dup2;
84 } fae_data;
85 } posix_spawn_file_actions_entry_t;
86
87 /*
88 * Spawn routines
89 */
90
91 static int
process_spawnattr(const posix_spawnattr_t sa)92 process_spawnattr(const posix_spawnattr_t sa)
93 {
94 struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
95 int aslr, i;
96
97 /*
98 * POSIX doesn't really describe in which order everything
99 * should be set. We'll just set them in the order in which they
100 * are mentioned.
101 */
102
103 /* Set process group */
104 if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
105 if (setpgid(0, sa->sa_pgroup) != 0)
106 return (errno);
107 }
108
109 /* Set scheduler policy */
110 if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
111 if (sched_setscheduler(0, sa->sa_schedpolicy,
112 &sa->sa_schedparam) != 0)
113 return (errno);
114 } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
115 if (sched_setparam(0, &sa->sa_schedparam) != 0)
116 return (errno);
117 }
118
119 /* Reset user ID's */
120 if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
121 if (setegid(getgid()) != 0)
122 return (errno);
123 if (seteuid(getuid()) != 0)
124 return (errno);
125 }
126
127 /*
128 * Set signal masks/defaults.
129 * Use unwrapped syscall, libthr is in undefined state after vfork().
130 */
131 if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
132 __sys_sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
133 }
134
135 if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
136 for (i = 1; i <= _SIG_MAXSIG; i++) {
137 if (sigismember(&sa->sa_sigdefault, i))
138 if (__sys_sigaction(i, &sigact, NULL) != 0)
139 return (errno);
140 }
141 }
142
143 /* Disable ASLR. */
144 if ((sa->sa_flags & POSIX_SPAWN_DISABLE_ASLR_NP) != 0) {
145 aslr = PROC_ASLR_FORCE_DISABLE;
146 if (procctl(P_PID, 0, PROC_ASLR_CTL, &aslr) != 0)
147 return (errno);
148 }
149
150 return (0);
151 }
152
153 static int
process_file_actions_entry(posix_spawn_file_actions_entry_t * fae)154 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae)
155 {
156 int fd, saved_errno;
157
158 switch (fae->fae_action) {
159 case FAE_OPEN:
160 /* Perform an open(), make it use the right fd */
161 fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
162 if (fd < 0)
163 return (errno);
164 if (fd != fae->fae_fildes) {
165 if (_dup2(fd, fae->fae_fildes) == -1) {
166 saved_errno = errno;
167 (void)_close(fd);
168 return (saved_errno);
169 }
170 if (_close(fd) != 0) {
171 if (errno == EBADF)
172 return (EBADF);
173 }
174 }
175 if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
176 return (errno);
177 break;
178 case FAE_DUP2:
179 /* Perform a dup2() */
180 if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
181 return (errno);
182 if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
183 return (errno);
184 break;
185 case FAE_CLOSE:
186 /* Perform a close(), do not fail if already closed */
187 (void)_close(fae->fae_fildes);
188 break;
189 case FAE_CHDIR:
190 if (chdir(fae->fae_path) != 0)
191 return (errno);
192 break;
193 case FAE_FCHDIR:
194 if (fchdir(fae->fae_fildes) != 0)
195 return (errno);
196 break;
197 case FAE_CLOSEFROM:
198 closefrom(fae->fae_fildes);
199 break;
200 }
201 return (0);
202 }
203
204 static int
process_file_actions(const posix_spawn_file_actions_t fa)205 process_file_actions(const posix_spawn_file_actions_t fa)
206 {
207 posix_spawn_file_actions_entry_t *fae;
208 int error;
209
210 /* Replay all file descriptor modifications */
211 STAILQ_FOREACH(fae, &fa->fa_list, fae_list) {
212 error = process_file_actions_entry(fae);
213 if (error)
214 return (error);
215 }
216 return (0);
217 }
218
219 struct posix_spawn_args {
220 const char *path;
221 const posix_spawn_file_actions_t *fa;
222 const posix_spawnattr_t *sa;
223 char * const * argv;
224 char * const * envp;
225 int use_env_path;
226 volatile int error;
227 };
228
229 #define PSPAWN_STACK_ALIGNMENT 16
230 #define PSPAWN_STACK_ALIGNBYTES (PSPAWN_STACK_ALIGNMENT - 1)
231 #define PSPAWN_STACK_ALIGN(sz) \
232 (((sz) + PSPAWN_STACK_ALIGNBYTES) & ~PSPAWN_STACK_ALIGNBYTES)
233
234 #if defined(__i386__) || defined(__amd64__)
235 /*
236 * Below we'll assume that _RFORK_THREAD_STACK_SIZE is appropriately aligned for
237 * the posix_spawn() case where we do not end up calling execvpe and won't ever
238 * try to allocate space on the stack for argv[].
239 */
240 #define _RFORK_THREAD_STACK_SIZE 4096
241 _Static_assert((_RFORK_THREAD_STACK_SIZE % PSPAWN_STACK_ALIGNMENT) == 0,
242 "Inappropriate stack size alignment");
243 #endif
244
245 static int
_posix_spawn_thr(void * data)246 _posix_spawn_thr(void *data)
247 {
248 struct posix_spawn_args *psa;
249 char * const *envp;
250
251 psa = data;
252 if (psa->sa != NULL) {
253 psa->error = process_spawnattr(*psa->sa);
254 if (psa->error)
255 _exit(127);
256 }
257 if (psa->fa != NULL) {
258 psa->error = process_file_actions(*psa->fa);
259 if (psa->error)
260 _exit(127);
261 }
262 envp = psa->envp != NULL ? psa->envp : environ;
263 if (psa->use_env_path)
264 execvpe(psa->path, psa->argv, envp);
265 else
266 _execve(psa->path, psa->argv, envp);
267 psa->error = errno;
268
269 /* This is called in such a way that it must not exit. */
270 _exit(127);
271 }
272
273 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)274 do_posix_spawn(pid_t *pid, const char *path,
275 const posix_spawn_file_actions_t *fa,
276 const posix_spawnattr_t *sa,
277 char * const argv[], char * const envp[], int use_env_path)
278 {
279 struct posix_spawn_args psa;
280 pid_t p;
281 #ifdef _RFORK_THREAD_STACK_SIZE
282 char *stack;
283 size_t cnt, stacksz;
284
285 stacksz = _RFORK_THREAD_STACK_SIZE;
286 if (use_env_path) {
287 /*
288 * We need to make sure we have enough room on the stack for the
289 * potential alloca() in execvPe if it gets kicked back an
290 * ENOEXEC from execve(2), plus the original buffer we gave
291 * ourselves; this protects us in the event that the caller
292 * intentionally or inadvertently supplies enough arguments to
293 * make us blow past the stack we've allocated from it.
294 */
295 for (cnt = 0; argv[cnt] != NULL; ++cnt)
296 ;
297 stacksz += MAX(3, cnt + 2) * sizeof(char *);
298 stacksz = PSPAWN_STACK_ALIGN(stacksz);
299 }
300
301 /*
302 * aligned_alloc is not safe to use here, because we can't guarantee
303 * that aligned_alloc and free will be provided by the same
304 * implementation. We've actively hit at least one application that
305 * will provide its own malloc/free but not aligned_alloc leading to
306 * a free by the wrong allocator.
307 */
308 stack = malloc(stacksz);
309 if (stack == NULL)
310 return (ENOMEM);
311 stacksz = (((uintptr_t)stack + stacksz) & ~PSPAWN_STACK_ALIGNBYTES) -
312 (uintptr_t)stack;
313 #endif
314 psa.path = path;
315 psa.fa = fa;
316 psa.sa = sa;
317 psa.argv = argv;
318 psa.envp = envp;
319 psa.use_env_path = use_env_path;
320 psa.error = 0;
321
322 /*
323 * Passing RFSPAWN to rfork(2) gives us effectively a vfork that drops
324 * non-ignored signal handlers. We'll fall back to the slightly less
325 * ideal vfork(2) if we get an EINVAL from rfork -- this should only
326 * happen with newer libc on older kernel that doesn't accept
327 * RFSPAWN.
328 *
329 * Combination of vfork() (or its equivalent rfork() form) and
330 * a special property of the libthr rtld locks ensure that
331 * rtld is operational in the child. In particular, libthr
332 * rtld locks do not store owner' tid into the lock word.
333 */
334 #ifdef _RFORK_THREAD_STACK_SIZE
335 /*
336 * x86 stores the return address on the stack, so rfork(2) cannot work
337 * as-is because the child would clobber the return address of the
338 * parent. Because of this, we must use rfork_thread instead while
339 * almost every other arch stores the return address in a register.
340 */
341 p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa);
342 free(stack);
343 #else
344 p = rfork(RFSPAWN);
345 if (p == 0)
346 /* _posix_spawn_thr does not return */
347 _posix_spawn_thr(&psa);
348 #endif
349 /*
350 * The above block should leave us in a state where we've either
351 * succeeded and we're ready to process the results, or we need to
352 * fallback to vfork() if the kernel didn't like RFSPAWN.
353 */
354
355 if (p == -1 && errno == EINVAL) {
356 p = vfork();
357 if (p == 0)
358 /* _posix_spawn_thr does not return */
359 _posix_spawn_thr(&psa);
360 }
361 if (p == -1)
362 return (errno);
363 if (psa.error != 0)
364 /* Failed; ready to reap */
365 _waitpid(p, NULL, WNOHANG);
366 else if (pid != NULL)
367 /* exec succeeded */
368 *pid = p;
369 return (psa.error);
370 }
371
372 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[])373 posix_spawn(pid_t *pid, const char *path,
374 const posix_spawn_file_actions_t *fa,
375 const posix_spawnattr_t *sa,
376 char * const argv[], char * const envp[])
377 {
378 return (do_posix_spawn(pid, path, fa, sa, argv, envp, 0));
379 }
380
381 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[])382 posix_spawnp(pid_t *pid, const char *path,
383 const posix_spawn_file_actions_t *fa,
384 const posix_spawnattr_t *sa,
385 char * const argv[], char * const envp[])
386 {
387 return (do_posix_spawn(pid, path, fa, sa, argv, envp, 1));
388 }
389
390 /*
391 * File descriptor actions
392 */
393
394 int
posix_spawn_file_actions_init(posix_spawn_file_actions_t * ret)395 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret)
396 {
397 posix_spawn_file_actions_t fa;
398
399 fa = malloc(sizeof(struct __posix_spawn_file_actions));
400 if (fa == NULL)
401 return (-1);
402
403 STAILQ_INIT(&fa->fa_list);
404 *ret = fa;
405 return (0);
406 }
407
408 int
posix_spawn_file_actions_destroy(posix_spawn_file_actions_t * fa)409 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
410 {
411 posix_spawn_file_actions_entry_t *fae;
412
413 while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) {
414 /* Remove file action entry from the queue */
415 STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list);
416
417 /* Deallocate file action entry */
418 if (fae->fae_action == FAE_OPEN ||
419 fae->fae_action == FAE_CHDIR)
420 free(fae->fae_path);
421 free(fae);
422 }
423
424 free(*fa);
425 return (0);
426 }
427
428 int
posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,int fildes,const char * __restrict path,int oflag,mode_t mode)429 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,
430 int fildes, const char * __restrict path, int oflag, mode_t mode)
431 {
432 posix_spawn_file_actions_entry_t *fae;
433 int error;
434
435 if (fildes < 0)
436 return (EBADF);
437
438 /* Allocate object */
439 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
440 if (fae == NULL)
441 return (errno);
442
443 /* Set values and store in queue */
444 fae->fae_action = FAE_OPEN;
445 fae->fae_path = strdup(path);
446 if (fae->fae_path == NULL) {
447 error = errno;
448 free(fae);
449 return (error);
450 }
451 fae->fae_fildes = fildes;
452 fae->fae_oflag = oflag;
453 fae->fae_mode = mode;
454
455 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
456 return (0);
457 }
458
459 int
posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t * fa,int fildes,int newfildes)460 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa,
461 int fildes, int newfildes)
462 {
463 posix_spawn_file_actions_entry_t *fae;
464
465 if (fildes < 0 || newfildes < 0)
466 return (EBADF);
467
468 /* Allocate object */
469 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
470 if (fae == NULL)
471 return (errno);
472
473 /* Set values and store in queue */
474 fae->fae_action = FAE_DUP2;
475 fae->fae_fildes = fildes;
476 fae->fae_newfildes = newfildes;
477
478 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
479 return (0);
480 }
481
482 int
posix_spawn_file_actions_addclose(posix_spawn_file_actions_t * fa,int fildes)483 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa,
484 int fildes)
485 {
486 posix_spawn_file_actions_entry_t *fae;
487
488 if (fildes < 0)
489 return (EBADF);
490
491 /* Allocate object */
492 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
493 if (fae == NULL)
494 return (errno);
495
496 /* Set values and store in queue */
497 fae->fae_action = FAE_CLOSE;
498 fae->fae_fildes = fildes;
499
500 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
501 return (0);
502 }
503
504 int
posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t * __restrict fa,const char * __restrict path)505 posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *
506 __restrict fa, const char *__restrict path)
507 {
508 posix_spawn_file_actions_entry_t *fae;
509 int error;
510
511 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
512 if (fae == NULL)
513 return (errno);
514
515 fae->fae_action = FAE_CHDIR;
516 fae->fae_path = strdup(path);
517 if (fae->fae_path == NULL) {
518 error = errno;
519 free(fae);
520 return (error);
521 }
522
523 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
524 return (0);
525 }
526
527 int
posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t * __restrict fa,int fildes)528 posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *__restrict fa,
529 int fildes)
530 {
531 posix_spawn_file_actions_entry_t *fae;
532
533 if (fildes < 0)
534 return (EBADF);
535
536 /* Allocate object */
537 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
538 if (fae == NULL)
539 return (errno);
540
541 fae->fae_action = FAE_FCHDIR;
542 fae->fae_fildes = fildes;
543
544 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
545 return (0);
546 }
547
548 int
posix_spawn_file_actions_addclosefrom_np(posix_spawn_file_actions_t * __restrict fa,int from)549 posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t *
550 __restrict fa, int from)
551 {
552 posix_spawn_file_actions_entry_t *fae;
553
554 if (from < 0)
555 return (EBADF);
556
557 /* Allocate object */
558 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
559 if (fae == NULL)
560 return (errno);
561
562 fae->fae_action = FAE_CLOSEFROM;
563 fae->fae_fildes = from;
564
565 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
566 return (0);
567 }
568
569 /*
570 * Spawn attributes
571 */
572
573 int
posix_spawnattr_init(posix_spawnattr_t * ret)574 posix_spawnattr_init(posix_spawnattr_t *ret)
575 {
576 posix_spawnattr_t sa;
577
578 sa = calloc(1, sizeof(struct __posix_spawnattr));
579 if (sa == NULL)
580 return (errno);
581
582 /* Set defaults as specified by POSIX, cleared above */
583 *ret = sa;
584 return (0);
585 }
586
587 int
posix_spawnattr_destroy(posix_spawnattr_t * sa)588 posix_spawnattr_destroy(posix_spawnattr_t *sa)
589 {
590 free(*sa);
591 return (0);
592 }
593
594 int
posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,short * __restrict flags)595 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,
596 short * __restrict flags)
597 {
598 *flags = (*sa)->sa_flags;
599 return (0);
600 }
601
602 int
posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,pid_t * __restrict pgroup)603 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,
604 pid_t * __restrict pgroup)
605 {
606 *pgroup = (*sa)->sa_pgroup;
607 return (0);
608 }
609
610 int
posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,struct sched_param * __restrict schedparam)611 posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,
612 struct sched_param * __restrict schedparam)
613 {
614 *schedparam = (*sa)->sa_schedparam;
615 return (0);
616 }
617
618 int
posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,int * __restrict schedpolicy)619 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,
620 int * __restrict schedpolicy)
621 {
622 *schedpolicy = (*sa)->sa_schedpolicy;
623 return (0);
624 }
625
626 int
posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,sigset_t * __restrict sigdefault)627 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,
628 sigset_t * __restrict sigdefault)
629 {
630 *sigdefault = (*sa)->sa_sigdefault;
631 return (0);
632 }
633
634 int
posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,sigset_t * __restrict sigmask)635 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,
636 sigset_t * __restrict sigmask)
637 {
638 *sigmask = (*sa)->sa_sigmask;
639 return (0);
640 }
641
642 int
posix_spawnattr_setflags(posix_spawnattr_t * sa,short flags)643 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags)
644 {
645 if ((flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP |
646 POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER |
647 POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
648 POSIX_SPAWN_DISABLE_ASLR_NP)) != 0)
649 return (EINVAL);
650 (*sa)->sa_flags = flags;
651 return (0);
652 }
653
654 int
posix_spawnattr_setpgroup(posix_spawnattr_t * sa,pid_t pgroup)655 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup)
656 {
657 (*sa)->sa_pgroup = pgroup;
658 return (0);
659 }
660
661 int
posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,const struct sched_param * __restrict schedparam)662 posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,
663 const struct sched_param * __restrict schedparam)
664 {
665 (*sa)->sa_schedparam = *schedparam;
666 return (0);
667 }
668
669 int
posix_spawnattr_setschedpolicy(posix_spawnattr_t * sa,int schedpolicy)670 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy)
671 {
672 (*sa)->sa_schedpolicy = schedpolicy;
673 return (0);
674 }
675
676 int
posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,const sigset_t * __restrict sigdefault)677 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,
678 const sigset_t * __restrict sigdefault)
679 {
680 (*sa)->sa_sigdefault = *sigdefault;
681 return (0);
682 }
683
684 int
posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,const sigset_t * __restrict sigmask)685 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,
686 const sigset_t * __restrict sigmask)
687 {
688 (*sa)->sa_sigmask = *sigmask;
689 return (0);
690 }
691