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