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