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