xref: /illumos-gate/usr/src/lib/libc/port/threads/spawn.c (revision e0a5e15f4abf222f81c31cbe9dd1bca17d27ba57)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include "lint.h"
28 #include "thr_uberdata.h"
29 #include <sys/libc_kernel.h>
30 #include <sys/procset.h>
31 #include <sys/fork.h>
32 #include <dirent.h>
33 #include <alloca.h>
34 #include <spawn.h>
35 
36 #define	ALL_POSIX_SPAWN_FLAGS			\
37 		(POSIX_SPAWN_RESETIDS |		\
38 		POSIX_SPAWN_SETPGROUP |		\
39 		POSIX_SPAWN_SETSIGDEF |		\
40 		POSIX_SPAWN_SETSIGMASK |	\
41 		POSIX_SPAWN_SETSCHEDPARAM |	\
42 		POSIX_SPAWN_SETSCHEDULER |	\
43 		POSIX_SPAWN_SETSIGIGN_NP |	\
44 		POSIX_SPAWN_NOSIGCHLD_NP |	\
45 		POSIX_SPAWN_WAITPID_NP |	\
46 		POSIX_SPAWN_NOEXECERR_NP)
47 
48 typedef struct {
49 	int		sa_psflags;	/* POSIX_SPAWN_* flags */
50 	int		sa_priority;
51 	int		sa_schedpolicy;
52 	pid_t		sa_pgroup;
53 	sigset_t	sa_sigdefault;
54 	sigset_t	sa_sigignore;
55 	sigset_t	sa_sigmask;
56 } spawn_attr_t;
57 
58 typedef struct file_attr {
59 	struct file_attr *fa_next;	/* circular list of file actions */
60 	struct file_attr *fa_prev;
61 	enum {FA_OPEN, FA_CLOSE, FA_DUP2, FA_CLOSEFROM} fa_type;
62 	int		fa_need_dirbuf;	/* only consulted in the head action */
63 	char		*fa_path;	/* copied pathname for open() */
64 	uint_t		fa_pathsize;	/* size of fa_path[] array */
65 	int		fa_oflag;	/* oflag for open() */
66 	mode_t		fa_mode;	/* mode for open() */
67 	int		fa_filedes;	/* file descriptor for open()/close() */
68 	int		fa_newfiledes;	/* new file descriptor for dup2() */
69 } file_attr_t;
70 
71 #if defined(_LP64)
72 #define	__open64	__open
73 #define	getdents64	getdents
74 #define	dirent64_t	dirent_t
75 #else
76 extern int getdents64(int, dirent64_t *, size_t);
77 #endif
78 
79 /*
80  * Support function:
81  * Close all open file descriptors greater than or equal to lowfd.
82  * This is executed in the child of vfork(), so we must not call
83  * opendir() / readdir() because that would alter the parent's
84  * address space.  We use the low-level getdents64() system call.
85  * Return non-zero on error.
86  */
87 static int
88 spawn_closefrom(int lowfd, void *buf)
89 {
90 	int procfd;
91 	int fd;
92 	int buflen;
93 	dirent64_t *dp;
94 	dirent64_t *dpend;
95 
96 	if (lowfd <  0)
97 		lowfd = 0;
98 
99 	/*
100 	 * Close lowfd right away as a hedge against failing
101 	 * to open the /proc file descriptor directory due
102 	 * all file descriptors being currently used up.
103 	 */
104 	(void) __close(lowfd++);
105 
106 	if ((procfd = __open64("/proc/self/fd", O_RDONLY, 0)) < 0) {
107 		/*
108 		 * We could not open the /proc file descriptor directory.
109 		 * Just fail and be done with it.
110 		 */
111 		return (-1);
112 	}
113 
114 	for (;;) {
115 		/*
116 		 * Collect a bunch of open file descriptors and close them.
117 		 * Repeat until the directory is exhausted.
118 		 */
119 		dp = (dirent64_t *)buf;
120 		if ((buflen = getdents64(procfd, dp, DIRBUF)) <= 0) {
121 			(void) __close(procfd);
122 			break;
123 		}
124 		dpend = (dirent64_t *)((uintptr_t)buf + buflen);
125 		do {
126 			/* skip '.', '..' and procfd */
127 			if (dp->d_name[0] != '.' &&
128 			    (fd = atoi(dp->d_name)) != procfd &&
129 			    fd >= lowfd)
130 				(void) __close(fd);
131 			dp = (dirent64_t *)((uintptr_t)dp + dp->d_reclen);
132 		} while (dp < dpend);
133 	}
134 
135 	return (0);
136 }
137 
138 static int
139 perform_flag_actions(spawn_attr_t *sap)
140 {
141 	int sig;
142 	struct sigaction action;
143 
144 	if (sap->sa_psflags & POSIX_SPAWN_SETSIGMASK) {
145 		(void) __lwp_sigmask(SIG_SETMASK, &sap->sa_sigmask);
146 	}
147 
148 	if (sap->sa_psflags & POSIX_SPAWN_SETSIGIGN_NP) {
149 		(void) memset(&action, 0, sizeof (action));
150 		action.sa_handler = SIG_IGN;
151 		for (sig = 1; sig < NSIG; sig++) {
152 			if (sigismember(&sap->sa_sigignore, sig))
153 				(void) __sigaction(sig, &action, NULL);
154 		}
155 	}
156 
157 	if (sap->sa_psflags & POSIX_SPAWN_SETSIGDEF) {
158 		(void) memset(&action, 0, sizeof (action));
159 		action.sa_handler = SIG_DFL;
160 		for (sig = 1; sig < NSIG; sig++) {
161 			if (sigismember(&sap->sa_sigdefault, sig))
162 				(void) __sigaction(sig, &action, NULL);
163 		}
164 	}
165 
166 	if (sap->sa_psflags & POSIX_SPAWN_RESETIDS) {
167 		if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
168 			return (errno);
169 	}
170 
171 	if (sap->sa_psflags & POSIX_SPAWN_SETPGROUP) {
172 		if (setpgid(0, sap->sa_pgroup) != 0)
173 			return (errno);
174 	}
175 
176 	if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDULER) {
177 		if (setparam(P_LWPID, P_MYID,
178 		    sap->sa_schedpolicy, sap->sa_priority) == -1)
179 			return (errno);
180 	} else if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDPARAM) {
181 		if (setprio(P_LWPID, P_MYID, sap->sa_priority, NULL) == -1)
182 			return (errno);
183 	}
184 
185 	return (0);
186 }
187 
188 static int
189 perform_file_actions(file_attr_t *fap, void *dirbuf)
190 {
191 	file_attr_t *froot = fap;
192 	int fd;
193 
194 	do {
195 		switch (fap->fa_type) {
196 		case FA_OPEN:
197 			fd = __open(fap->fa_path, fap->fa_oflag, fap->fa_mode);
198 			if (fd < 0)
199 				return (errno);
200 			if (fd != fap->fa_filedes) {
201 				if (__fcntl(fd, F_DUP2FD, fap->fa_filedes) < 0)
202 					return (errno);
203 				(void) __close(fd);
204 			}
205 			break;
206 		case FA_CLOSE:
207 			if (__close(fap->fa_filedes) == -1 &&
208 			    errno != EBADF)	/* already closed, no error */
209 				return (errno);
210 			break;
211 		case FA_DUP2:
212 			fd = __fcntl(fap->fa_filedes, F_DUP2FD,
213 			    fap->fa_newfiledes);
214 			if (fd < 0)
215 				return (errno);
216 			break;
217 		case FA_CLOSEFROM:
218 			if (spawn_closefrom(fap->fa_filedes, dirbuf))
219 				return (errno);
220 			break;
221 		}
222 	} while ((fap = fap->fa_next) != froot);
223 
224 	return (0);
225 }
226 
227 static int
228 forkflags(spawn_attr_t *sap)
229 {
230 	int flags = 0;
231 
232 	if (sap != NULL) {
233 		if (sap->sa_psflags & POSIX_SPAWN_NOSIGCHLD_NP)
234 			flags |= FORK_NOSIGCHLD;
235 		if (sap->sa_psflags & POSIX_SPAWN_WAITPID_NP)
236 			flags |= FORK_WAITPID;
237 	}
238 
239 	return (flags);
240 }
241 
242 /*
243  * set_error() / get_error() are used to guarantee that the local variable
244  * 'error' is set correctly in memory on return from vfork() in the parent.
245  */
246 
247 static int
248 set_error(int *errp, int err)
249 {
250 	return (*errp = err);
251 }
252 
253 static int
254 get_error(int *errp)
255 {
256 	return (*errp);
257 }
258 
259 /*
260  * For MT safety, do not invoke the dynamic linker after calling vfork().
261  * If some other thread was in the dynamic linker when this thread's parent
262  * called vfork() then the dynamic linker's lock would still be held here
263  * (with a defunct owner) and we would deadlock ourself if we invoked it.
264  *
265  * Therefore, all of the functions we call here after returning from
266  * vforkx() in the child are not and must never be exported from libc
267  * as global symbols.  To do so would risk invoking the dynamic linker.
268  */
269 
270 int
271 posix_spawn(
272 	pid_t *pidp,
273 	const char *path,
274 	const posix_spawn_file_actions_t *file_actions,
275 	const posix_spawnattr_t *attrp,
276 	char *const argv[],
277 	char *const envp[])
278 {
279 	spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL;
280 	file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL;
281 	void *dirbuf = NULL;
282 	int error;		/* this will be set by the child */
283 	pid_t pid;
284 
285 	if (attrp != NULL && sap == NULL)
286 		return (EINVAL);
287 
288 	if (fap != NULL && fap->fa_need_dirbuf) {
289 		/*
290 		 * Preallocate the buffer for the call to getdents64() in
291 		 * spawn_closefrom() since we can't do it in the vfork() child.
292 		 */
293 		if ((dirbuf = lmalloc(DIRBUF)) == NULL)
294 			return (ENOMEM);
295 	}
296 
297 	switch (pid = vforkx(forkflags(sap))) {
298 	case 0:			/* child */
299 		break;
300 	case -1:		/* parent, failure */
301 		if (dirbuf)
302 			lfree(dirbuf, DIRBUF);
303 		return (errno);
304 	default:		/* parent, success */
305 		/*
306 		 * We don't get here until the child exec()s or exit()s
307 		 */
308 		if (pidp != NULL && get_error(&error) == 0)
309 			*pidp = pid;
310 		if (dirbuf)
311 			lfree(dirbuf, DIRBUF);
312 		return (get_error(&error));
313 	}
314 
315 	if (sap != NULL)
316 		if (set_error(&error, perform_flag_actions(sap)) != 0)
317 			_exit(_EVAPORATE);
318 
319 	if (fap != NULL)
320 		if (set_error(&error, perform_file_actions(fap, dirbuf)) != 0)
321 			_exit(_EVAPORATE);
322 
323 	(void) set_error(&error, 0);
324 	(void) execve(path, argv, envp);
325 	if (sap != NULL && (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP))
326 		_exit(127);
327 	(void) set_error(&error, errno);
328 	_exit(_EVAPORATE);
329 	return (0);	/* not reached */
330 }
331 
332 /*
333  * Much of posix_spawnp() blatently stolen from execvp() (port/gen/execvp.c).
334  */
335 
336 extern int libc__xpg4;
337 
338 static const char *
339 execat(const char *s1, const char *s2, char *si)
340 {
341 	int cnt = PATH_MAX + 1;
342 	char *s;
343 	char c;
344 
345 	for (s = si; (c = *s1) != '\0' && c != ':'; s1++) {
346 		if (cnt > 0) {
347 			*s++ = c;
348 			cnt--;
349 		}
350 	}
351 	if (si != s && cnt > 0) {
352 		*s++ = '/';
353 		cnt--;
354 	}
355 	for (; (c = *s2) != '\0' && cnt > 0; s2++) {
356 		*s++ = c;
357 		cnt--;
358 	}
359 	*s = '\0';
360 	return (*s1? ++s1: NULL);
361 }
362 
363 /* ARGSUSED */
364 int
365 posix_spawnp(
366 	pid_t *pidp,
367 	const char *file,
368 	const posix_spawn_file_actions_t *file_actions,
369 	const posix_spawnattr_t *attrp,
370 	char *const argv[],
371 	char *const envp[])
372 {
373 	spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL;
374 	file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL;
375 	void *dirbuf = NULL;
376 	const char *pathstr = (strchr(file, '/') == NULL)? getenv("PATH") : "";
377 	int xpg4 = libc__xpg4;
378 	int error = 0;		/* this will be set by the child */
379 	char path[PATH_MAX+4];
380 	const char *cp;
381 	pid_t pid;
382 	char **newargs;
383 	int argc;
384 	int i;
385 	static const char *sun_path = "/bin/sh";
386 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
387 	static const char *shell = "sh";
388 
389 	if (attrp != NULL && sap == NULL)
390 		return (EINVAL);
391 
392 	if (*file == '\0')
393 		return (EACCES);
394 
395 	if (fap != NULL && fap->fa_need_dirbuf) {
396 		/*
397 		 * Preallocate the buffer for the call to getdents64() in
398 		 * spawn_closefrom() since we can't do it in the vfork() child.
399 		 */
400 		if ((dirbuf = lmalloc(DIRBUF)) == NULL)
401 			return (ENOMEM);
402 	}
403 
404 	/*
405 	 * We may need to invoke the shell with a slightly modified
406 	 * argv[] array.  To do this we need to preallocate the array.
407 	 * We must call alloca() before calling vfork() because doing
408 	 * it after vfork() (in the child) would corrupt the parent.
409 	 */
410 	for (argc = 0; argv[argc] != NULL; argc++)
411 		continue;
412 	newargs = alloca((argc + 2) * sizeof (char *));
413 
414 	switch (pid = vforkx(forkflags(sap))) {
415 	case 0:			/* child */
416 		break;
417 	case -1:		/* parent, failure */
418 		if (dirbuf)
419 			lfree(dirbuf, DIRBUF);
420 		return (errno);
421 	default:		/* parent, success */
422 		/*
423 		 * We don't get here until the child exec()s or exit()s
424 		 */
425 		if (pidp != NULL && get_error(&error) == 0)
426 			*pidp = pid;
427 		if (dirbuf)
428 			lfree(dirbuf, DIRBUF);
429 		return (get_error(&error));
430 	}
431 
432 	if (sap != NULL)
433 		if (set_error(&error, perform_flag_actions(sap)) != 0)
434 			_exit(_EVAPORATE);
435 
436 	if (fap != NULL)
437 		if (set_error(&error, perform_file_actions(fap, dirbuf)) != 0)
438 			_exit(_EVAPORATE);
439 
440 	if (pathstr == NULL) {
441 		/*
442 		 * XPG4:  pathstr is equivalent to _CS_PATH, except that
443 		 * :/usr/sbin is appended when root, and pathstr must end
444 		 * with a colon when not root.  Keep these paths in sync
445 		 * with _CS_PATH in confstr.c.  Note that pathstr must end
446 		 * with a colon when not root so that when file doesn't
447 		 * contain '/', the last call to execat() will result in an
448 		 * attempt to execv file from the current directory.
449 		 */
450 		if (geteuid() == 0 || getuid() == 0) {
451 			if (!xpg4)
452 				pathstr = "/usr/sbin:/usr/ccs/bin:/usr/bin";
453 			else
454 				pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
455 				    "/usr/bin:/opt/SUNWspro/bin:/usr/sbin";
456 		} else {
457 			if (!xpg4)
458 				pathstr = "/usr/ccs/bin:/usr/bin:";
459 			else
460 				pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
461 				    "/usr/bin:/opt/SUNWspro/bin:";
462 		}
463 	}
464 
465 	cp = pathstr;
466 	do {
467 		cp = execat(cp, file, path);
468 		/*
469 		 * 4025035 and 4038378
470 		 * if a filename begins with a "-" prepend "./" so that
471 		 * the shell can't interpret it as an option
472 		 */
473 		if (*path == '-') {
474 			char *s;
475 
476 			for (s = path; *s != '\0'; s++)
477 				continue;
478 			for (; s >= path; s--)
479 				*(s + 2) = *s;
480 			path[0] = '.';
481 			path[1] = '/';
482 		}
483 		(void) set_error(&error, 0);
484 		(void) execve(path, argv, envp);
485 		if (set_error(&error, errno) == ENOEXEC) {
486 			newargs[0] = (char *)shell;
487 			newargs[1] = path;
488 			for (i = 1; i <= argc; i++)
489 				newargs[i + 1] = argv[i];
490 			(void) set_error(&error, 0);
491 			(void) execve(xpg4? xpg4_path : sun_path,
492 			    newargs, envp);
493 			if (sap != NULL &&
494 			    (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP))
495 				_exit(127);
496 			(void) set_error(&error, errno);
497 			_exit(_EVAPORATE);
498 		}
499 	} while (cp);
500 
501 	if (sap != NULL &&
502 	    (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP)) {
503 		(void) set_error(&error, 0);
504 		_exit(127);
505 	}
506 	_exit(_EVAPORATE);
507 	return (0);	/* not reached */
508 }
509 
510 int
511 posix_spawn_file_actions_init(
512 	posix_spawn_file_actions_t *file_actions)
513 {
514 	file_actions->__file_attrp = NULL;
515 	return (0);
516 }
517 
518 int
519 posix_spawn_file_actions_destroy(
520 	posix_spawn_file_actions_t *file_actions)
521 {
522 	file_attr_t *froot = file_actions->__file_attrp;
523 	file_attr_t *fap;
524 	file_attr_t *next;
525 
526 	if ((fap = froot) != NULL) {
527 		do {
528 			next = fap->fa_next;
529 			if (fap->fa_type == FA_OPEN)
530 				lfree(fap->fa_path, fap->fa_pathsize);
531 			lfree(fap, sizeof (*fap));
532 		} while ((fap = next) != froot);
533 	}
534 	file_actions->__file_attrp = NULL;
535 	return (0);
536 }
537 
538 static void
539 add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap)
540 {
541 	file_attr_t *froot = file_actions->__file_attrp;
542 
543 	if (froot == NULL) {
544 		fap->fa_next = fap->fa_prev = fap;
545 		file_actions->__file_attrp = froot = fap;
546 	} else {
547 		fap->fa_next = froot;
548 		fap->fa_prev = froot->fa_prev;
549 		froot->fa_prev->fa_next = fap;
550 		froot->fa_prev = fap;
551 	}
552 
553 	/*
554 	 * Once set, __file_attrp no longer changes, so this assignment
555 	 * always goes into the first element in the list, as required.
556 	 */
557 	if (fap->fa_type == FA_CLOSEFROM)
558 		froot->fa_need_dirbuf = 1;
559 }
560 
561 int
562 posix_spawn_file_actions_addopen(
563 	posix_spawn_file_actions_t *file_actions,
564 	int filedes,
565 	const char *path,
566 	int oflag,
567 	mode_t mode)
568 {
569 	file_attr_t *fap;
570 
571 	if (filedes < 0)
572 		return (EBADF);
573 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
574 		return (ENOMEM);
575 
576 	fap->fa_pathsize = strlen(path) + 1;
577 	if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
578 		lfree(fap, sizeof (*fap));
579 		return (ENOMEM);
580 	}
581 	(void) strcpy(fap->fa_path, path);
582 
583 	fap->fa_type = FA_OPEN;
584 	fap->fa_oflag = oflag;
585 	fap->fa_mode = mode;
586 	fap->fa_filedes = filedes;
587 	add_file_attr(file_actions, fap);
588 
589 	return (0);
590 }
591 
592 int
593 posix_spawn_file_actions_addclose(
594 	posix_spawn_file_actions_t *file_actions,
595 	int filedes)
596 {
597 	file_attr_t *fap;
598 
599 	if (filedes < 0)
600 		return (EBADF);
601 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
602 		return (ENOMEM);
603 
604 	fap->fa_type = FA_CLOSE;
605 	fap->fa_filedes = filedes;
606 	add_file_attr(file_actions, fap);
607 
608 	return (0);
609 }
610 
611 int
612 posix_spawn_file_actions_adddup2(
613 	posix_spawn_file_actions_t *file_actions,
614 	int filedes,
615 	int newfiledes)
616 {
617 	file_attr_t *fap;
618 
619 	if (filedes < 0 || newfiledes < 0)
620 		return (EBADF);
621 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
622 		return (ENOMEM);
623 
624 	fap->fa_type = FA_DUP2;
625 	fap->fa_filedes = filedes;
626 	fap->fa_newfiledes = newfiledes;
627 	add_file_attr(file_actions, fap);
628 
629 	return (0);
630 }
631 
632 int
633 posix_spawn_file_actions_addclosefrom_np(
634 	posix_spawn_file_actions_t *file_actions,
635 	int lowfiledes)
636 {
637 	file_attr_t *fap;
638 
639 	if (lowfiledes < 0)
640 		return (EBADF);
641 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
642 		return (ENOMEM);
643 	fap->fa_type = FA_CLOSEFROM;
644 	fap->fa_filedes = lowfiledes;
645 	add_file_attr(file_actions, fap);
646 
647 	return (0);
648 }
649 
650 int
651 posix_spawnattr_init(
652 	posix_spawnattr_t *attr)
653 {
654 	if ((attr->__spawn_attrp = lmalloc(sizeof (posix_spawnattr_t))) == NULL)
655 		return (ENOMEM);
656 	/*
657 	 * Add default stuff here?
658 	 */
659 	return (0);
660 }
661 
662 int
663 posix_spawnattr_destroy(
664 	posix_spawnattr_t *attr)
665 {
666 	spawn_attr_t *sap = attr->__spawn_attrp;
667 
668 	if (sap == NULL)
669 		return (EINVAL);
670 
671 	/*
672 	 * deallocate stuff here?
673 	 */
674 	lfree(sap, sizeof (*sap));
675 	attr->__spawn_attrp = NULL;
676 	return (0);
677 }
678 
679 int
680 posix_spawnattr_setflags(
681 	posix_spawnattr_t *attr,
682 	short flags)
683 {
684 	spawn_attr_t *sap = attr->__spawn_attrp;
685 
686 	if (sap == NULL ||
687 	    (flags & ~ALL_POSIX_SPAWN_FLAGS))
688 		return (EINVAL);
689 
690 	sap->sa_psflags = flags;
691 	return (0);
692 }
693 
694 int
695 posix_spawnattr_getflags(
696 	const posix_spawnattr_t *attr,
697 	short *flags)
698 {
699 	spawn_attr_t *sap = attr->__spawn_attrp;
700 
701 	if (sap == NULL)
702 		return (EINVAL);
703 
704 	*flags = sap->sa_psflags;
705 	return (0);
706 }
707 
708 int
709 posix_spawnattr_setpgroup(
710 	posix_spawnattr_t *attr,
711 	pid_t pgroup)
712 {
713 	spawn_attr_t *sap = attr->__spawn_attrp;
714 
715 	if (sap == NULL)
716 		return (EINVAL);
717 
718 	sap->sa_pgroup = pgroup;
719 	return (0);
720 }
721 
722 int
723 posix_spawnattr_getpgroup(
724 	const posix_spawnattr_t *attr,
725 	pid_t *pgroup)
726 {
727 	spawn_attr_t *sap = attr->__spawn_attrp;
728 
729 	if (sap == NULL)
730 		return (EINVAL);
731 
732 	*pgroup = sap->sa_pgroup;
733 	return (0);
734 }
735 
736 int
737 posix_spawnattr_setschedparam(
738 	posix_spawnattr_t *attr,
739 	const struct sched_param *schedparam)
740 {
741 	spawn_attr_t *sap = attr->__spawn_attrp;
742 
743 	if (sap == NULL)
744 		return (EINVAL);
745 
746 	/*
747 	 * Check validity?
748 	 */
749 	sap->sa_priority = schedparam->sched_priority;
750 	return (0);
751 }
752 
753 int
754 posix_spawnattr_getschedparam(
755 	const posix_spawnattr_t *attr,
756 	struct sched_param *schedparam)
757 {
758 	spawn_attr_t *sap = attr->__spawn_attrp;
759 
760 	if (sap == NULL)
761 		return (EINVAL);
762 
763 	schedparam->sched_priority = sap->sa_priority;
764 	return (0);
765 }
766 
767 int
768 posix_spawnattr_setschedpolicy(
769 	posix_spawnattr_t *attr,
770 	int schedpolicy)
771 {
772 	spawn_attr_t *sap = attr->__spawn_attrp;
773 
774 	if (sap == NULL || schedpolicy == SCHED_SYS)
775 		return (EINVAL);
776 
777 	/*
778 	 * Cache the policy information for later use
779 	 * by the vfork() child of posix_spawn().
780 	 */
781 	if (get_info_by_policy(schedpolicy) == NULL)
782 		return (errno);
783 
784 	sap->sa_schedpolicy = schedpolicy;
785 	return (0);
786 }
787 
788 int
789 posix_spawnattr_getschedpolicy(
790 	const posix_spawnattr_t *attr,
791 	int *schedpolicy)
792 {
793 	spawn_attr_t *sap = attr->__spawn_attrp;
794 
795 	if (sap == NULL)
796 		return (EINVAL);
797 
798 	*schedpolicy = sap->sa_schedpolicy;
799 	return (0);
800 }
801 
802 int
803 posix_spawnattr_setsigdefault(
804 	posix_spawnattr_t *attr,
805 	const sigset_t *sigdefault)
806 {
807 	spawn_attr_t *sap = attr->__spawn_attrp;
808 
809 	if (sap == NULL)
810 		return (EINVAL);
811 
812 	sap->sa_sigdefault = *sigdefault;
813 	return (0);
814 }
815 
816 int
817 posix_spawnattr_getsigdefault(
818 	const posix_spawnattr_t *attr,
819 	sigset_t *sigdefault)
820 {
821 	spawn_attr_t *sap = attr->__spawn_attrp;
822 
823 	if (sap == NULL)
824 		return (EINVAL);
825 
826 	*sigdefault = sap->sa_sigdefault;
827 	return (0);
828 }
829 
830 int
831 posix_spawnattr_setsigignore_np(
832 	posix_spawnattr_t *attr,
833 	const sigset_t *sigignore)
834 {
835 	spawn_attr_t *sap = attr->__spawn_attrp;
836 
837 	if (sap == NULL)
838 		return (EINVAL);
839 
840 	sap->sa_sigignore = *sigignore;
841 	return (0);
842 }
843 
844 int
845 posix_spawnattr_getsigignore_np(
846 	const posix_spawnattr_t *attr,
847 	sigset_t *sigignore)
848 {
849 	spawn_attr_t *sap = attr->__spawn_attrp;
850 
851 	if (sap == NULL)
852 		return (EINVAL);
853 
854 	*sigignore = sap->sa_sigignore;
855 	return (0);
856 }
857 
858 int
859 posix_spawnattr_setsigmask(
860 	posix_spawnattr_t *attr,
861 	const sigset_t *sigmask)
862 {
863 	spawn_attr_t *sap = attr->__spawn_attrp;
864 
865 	if (sap == NULL)
866 		return (EINVAL);
867 
868 	sap->sa_sigmask = *sigmask;
869 	return (0);
870 }
871 
872 int
873 posix_spawnattr_getsigmask(
874 	const posix_spawnattr_t *attr,
875 	sigset_t *sigmask)
876 {
877 	spawn_attr_t *sap = attr->__spawn_attrp;
878 
879 	if (sap == NULL)
880 		return (EINVAL);
881 
882 	*sigmask = sap->sa_sigmask;
883 	return (0);
884 }
885