xref: /illumos-gate/usr/src/lib/libc/port/threads/spawn.c (revision dd4eeefd)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "lint.h"
30 #include "thr_uberdata.h"
31 #include <sys/libc_kernel.h>
32 #include <sys/procset.h>
33 #include <sys/rtpriocntl.h>
34 #include <sys/tspriocntl.h>
35 #include <sys/fork.h>
36 #include <sys/rt.h>
37 #include <sys/ts.h>
38 #include <alloca.h>
39 #include <spawn.h>
40 #include "rtsched.h"
41 
42 #define	ALL_POSIX_SPAWN_FLAGS			\
43 		(POSIX_SPAWN_RESETIDS |		\
44 		POSIX_SPAWN_SETPGROUP |		\
45 		POSIX_SPAWN_SETSIGDEF |		\
46 		POSIX_SPAWN_SETSIGMASK |	\
47 		POSIX_SPAWN_SETSCHEDPARAM |	\
48 		POSIX_SPAWN_SETSCHEDULER |	\
49 		POSIX_SPAWN_NOSIGCHLD_NP |	\
50 		POSIX_SPAWN_WAITPID_NP)
51 
52 typedef struct {
53 	short		sa_psflags;	/* POSIX_SPAWN_* flags */
54 	pri_t		sa_priority;
55 	int		sa_schedpolicy;
56 	pid_t		sa_pgroup;
57 	sigset_t	sa_sigdefault;
58 	sigset_t	sa_sigmask;
59 } spawn_attr_t;
60 
61 typedef struct file_attr {
62 	struct file_attr *fa_next;	/* circular list of file actions */
63 	struct file_attr *fa_prev;
64 	enum {FA_OPEN, FA_CLOSE, FA_DUP2} fa_type;
65 	uint_t		fa_pathsize;	/* size of fa_path[] array */
66 	char		*fa_path;	/* copied pathname for open() */
67 	int		fa_oflag;	/* oflag for open() */
68 	mode_t		fa_mode;	/* mode for open() */
69 	int		fa_filedes;	/* file descriptor for open()/close() */
70 	int		fa_newfiledes;	/* new file descriptor for dup2() */
71 } file_attr_t;
72 
73 extern struct pcclass ts_class, rt_class;
74 
75 extern	pid_t	_vforkx(int);
76 #pragma unknown_control_flow(_vforkx)
77 extern	void	*_private_memset(void *, int, size_t);
78 extern	int	__lwp_sigmask(int, const sigset_t *, sigset_t *);
79 extern	int	__open(const char *, int, mode_t);
80 extern	int	__sigaction(int, const struct sigaction *, struct sigaction *);
81 extern	int	_private_close(int);
82 extern	int	_private_execve(const char *, char *const *, char *const *);
83 extern	int	_private_fcntl(int, int, intptr_t);
84 extern	int	_private_setgid(gid_t);
85 extern	int	_private_setpgid(pid_t, pid_t);
86 extern	int	_private_setuid(uid_t);
87 extern	int	_private_sigismember(sigset_t *, int);
88 extern	gid_t	_private_getgid(void);
89 extern	uid_t	_private_getuid(void);
90 extern	uid_t	_private_geteuid(void);
91 extern	void	_private_exit(int);
92 
93 /*
94  * We call this function rather than priocntl() because we must not call
95  * any function that is exported from libc while in the child of vfork().
96  * Also, we are not using PC_GETXPARMS or PC_SETXPARMS so we can use
97  * the simple call to __priocntlset() rather than the varargs version.
98  */
99 static long
100 _private_priocntl(idtype_t idtype, id_t id, int cmd, caddr_t arg)
101 {
102 	extern long _private__priocntlset(int, procset_t *, int, caddr_t, ...);
103 	procset_t procset;
104 
105 	setprocset(&procset, POP_AND, idtype, id, P_ALL, 0);
106 	return (_private__priocntlset(PC_VERSION, &procset, cmd, arg, 0));
107 }
108 
109 /*
110  * The following two functions are blatently stolen from
111  * sched_setscheduler() and sched_setparam() in librt.
112  * This would be a lot easier if librt were folded into libc.
113  */
114 static int
115 setscheduler(int policy, pri_t prio)
116 {
117 	pcparms_t	pcparm;
118 	tsinfo_t	*tsi;
119 	tsparms_t	*tsp;
120 	int		scale;
121 
122 	switch (policy) {
123 	case SCHED_FIFO:
124 	case SCHED_RR:
125 		if (prio < rt_class.pcc_primin || prio > rt_class.pcc_primax) {
126 			errno = EINVAL;
127 			return (-1);
128 		}
129 		pcparm.pc_cid = rt_class.pcc_info.pc_cid;
130 		((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
131 		((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
132 		    (policy == SCHED_RR ? RT_TQDEF : RT_TQINF);
133 		break;
134 
135 	case SCHED_OTHER:
136 		pcparm.pc_cid = ts_class.pcc_info.pc_cid;
137 		tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo;
138 		scale = tsi->ts_maxupri;
139 		tsp = (tsparms_t *)pcparm.pc_clparms;
140 		tsp->ts_uprilim = tsp->ts_upri = -(scale * prio) / 20;
141 		break;
142 
143 	default:
144 		errno = EINVAL;
145 		return (-1);
146 	}
147 
148 	return (_private_priocntl(P_PID, P_MYID,
149 	    PC_SETPARMS, (caddr_t)&pcparm));
150 }
151 
152 static int
153 setparam(pcparms_t *pcparmp, pri_t prio)
154 {
155 	tsparms_t	*tsp;
156 	tsinfo_t	*tsi;
157 	int		scale;
158 
159 	if (pcparmp->pc_cid == rt_class.pcc_info.pc_cid) {
160 		/* SCHED_FIFO or SCHED_RR policy */
161 		if (prio < rt_class.pcc_primin || prio > rt_class.pcc_primax) {
162 			errno = EINVAL;
163 			return (-1);
164 		}
165 		((rtparms_t *)pcparmp->pc_clparms)->rt_tqnsecs = RT_NOCHANGE;
166 		((rtparms_t *)pcparmp->pc_clparms)->rt_pri = prio;
167 	} else if (pcparmp->pc_cid == ts_class.pcc_info.pc_cid) {
168 		/* SCHED_OTHER policy */
169 		tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo;
170 		scale = tsi->ts_maxupri;
171 		tsp = (tsparms_t *)pcparmp->pc_clparms;
172 		tsp->ts_uprilim = tsp->ts_upri = -(scale * prio) / 20;
173 	} else {
174 		errno = EINVAL;
175 		return (-1);
176 	}
177 
178 	return (_private_priocntl(P_PID, P_MYID,
179 	    PC_SETPARMS, (caddr_t)pcparmp));
180 }
181 
182 static int
183 perform_flag_actions(spawn_attr_t *sap)
184 {
185 	int sig;
186 
187 	if (sap->sa_psflags & POSIX_SPAWN_SETSIGMASK) {
188 		(void) __lwp_sigmask(SIG_SETMASK, &sap->sa_sigmask, NULL);
189 	}
190 
191 	if (sap->sa_psflags & POSIX_SPAWN_SETSIGDEF) {
192 		struct sigaction sigdfl;
193 
194 		(void) _private_memset(&sigdfl, 0, sizeof (sigdfl));
195 		for (sig = 1; sig < NSIG; sig++) {
196 			if (_private_sigismember(&sap->sa_sigdefault, sig))
197 				(void) __sigaction(sig, &sigdfl, NULL);
198 		}
199 	}
200 
201 	if (sap->sa_psflags & POSIX_SPAWN_RESETIDS) {
202 		if (_private_setgid(_private_getgid()) != 0 ||
203 		    _private_setuid(_private_getuid()) != 0)
204 			return (errno);
205 	}
206 
207 	if (sap->sa_psflags & POSIX_SPAWN_SETPGROUP) {
208 		if (_private_setpgid(0, sap->sa_pgroup) != 0)
209 			return (errno);
210 	}
211 
212 	if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDULER) {
213 		if (setscheduler(sap->sa_schedpolicy, sap->sa_priority) != 0)
214 			return (errno);
215 	} else if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDPARAM) {
216 		/*
217 		 * Get the process's current scheduling parameters,
218 		 * then modify to set the new priority.
219 		 */
220 		pcparms_t pcparm;
221 
222 		pcparm.pc_cid = PC_CLNULL;
223 		if (_private_priocntl(P_PID, P_MYID,
224 		    PC_GETPARMS, (caddr_t)&pcparm) == -1)
225 			return (errno);
226 		if (setparam(&pcparm, sap->sa_priority) != 0)
227 			return (errno);
228 	}
229 
230 	return (0);
231 }
232 
233 static int
234 perform_file_actions(file_attr_t *fap)
235 {
236 	file_attr_t *froot = fap;
237 	int fd;
238 
239 	do {
240 		switch (fap->fa_type) {
241 		case FA_OPEN:
242 			fd = __open(fap->fa_path, fap->fa_oflag, fap->fa_mode);
243 			if (fd < 0)
244 				return (errno);
245 			if (fd != fap->fa_filedes) {
246 				if (_private_fcntl(fd, F_DUP2FD,
247 				    fap->fa_filedes) < 0)
248 					return (errno);
249 				(void) _private_close(fd);
250 			}
251 			break;
252 		case FA_CLOSE:
253 			if (_private_close(fap->fa_filedes) == -1)
254 				return (errno);
255 			break;
256 		case FA_DUP2:
257 			fd = _private_fcntl(fap->fa_filedes, F_DUP2FD,
258 				fap->fa_newfiledes);
259 			if (fd < 0)
260 				return (errno);
261 			break;
262 		}
263 	} while ((fap = fap->fa_next) != froot);
264 
265 	return (0);
266 }
267 
268 static int
269 forkflags(spawn_attr_t *sap)
270 {
271 	int flags = 0;
272 
273 	if (sap != NULL) {
274 		if (sap->sa_psflags & POSIX_SPAWN_NOSIGCHLD_NP)
275 			flags |= FORK_NOSIGCHLD;
276 		if (sap->sa_psflags & POSIX_SPAWN_WAITPID_NP)
277 			flags |= FORK_WAITPID;
278 	}
279 
280 	return (flags);
281 }
282 
283 /*
284  * set_error() / get_error() are used to guarantee that the local variable
285  * 'error' is set correctly in memory on return from vfork() in the parent.
286  */
287 
288 static int
289 set_error(int *errp, int err)
290 {
291 	return (*errp = err);
292 }
293 
294 static int
295 get_error(int *errp)
296 {
297 	return (*errp);
298 }
299 
300 /*
301  * For MT safety, do not invoke the dynamic linker after calling vfork().
302  * If some other thread was in the dynamic linker when this thread's parent
303  * called vfork() then the dynamic linker's lock would still be held here
304  * (with a defunct owner) and we would deadlock ourself if we invoked it.
305  *
306  * Therefore, all of the functions we call here after returning from
307  * _vforkx() in the child are not and must never be exported from libc
308  * as global symbols.  To do so would risk invoking the dynamic linker.
309  */
310 
311 #pragma weak posix_spawn = _posix_spawn
312 int
313 _posix_spawn(
314 	pid_t *pidp,
315 	const char *path,
316 	const posix_spawn_file_actions_t *file_actions,
317 	const posix_spawnattr_t *attrp,
318 	char *const argv[],
319 	char *const envp[])
320 {
321 	spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL;
322 	file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL;
323 	int error;		/* this will be set by the child */
324 	pid_t pid;
325 
326 	if (attrp != NULL && sap == NULL)
327 		return (EINVAL);
328 
329 	switch (pid = _vforkx(forkflags(sap))) {
330 	case 0:			/* child */
331 		break;
332 	case -1:		/* parent, failure */
333 		return (errno);
334 	default:		/* parent, success */
335 		/*
336 		 * We don't get here until the child exec()s or exit()s
337 		 */
338 		if (pidp != NULL && get_error(&error) == 0)
339 			*pidp = pid;
340 		return (get_error(&error));
341 	}
342 
343 	if (sap != NULL)
344 		if (set_error(&error, perform_flag_actions(sap)) != 0)
345 			_private_exit(_EVAPORATE);
346 
347 	if (fap != NULL)
348 		if (set_error(&error, perform_file_actions(fap)) != 0)
349 			_private_exit(_EVAPORATE);
350 
351 	(void) set_error(&error, 0);
352 	(void) _private_execve(path, argv, envp);
353 	(void) set_error(&error, errno);
354 	_private_exit(_EVAPORATE);
355 	return (0);	/* not reached */
356 }
357 
358 /*
359  * Much of posix_spawnp() blatently stolen from execvp() (port/gen/execvp.c).
360  */
361 
362 extern int __xpg4;	/* defined in xpg4.c; 0 if not xpg4-compiled program */
363 
364 static const char *
365 execat(const char *s1, const char *s2, char *si)
366 {
367 	int cnt = PATH_MAX + 1;
368 	char *s;
369 	char c;
370 
371 	for (s = si; (c = *s1) != '\0' && c != ':'; s1++) {
372 		if (cnt > 0) {
373 			*s++ = c;
374 			cnt--;
375 		}
376 	}
377 	if (si != s && cnt > 0) {
378 		*s++ = '/';
379 		cnt--;
380 	}
381 	for (; (c = *s2) != '\0' && cnt > 0; s2++) {
382 		*s++ = c;
383 		cnt--;
384 	}
385 	*s = '\0';
386 	return (*s1? ++s1: NULL);
387 }
388 
389 #pragma weak posix_spawnp = _posix_spawnp
390 /* ARGSUSED */
391 int
392 _posix_spawnp(
393 	pid_t *pidp,
394 	const char *file,
395 	const posix_spawn_file_actions_t *file_actions,
396 	const posix_spawnattr_t *attrp,
397 	char *const argv[],
398 	char *const envp[])
399 {
400 	spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL;
401 	file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL;
402 	const char *pathstr = (strchr(file, '/') == NULL)? getenv("PATH") : "";
403 	int xpg4 = __xpg4;
404 	int error;		/* this will be set by the child */
405 	char path[PATH_MAX+4];
406 	const char *cp;
407 	pid_t pid;
408 	char **newargs;
409 	int argc;
410 	int i;
411 	static const char *sun_path = "/bin/sh";
412 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
413 	static const char *shell = "sh";
414 
415 	if (attrp != NULL && sap == NULL)
416 		return (EINVAL);
417 
418 	if (*file == '\0')
419 		return (EACCES);
420 
421 	/*
422 	 * We may need to invoke the shell with a slightly modified
423 	 * argv[] array.  To do this we need to preallocate the array.
424 	 * We must call alloca() before calling vfork() because doing
425 	 * it after vfork() (in the child) would corrupt the parent.
426 	 */
427 	for (argc = 0; argv[argc] != NULL; argc++)
428 		continue;
429 	newargs = alloca((argc + 2) * sizeof (char *));
430 
431 	switch (pid = _vforkx(forkflags(sap))) {
432 	case 0:			/* child */
433 		break;
434 	case -1:		/* parent, failure */
435 		return (errno);
436 	default:		/* parent, success */
437 		/*
438 		 * We don't get here until the child exec()s or exit()s
439 		 */
440 		if (pidp != NULL && get_error(&error) == 0)
441 			*pidp = pid;
442 		return (get_error(&error));
443 	}
444 
445 	if (sap != NULL)
446 		if (set_error(&error, perform_flag_actions(sap)) != 0)
447 			_private_exit(_EVAPORATE);
448 
449 	if (fap != NULL)
450 		if (set_error(&error, perform_file_actions(fap)) != 0)
451 			_private_exit(_EVAPORATE);
452 
453 	if (pathstr == NULL) {
454 		/*
455 		 * XPG4:  pathstr is equivalent to _CS_PATH, except that
456 		 * :/usr/sbin is appended when root, and pathstr must end
457 		 * with a colon when not root.  Keep these paths in sync
458 		 * with _CS_PATH in confstr.c.  Note that pathstr must end
459 		 * with a colon when not root so that when file doesn't
460 		 * contain '/', the last call to execat() will result in an
461 		 * attempt to execv file from the current directory.
462 		 */
463 		if (_private_geteuid() == 0 || _private_getuid() == 0) {
464 			if (!xpg4)
465 				pathstr = "/usr/sbin:/usr/ccs/bin:/usr/bin";
466 			else
467 				pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
468 				    "/usr/bin:/opt/SUNWspro/bin:/usr/sbin";
469 		} else {
470 			if (!xpg4)
471 				pathstr = "/usr/ccs/bin:/usr/bin:";
472 			else
473 				pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
474 				    "/usr/bin:/opt/SUNWspro/bin:";
475 		}
476 	}
477 
478 	cp = pathstr;
479 	do {
480 		cp = execat(cp, file, path);
481 		/*
482 		 * 4025035 and 4038378
483 		 * if a filename begins with a "-" prepend "./" so that
484 		 * the shell can't interpret it as an option
485 		 */
486 		if (*path == '-') {
487 			char *s;
488 
489 			for (s = path; *s != '\0'; s++)
490 				continue;
491 			for (; s >= path; s--)
492 				*(s + 2) = *s;
493 			path[0] = '.';
494 			path[1] = '/';
495 		}
496 		(void) set_error(&error, 0);
497 		(void) _private_execve(path, argv, envp);
498 		if (set_error(&error, errno) == ENOEXEC) {
499 			newargs[0] = (char *)shell;
500 			newargs[1] = path;
501 			for (i = 1; i <= argc; i++)
502 				newargs[i + 1] = argv[i];
503 			(void) set_error(&error, 0);
504 			(void) _private_execve(xpg4? xpg4_path : sun_path,
505 			    newargs, envp);
506 			(void) set_error(&error, errno);
507 			_private_exit(_EVAPORATE);
508 		}
509 	} while (cp);
510 	_private_exit(_EVAPORATE);
511 	return (0);	/* not reached */
512 }
513 
514 #pragma weak posix_spawn_file_actions_init = \
515 		_posix_spawn_file_actions_init
516 int
517 _posix_spawn_file_actions_init(
518 	posix_spawn_file_actions_t *file_actions)
519 {
520 	file_actions->__file_attrp = NULL;
521 	return (0);
522 }
523 
524 #pragma weak posix_spawn_file_actions_destroy = \
525 		_posix_spawn_file_actions_destroy
526 int
527 _posix_spawn_file_actions_destroy(
528 	posix_spawn_file_actions_t *file_actions)
529 {
530 	file_attr_t *froot = file_actions->__file_attrp;
531 	file_attr_t *fap;
532 	file_attr_t *next;
533 
534 	if ((fap = froot) != NULL) {
535 		do {
536 			next = fap->fa_next;
537 			if (fap-> fa_type == FA_OPEN)
538 				lfree(fap->fa_path, fap->fa_pathsize);
539 			lfree(fap, sizeof (*fap));
540 		} while ((fap = next) != froot);
541 	}
542 	file_actions->__file_attrp = NULL;
543 	return (0);
544 }
545 
546 static void
547 add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap)
548 {
549 	file_attr_t *froot = file_actions->__file_attrp;
550 
551 	if (froot == NULL) {
552 		fap->fa_next = fap->fa_prev = fap;
553 		file_actions->__file_attrp = fap;
554 	} else {
555 		fap->fa_next = froot;
556 		fap->fa_prev = froot->fa_prev;
557 		froot->fa_prev->fa_next = fap;
558 		froot->fa_prev = fap;
559 	}
560 }
561 
562 #pragma weak posix_spawn_file_actions_addopen = \
563 		_posix_spawn_file_actions_addopen
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 #pragma weak posix_spawn_file_actions_addclose = \
596 		_posix_spawn_file_actions_addclose
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 #pragma weak posix_spawn_file_actions_adddup2 = \
617 		_posix_spawn_file_actions_adddup2
618 int
619 _posix_spawn_file_actions_adddup2(
620 	posix_spawn_file_actions_t *file_actions,
621 	int filedes,
622 	int newfiledes)
623 {
624 	file_attr_t *fap;
625 
626 	if (filedes < 0 || newfiledes < 0)
627 		return (EBADF);
628 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
629 		return (ENOMEM);
630 
631 	fap->fa_type = FA_DUP2;
632 	fap->fa_filedes = filedes;
633 	fap->fa_newfiledes = newfiledes;
634 	add_file_attr(file_actions, fap);
635 
636 	return (0);
637 }
638 
639 #pragma weak posix_spawnattr_init = \
640 		_posix_spawnattr_init
641 int
642 _posix_spawnattr_init(
643 	posix_spawnattr_t *attr)
644 {
645 	if ((attr->__spawn_attrp = lmalloc(sizeof (posix_spawnattr_t))) == NULL)
646 		return (ENOMEM);
647 	/*
648 	 * Add default stuff here?
649 	 */
650 	return (0);
651 }
652 
653 #pragma weak posix_spawnattr_destroy = \
654 		_posix_spawnattr_destroy
655 int
656 _posix_spawnattr_destroy(
657 	posix_spawnattr_t *attr)
658 {
659 	spawn_attr_t *sap = attr->__spawn_attrp;
660 
661 	if (sap == NULL)
662 		return (EINVAL);
663 
664 	/*
665 	 * deallocate stuff here?
666 	 */
667 	lfree(sap, sizeof (*sap));
668 	attr->__spawn_attrp = NULL;
669 	return (0);
670 }
671 
672 #pragma weak posix_spawnattr_setflags = \
673 		_posix_spawnattr_setflags
674 int
675 _posix_spawnattr_setflags(
676 	posix_spawnattr_t *attr,
677 	short flags)
678 {
679 	spawn_attr_t *sap = attr->__spawn_attrp;
680 
681 	if (sap == NULL ||
682 	    (flags & ~ALL_POSIX_SPAWN_FLAGS))
683 		return (EINVAL);
684 
685 	if (flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) {
686 		/*
687 		 * Populate ts_class and rt_class.
688 		 * We will need them in the child of vfork().
689 		 */
690 		if (rt_class.pcc_state == 0)
691 			(void) get_info_by_policy(SCHED_FIFO);
692 		if (ts_class.pcc_state == 0)
693 			(void) get_info_by_policy(SCHED_OTHER);
694 	}
695 
696 	sap->sa_psflags = flags;
697 	return (0);
698 }
699 
700 #pragma weak posix_spawnattr_getflags = \
701 		_posix_spawnattr_getflags
702 int
703 _posix_spawnattr_getflags(
704 	const posix_spawnattr_t *attr,
705 	short *flags)
706 {
707 	spawn_attr_t *sap = attr->__spawn_attrp;
708 
709 	if (sap == NULL)
710 		return (EINVAL);
711 
712 	*flags = sap->sa_psflags;
713 	return (0);
714 }
715 
716 #pragma weak posix_spawnattr_setpgroup = \
717 		_posix_spawnattr_setpgroup
718 int
719 _posix_spawnattr_setpgroup(
720 	posix_spawnattr_t *attr,
721 	pid_t pgroup)
722 {
723 	spawn_attr_t *sap = attr->__spawn_attrp;
724 
725 	if (sap == NULL)
726 		return (EINVAL);
727 
728 	sap->sa_pgroup = pgroup;
729 	return (0);
730 }
731 
732 #pragma weak posix_spawnattr_getpgroup = \
733 		_posix_spawnattr_getpgroup
734 int
735 _posix_spawnattr_getpgroup(
736 	const posix_spawnattr_t *attr,
737 	pid_t *pgroup)
738 {
739 	spawn_attr_t *sap = attr->__spawn_attrp;
740 
741 	if (sap == NULL)
742 		return (EINVAL);
743 
744 	*pgroup = sap->sa_pgroup;
745 	return (0);
746 }
747 
748 #pragma weak posix_spawnattr_setschedparam = \
749 		_posix_spawnattr_setschedparam
750 int
751 _posix_spawnattr_setschedparam(
752 	posix_spawnattr_t *attr,
753 	const struct sched_param *schedparam)
754 {
755 	spawn_attr_t *sap = attr->__spawn_attrp;
756 
757 	if (sap == NULL)
758 		return (EINVAL);
759 
760 	/*
761 	 * Check validity?
762 	 */
763 	sap->sa_priority = schedparam->sched_priority;
764 	return (0);
765 }
766 
767 #pragma weak posix_spawnattr_getschedparam = \
768 		_posix_spawnattr_getschedparam
769 int
770 _posix_spawnattr_getschedparam(
771 	const posix_spawnattr_t *attr,
772 	struct sched_param *schedparam)
773 {
774 	spawn_attr_t *sap = attr->__spawn_attrp;
775 
776 	if (sap == NULL)
777 		return (EINVAL);
778 
779 	schedparam->sched_priority = sap->sa_priority;
780 	return (0);
781 }
782 
783 #pragma weak posix_spawnattr_setschedpolicy = \
784 		_posix_spawnattr_setschedpolicy
785 int
786 _posix_spawnattr_setschedpolicy(
787 	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 	switch (schedpolicy) {
796 	case SCHED_OTHER:
797 	case SCHED_FIFO:
798 	case SCHED_RR:
799 		break;
800 	default:
801 		return (EINVAL);
802 	}
803 
804 	sap->sa_schedpolicy = schedpolicy;
805 	return (0);
806 }
807 
808 #pragma weak posix_spawnattr_getschedpolicy = \
809 		_posix_spawnattr_getschedpolicy
810 int
811 _posix_spawnattr_getschedpolicy(
812 	const posix_spawnattr_t *attr,
813 	int *schedpolicy)
814 {
815 	spawn_attr_t *sap = attr->__spawn_attrp;
816 
817 	if (sap == NULL)
818 		return (EINVAL);
819 
820 	*schedpolicy = sap->sa_schedpolicy;
821 	return (0);
822 }
823 
824 #pragma weak posix_spawnattr_setsigdefault = \
825 		_posix_spawnattr_setsigdefault
826 int
827 _posix_spawnattr_setsigdefault(
828 	posix_spawnattr_t *attr,
829 	const sigset_t *sigdefault)
830 {
831 	spawn_attr_t *sap = attr->__spawn_attrp;
832 
833 	if (sap == NULL)
834 		return (EINVAL);
835 
836 	sap->sa_sigdefault = *sigdefault;
837 	return (0);
838 }
839 
840 #pragma weak posix_spawnattr_getsigdefault = \
841 		_posix_spawnattr_getsigdefault
842 int
843 _posix_spawnattr_getsigdefault(
844 	const posix_spawnattr_t *attr,
845 	sigset_t *sigdefault)
846 {
847 	spawn_attr_t *sap = attr->__spawn_attrp;
848 
849 	if (sap == NULL)
850 		return (EINVAL);
851 
852 	*sigdefault = sap->sa_sigdefault;
853 	return (0);
854 }
855 
856 #pragma weak posix_spawnattr_setsigmask = \
857 		_posix_spawnattr_setsigmask
858 int
859 _posix_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 
872 #pragma weak posix_spawnattr_getsigmask = \
873 		_posix_spawnattr_getsigmask
874 int
875 _posix_spawnattr_getsigmask(
876 	const posix_spawnattr_t *attr,
877 	sigset_t *sigmask)
878 {
879 	spawn_attr_t *sap = attr->__spawn_attrp;
880 
881 	if (sap == NULL)
882 		return (EINVAL);
883 
884 	*sigmask = sap->sa_sigmask;
885 	return (0);
886 }
887