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