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