Home | History | Annotate | Download | only in threads
      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