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