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