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