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