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