xref: /illumos-gate/usr/src/lib/libc/port/threads/spawn.c (revision 438283cf397cce47d80cc67b04bbfdfe73b0d142)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 2011 by Delphix. All rights reserved.
29  * Copyright 2026 Oxide Computer Company
30  * Copyright 2026 RackTop Systems, Inc.
31  */
32 
33 #include "lint.h"
34 #include "thr_uberdata.h"
35 #include <sys/sysmacros.h>
36 #include <limits.h>
37 #include <spawn.h>
38 #include <stdbool.h>
39 #include <sys/spawn_impl.h>
40 #include <paths.h>
41 
42 extern const char **_environ;
43 extern int libc__xpg4;
44 
45 extern pid_t __spawn(const char *, const void *, uint32_t, const void *,
46     uint32_t);
47 
48 /*
49  * Add sz to the running total *lenp, refusing if the total would exceed
50  * ARG_MAX. ARG_MAX is well below UINT32_MAX, so capping there also keeps the
51  * marshalled totals and offsets within their uint32_t fields.
52  */
53 static inline bool
54 spawn_addlen(size_t *lenp, size_t sz)
55 {
56 	if (sz > ARG_MAX - *lenp)
57 		return (false);
58 	*lenp += sz;
59 	return (true);
60 }
61 
62 /*
63  * Pack the argv[] and envp[] string vectors into sargs->sa_data as a single
64  * blob of NUL-terminated strings. Called first with sargs == NULL to compute
65  * the required buffer size, then again to populate the data and record the
66  * offsets and counts. Returns false if the combined size would exceed ARG_MAX
67  * or, on the second pass, would overflow the allocated buffer.
68  */
69 static bool
70 spawn_marshal_argenv(spawn_args_t *sargs, char *const *argp, char *const *envp,
71     size_t *lenp)
72 {
73 	struct vec {
74 		char *const *vec;
75 		uint32_t *off;
76 		uint32_t *cnt;
77 	} vecs[] = {
78 		{ .vec = argp },
79 		{ .vec = envp }
80 	};
81 
82 	if (sargs != NULL) {
83 		vecs[0].off = &sargs->sa_arg_off;
84 		vecs[0].cnt = &sargs->sa_arg_cnt;
85 		vecs[1].off = &sargs->sa_env_off;
86 		vecs[1].cnt = &sargs->sa_env_cnt;
87 	}
88 
89 	for (size_t i = 0; i < ARRAY_SIZE(vecs); i++) {
90 		struct vec *vec = &vecs[i];
91 		uint32_t cnt = 0;
92 		char *const *p;
93 
94 		if (vec->off != NULL)
95 			*vec->off = (uint32_t)*lenp;
96 
97 		for (p = vec->vec; p != NULL && *p != NULL; p++) {
98 			size_t sz = strlen(*p) + 1;
99 
100 			if (sargs != NULL) {
101 				if (*lenp + sz > sargs->sa_datalen)
102 					return (false);
103 				(void) memcpy(&sargs->sa_data[*lenp], *p, sz);
104 			}
105 			if (!spawn_addlen(lenp, sz))
106 				return (false);
107 			cnt++;
108 		}
109 
110 		if (vec->cnt != NULL)
111 			*vec->cnt = cnt;
112 	}
113 
114 	return (true);
115 }
116 
117 /*
118  * Pack the spawn_attr_t, the resolved scheduling attributes, the circular
119  * list of file_attr_t records and, for posix_spawnp(), the shell and search
120  * path strings into sparam->sp_data.
121  * Called first with sparam == NULL to compute the required buffer size, then
122  * again to populate the data and record the offsets, lengths and counts.
123  * Returns false if the combined size would exceed ARG_MAX or, on the second
124  * pass, would overflow the allocated buffer.
125  */
126 static bool
127 spawn_marshal_param(spawn_param_t *sparam, const spawn_attr_t *sa,
128     const kspawn_sched_t *ksched, const file_attr_t *fa, const char *shell,
129     const char *pathstr, size_t *lenp)
130 {
131 	if (sa != NULL) {
132 		size_t sz = sizeof (*sa);
133 
134 		if (sparam != NULL) {
135 			if (*lenp + sz > sparam->sp_datalen)
136 				return (false);
137 			sparam->sp_attr_off = (uint32_t)*lenp;
138 			sparam->sp_attr_len = (uint32_t)sz;
139 			(void) memcpy(&sparam->sp_data[*lenp], sa, sz);
140 		}
141 		if (!spawn_addlen(lenp, sz))
142 			return (false);
143 	}
144 
145 	if (ksched != NULL) {
146 		size_t sz = sizeof (*ksched);
147 
148 		if (sparam != NULL) {
149 			if (*lenp + sz > sparam->sp_datalen)
150 				return (false);
151 			sparam->sp_sched_off = (uint32_t)*lenp;
152 			sparam->sp_sched_len = (uint32_t)sz;
153 			(void) memcpy(&sparam->sp_data[*lenp], ksched, sz);
154 		}
155 		if (!spawn_addlen(lenp, sz))
156 			return (false);
157 	}
158 
159 	if (fa != NULL) {
160 		const file_attr_t *froot = fa;
161 		uint32_t cnt = 0;
162 
163 		if (sparam != NULL)
164 			sparam->sp_fattr_off = (uint32_t)*lenp;
165 
166 		do {
167 			/*
168 			 * Each record is padded so that the next remains
169 			 * 32-bit aligned.
170 			 */
171 			size_t sz = P2ROUNDUP(
172 			    sizeof (kfile_attr_t) + fa->fa_pathsize,
173 			    sizeof (uint32_t));
174 
175 			if (sparam != NULL) {
176 				kfile_attr_t *ka;
177 
178 				if (*lenp + sz > sparam->sp_datalen)
179 					return (false);
180 
181 				ka = (kfile_attr_t *)&sparam->sp_data[*lenp];
182 				ka->kfa_len = sz;
183 				ka->kfa_type = fa->fa_type;
184 				ka->kfa_pathsize = fa->fa_pathsize;
185 
186 				switch (fa->fa_type) {
187 				case FA_OPEN:
188 					ka->kfa_filedes = fa->fa_filedes;
189 					ka->kfa_oflag = fa->fa_oflag;
190 					ka->kfa_mode = fa->fa_mode;
191 					/* FALLTHROUGH */
192 				case FA_CHDIR:
193 					(void) memcpy(ka->kfa_path,
194 					    fa->fa_path, fa->fa_pathsize);
195 					break;
196 				case FA_DUP2:
197 					ka->kfa_newfiledes = fa->fa_newfiledes;
198 					/* FALLTHROUGH */
199 				case FA_CLOSE:
200 				case FA_CLOSEFROM:
201 				case FA_FCHDIR:
202 					ka->kfa_filedes = fa->fa_filedes;
203 					break;
204 				}
205 			}
206 			if (!spawn_addlen(lenp, sz))
207 				return (false);
208 			cnt++;
209 		} while ((fa = fa->fa_next) != froot);
210 
211 		if (sparam != NULL)
212 			sparam->sp_fattr_cnt = cnt;
213 	}
214 
215 	if (shell != NULL) {
216 		size_t sz = strlen(shell) + 1;
217 
218 		if (sparam != NULL) {
219 			if (*lenp + sz > sparam->sp_datalen)
220 				return (false);
221 			sparam->sp_shell_off = (uint32_t)*lenp;
222 			sparam->sp_shell_len = (uint32_t)sz;
223 			(void) memcpy(&sparam->sp_data[*lenp], shell, sz);
224 		}
225 		if (!spawn_addlen(lenp, sz))
226 			return (false);
227 	}
228 
229 	if (pathstr != NULL) {
230 		size_t sz = strlen(pathstr) + 1;
231 
232 		if (sparam != NULL) {
233 			if (*lenp + sz > sparam->sp_datalen)
234 				return (false);
235 			sparam->sp_path_off = (uint32_t)*lenp;
236 			sparam->sp_path_len = (uint32_t)sz;
237 			(void) memcpy(&sparam->sp_data[*lenp], pathstr, sz);
238 		}
239 		if (!spawn_addlen(lenp, sz))
240 			return (false);
241 	}
242 
243 	return (true);
244 }
245 
246 static int
247 posix_spawn_common(pid_t *pidp, const char *path,
248     const posix_spawn_file_actions_t *file_actions,
249     const posix_spawnattr_t *attrp, char *const *argp, char *const *envp,
250     const char *shell, const char *pathstr)
251 {
252 	spawn_attr_t *sa = (attrp != NULL) ? attrp->__spawn_attrp : NULL;
253 	file_attr_t *fa = (file_actions != NULL) ? file_actions->__file_attrp :
254 	    NULL;
255 	const kspawn_sched_t *ksched = NULL;
256 	kspawn_sched_t ks;
257 	spawn_args_t *sargs = NULL;
258 	spawn_param_t *sparam = NULL;
259 	pid_t pid;
260 	size_t datalen, len;
261 	int ret = 0;
262 
263 	if (attrp != NULL && sa == NULL)
264 		return (EINVAL);
265 
266 	/*
267 	 * The scheduling attributes are resolved here into the form that the
268 	 * child applies to itself in the kernel.
269 	 */
270 	if (sa != NULL && (sa->sa_psflags &
271 	    (POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETSCHEDPARAM)) != 0) {
272 		ret = spawn_sched_resolve(sa->sa_psflags, sa->sa_schedpolicy,
273 		    sa->sa_priority, &ks);
274 		if (ret != 0)
275 			return (ret);
276 		ksched = &ks;
277 	}
278 
279 	/*
280 	 * spawn(2) creates a brand-new child process that does not share the
281 	 * parent's address space, so every piece of state needed to set up the
282 	 * child must be copied into the kernel ahead of time. Marshal the
283 	 * spawn attributes, the list of file actions and, for posix_spawnp(),
284 	 * the shell and search path, into a single contiguous blob that the
285 	 * kernel can copy in and retain.
286 	 */
287 	datalen = 0;
288 	if (!spawn_marshal_param(NULL, sa, ksched, fa, shell, pathstr,
289 	    &datalen)) {
290 		ret = E2BIG;
291 		goto out;
292 	}
293 	if (datalen > 0) {
294 		len = sizeof (*sparam) + datalen;
295 		sparam = lmalloc(len);
296 		if (sparam == NULL) {
297 			ret = errno;
298 			goto out;
299 		}
300 		sparam->sp_size = len;
301 		sparam->sp_datalen = datalen;
302 
303 		datalen = 0;
304 		if (!spawn_marshal_param(sparam, sa, ksched, fa, shell,
305 		    pathstr, &datalen) || datalen != sparam->sp_datalen) {
306 			ret = EINVAL;
307 			goto out;
308 		}
309 	}
310 
311 	/*
312 	 * Likewise the argument and environment vectors. As supplied by the
313 	 * caller these are arrays of pointers into strings that may well be
314 	 * scattered around the parent's address space. Flatten the strings
315 	 * into a single contiguous blob that the kernel can copy in.
316 	 */
317 	datalen = 0;
318 	if (!spawn_marshal_argenv(NULL, argp, envp, &datalen)) {
319 		ret = E2BIG;
320 		goto out;
321 	}
322 	len = sizeof (*sargs) + datalen;
323 	sargs = lmalloc(len);
324 	if (sargs == NULL) {
325 		ret = errno;
326 		goto out;
327 	}
328 	sargs->sa_size = len;
329 	sargs->sa_datalen = datalen;
330 
331 	datalen = 0;
332 	if (!spawn_marshal_argenv(sargs, argp, envp, &datalen) ||
333 	    datalen != sargs->sa_datalen) {
334 		ret = EINVAL;
335 		goto out;
336 	}
337 
338 	pid = __spawn(path, sparam, sparam != NULL ? sparam->sp_size : 0,
339 	    sargs, sargs->sa_size);
340 
341 	if (pid == -1) {
342 		ret = errno;
343 		goto out;
344 	}
345 
346 	if (pidp != NULL)
347 		*pidp = pid;
348 
349 out:
350 	if (sparam != NULL)
351 		lfree(sparam, sparam->sp_size);
352 	if (sargs != NULL)
353 		lfree(sargs, sargs->sa_size);
354 	return (ret);
355 }
356 
357 int
358 posix_spawn(pid_t *pidp, const char *path,
359     const posix_spawn_file_actions_t *file_actions,
360     const posix_spawnattr_t *attrp, char *const *argp, char *const *envp)
361 {
362 	return (posix_spawn_common(pidp, path, file_actions, attrp,
363 	    argp, envp, NULL, NULL));
364 }
365 
366 int
367 posix_spawnp(pid_t *pidp, const char *file,
368     const posix_spawn_file_actions_t *file_actions,
369     const posix_spawnattr_t *attrp, char *const *argp, char *const *envp)
370 {
371 	const char *pathstr =
372 	    (strchr(file, '/') == NULL) ? getenv("PATH") : "";
373 
374 	if (*file == '\0')
375 		return (EACCES);
376 
377 	if (pathstr == NULL) {
378 		/*
379 		 * XPG4:  pathstr is equivalent to _CS_PATH, except that
380 		 * :/usr/sbin is appended when root, and pathstr must end
381 		 * with a colon when not root.  Keep these paths in sync
382 		 * with _CS_PATH in confstr.c.  Note that pathstr must end
383 		 * with a colon when not root so that when file doesn't
384 		 * contain '/', the last attempt will be to exec the file
385 		 * from the current directory.
386 		 */
387 		if (geteuid() == 0 || getuid() == 0) {
388 			if (!libc__xpg4) {
389 				pathstr = "/usr/sbin:/usr/ccs/bin:/usr/bin";
390 			} else {
391 				pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
392 				    "/usr/bin:/opt/SUNWspro/bin:/usr/sbin";
393 			}
394 		} else {
395 			if (!libc__xpg4) {
396 				pathstr = "/usr/ccs/bin:/usr/bin:";
397 			} else {
398 				pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
399 				    "/usr/bin:/opt/SUNWspro/bin:";
400 			}
401 		}
402 	}
403 
404 	return (posix_spawn_common(pidp, file, file_actions, attrp,
405 	    argp, envp, _PATH_BSHELL, pathstr));
406 }
407 
408 int
409 posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions)
410 {
411 	file_actions->__file_attrp = NULL;
412 	return (0);
413 }
414 
415 int
416 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions)
417 {
418 	file_attr_t *froot = file_actions->__file_attrp;
419 	file_attr_t *fap;
420 	file_attr_t *next;
421 
422 	if ((fap = froot) != NULL) {
423 		do {
424 			next = fap->fa_next;
425 			if (fap->fa_type == FA_OPEN || fap->fa_type == FA_CHDIR)
426 				lfree(fap->fa_path, fap->fa_pathsize);
427 			lfree(fap, sizeof (*fap));
428 		} while ((fap = next) != froot);
429 	}
430 	file_actions->__file_attrp = NULL;
431 	return (0);
432 }
433 
434 static void
435 add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap)
436 {
437 	file_attr_t *froot = file_actions->__file_attrp;
438 
439 	if (froot == NULL) {
440 		fap->fa_next = fap->fa_prev = fap;
441 		file_actions->__file_attrp = froot = fap;
442 	} else {
443 		fap->fa_next = froot;
444 		fap->fa_prev = froot->fa_prev;
445 		froot->fa_prev->fa_next = fap;
446 		froot->fa_prev = fap;
447 	}
448 }
449 
450 int
451 posix_spawn_file_actions_addopen(
452     posix_spawn_file_actions_t *restrict file_actions,
453     int filedes, const char *restrict path, int oflag, mode_t mode)
454 {
455 	file_attr_t *fap;
456 
457 	if (filedes < 0)
458 		return (EBADF);
459 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
460 		return (ENOMEM);
461 
462 	fap->fa_pathsize = strlen(path) + 1;
463 	if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
464 		lfree(fap, sizeof (*fap));
465 		return (ENOMEM);
466 	}
467 	(void) memcpy(fap->fa_path, path, fap->fa_pathsize);
468 
469 	fap->fa_type = FA_OPEN;
470 	fap->fa_oflag = oflag;
471 	fap->fa_mode = mode;
472 	fap->fa_filedes = filedes;
473 	add_file_attr(file_actions, fap);
474 
475 	return (0);
476 }
477 
478 int
479 posix_spawn_file_actions_addclose(
480     posix_spawn_file_actions_t *restrict file_actions, int filedes)
481 {
482 	file_attr_t *fap;
483 
484 	if (filedes < 0)
485 		return (EBADF);
486 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
487 		return (ENOMEM);
488 
489 	fap->fa_type = FA_CLOSE;
490 	fap->fa_filedes = filedes;
491 	add_file_attr(file_actions, fap);
492 
493 	return (0);
494 }
495 
496 int
497 posix_spawn_file_actions_adddup2(
498     posix_spawn_file_actions_t *restrict file_actions,
499     int filedes, int newfiledes)
500 {
501 	file_attr_t *fap;
502 
503 	if (filedes < 0 || newfiledes < 0)
504 		return (EBADF);
505 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
506 		return (ENOMEM);
507 
508 	fap->fa_type = FA_DUP2;
509 	fap->fa_filedes = filedes;
510 	fap->fa_newfiledes = newfiledes;
511 	add_file_attr(file_actions, fap);
512 
513 	return (0);
514 }
515 
516 int
517 posix_spawn_file_actions_addclosefrom_np(
518     posix_spawn_file_actions_t *restrict file_actions, int lowfiledes)
519 {
520 	file_attr_t *fap;
521 
522 	if (lowfiledes < 0)
523 		return (EBADF);
524 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
525 		return (ENOMEM);
526 	fap->fa_type = FA_CLOSEFROM;
527 	fap->fa_filedes = lowfiledes;
528 	add_file_attr(file_actions, fap);
529 
530 	return (0);
531 }
532 
533 int
534 posix_spawn_file_actions_addchdir(
535     posix_spawn_file_actions_t *restrict file_actions,
536     const char *restrict path)
537 {
538 	file_attr_t *fap;
539 
540 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
541 		return (ENOMEM);
542 
543 	fap->fa_pathsize = strlen(path) + 1;
544 	if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
545 		lfree(fap, sizeof (*fap));
546 		return (ENOMEM);
547 	}
548 	(void) memcpy(fap->fa_path, path, fap->fa_pathsize);
549 
550 	fap->fa_type = FA_CHDIR;
551 	add_file_attr(file_actions, fap);
552 
553 	return (0);
554 }
555 
556 int
557 posix_spawn_file_actions_addchdir_np(
558     posix_spawn_file_actions_t *restrict file_actions,
559     const char *restrict path)
560 {
561 	return (posix_spawn_file_actions_addchdir(file_actions, path));
562 }
563 
564 int
565 posix_spawn_file_actions_addfchdir(
566     posix_spawn_file_actions_t *restrict file_actions, int fd)
567 {
568 	file_attr_t *fap;
569 
570 	if (fd < 0)
571 		return (EBADF);
572 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
573 		return (ENOMEM);
574 	fap->fa_type = FA_FCHDIR;
575 	fap->fa_filedes = fd;
576 	add_file_attr(file_actions, fap);
577 	return (0);
578 }
579 
580 int
581 posix_spawn_file_actions_addfchdir_np(
582     posix_spawn_file_actions_t *restrict file_actions, int fd)
583 {
584 	return (posix_spawn_file_actions_addfchdir(file_actions, fd));
585 }
586 
587 int
588 posix_spawnattr_init(posix_spawnattr_t *attr)
589 {
590 	spawn_attr_t *sap;
591 
592 	if ((sap = lmalloc(sizeof (*sap))) == NULL)
593 		return (ENOMEM);
594 
595 	attr->__spawn_attrp = sap;
596 
597 	/*
598 	 * Add default stuff here?
599 	 */
600 	return (0);
601 }
602 
603 int
604 posix_spawnattr_destroy(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 int
620 posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags)
621 {
622 	spawn_attr_t *sap = attr->__spawn_attrp;
623 
624 	if (sap == NULL ||
625 	    (flags & ~ALL_POSIX_SPAWN_FLAGS))
626 		return (EINVAL);
627 
628 	sap->sa_psflags = flags;
629 	return (0);
630 }
631 
632 int
633 posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags)
634 {
635 	spawn_attr_t *sap = attr->__spawn_attrp;
636 
637 	if (sap == NULL)
638 		return (EINVAL);
639 
640 	*flags = sap->sa_psflags;
641 	return (0);
642 }
643 
644 int
645 posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup)
646 {
647 	spawn_attr_t *sap = attr->__spawn_attrp;
648 
649 	if (sap == NULL)
650 		return (EINVAL);
651 
652 	sap->sa_pgroup = pgroup;
653 	return (0);
654 }
655 
656 int
657 posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, pid_t *pgroup)
658 {
659 	spawn_attr_t *sap = attr->__spawn_attrp;
660 
661 	if (sap == NULL)
662 		return (EINVAL);
663 
664 	*pgroup = sap->sa_pgroup;
665 	return (0);
666 }
667 
668 int
669 posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
670     const struct sched_param *schedparam)
671 {
672 	spawn_attr_t *sap = attr->__spawn_attrp;
673 
674 	if (sap == NULL)
675 		return (EINVAL);
676 
677 	/*
678 	 * Check validity?
679 	 */
680 	sap->sa_priority = schedparam->sched_priority;
681 	return (0);
682 }
683 
684 int
685 posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
686     struct sched_param *schedparam)
687 {
688 	spawn_attr_t *sap = attr->__spawn_attrp;
689 
690 	if (sap == NULL)
691 		return (EINVAL);
692 
693 	schedparam->sched_priority = sap->sa_priority;
694 	return (0);
695 }
696 
697 int
698 posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy)
699 {
700 	spawn_attr_t *sap = attr->__spawn_attrp;
701 
702 	if (sap == NULL || schedpolicy == SCHED_SYS)
703 		return (EINVAL);
704 
705 	/*
706 	 * Cache the policy information for later use by posix_spawn().
707 	 */
708 	if (get_info_by_policy(schedpolicy) == NULL)
709 		return (errno);
710 
711 	sap->sa_schedpolicy = schedpolicy;
712 	return (0);
713 }
714 
715 int
716 posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr, int *schedpolicy)
717 {
718 	spawn_attr_t *sap = attr->__spawn_attrp;
719 
720 	if (sap == NULL)
721 		return (EINVAL);
722 
723 	*schedpolicy = sap->sa_schedpolicy;
724 	return (0);
725 }
726 
727 int
728 posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
729     const sigset_t *sigdefault)
730 {
731 	spawn_attr_t *sap = attr->__spawn_attrp;
732 
733 	if (sap == NULL)
734 		return (EINVAL);
735 
736 	sap->sa_sigdefault = *sigdefault;
737 	return (0);
738 }
739 
740 int
741 posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr,
742     sigset_t *sigdefault)
743 {
744 	spawn_attr_t *sap = attr->__spawn_attrp;
745 
746 	if (sap == NULL)
747 		return (EINVAL);
748 
749 	*sigdefault = sap->sa_sigdefault;
750 	return (0);
751 }
752 
753 int
754 posix_spawnattr_setsigignore_np(posix_spawnattr_t *attr,
755     const sigset_t *sigignore)
756 {
757 	spawn_attr_t *sap = attr->__spawn_attrp;
758 
759 	if (sap == NULL)
760 		return (EINVAL);
761 
762 	sap->sa_sigignore = *sigignore;
763 	return (0);
764 }
765 
766 int
767 posix_spawnattr_getsigignore_np(const posix_spawnattr_t *attr,
768     sigset_t *sigignore)
769 {
770 	spawn_attr_t *sap = attr->__spawn_attrp;
771 
772 	if (sap == NULL)
773 		return (EINVAL);
774 
775 	*sigignore = sap->sa_sigignore;
776 	return (0);
777 }
778 
779 int
780 posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
781     const sigset_t *sigmask)
782 {
783 	spawn_attr_t *sap = attr->__spawn_attrp;
784 
785 	if (sap == NULL)
786 		return (EINVAL);
787 
788 	sap->sa_sigmask = *sigmask;
789 	return (0);
790 }
791 
792 int
793 posix_spawnattr_getsigmask(const posix_spawnattr_t *attr,
794     sigset_t *sigmask)
795 {
796 	spawn_attr_t *sap = attr->__spawn_attrp;
797 
798 	if (sap == NULL)
799 		return (EINVAL);
800 
801 	*sigmask = sap->sa_sigmask;
802 	return (0);
803 }
804 
805 /*
806  * Spawn a process to run "sh -c <cmd>".  Return the child's pid (in
807  * *pidp), and a file descriptor (in *fdp) for reading or writing to the
808  * child process, depending on the 'write' argument.
809  * Return 0 on success; otherwise return an error code.
810  */
811 int
812 posix_spawn_pipe_np(pid_t *pidp, int *fdp,
813     const char *cmd, boolean_t write,
814     posix_spawn_file_actions_t *fact, posix_spawnattr_t *attr)
815 {
816 	int	p[2];
817 	int	myside, yourside, stdio;
818 	const char *shpath = _PATH_BSHELL;
819 	const char *argvec[4] = { "sh", "-c", cmd, NULL };
820 	int	error;
821 
822 	if (pipe(p) < 0)
823 		return (errno);
824 
825 	if (access(shpath, X_OK))	/* XPG4 Requirement: */
826 		shpath = "";		/* force child to fail immediately */
827 
828 	if (write) {
829 		/*
830 		 * Data is read from p[0] and written to p[1].
831 		 * 'stdio' is the fd in the child process that should be
832 		 * connected to the pipe.
833 		 */
834 		myside = p[1];
835 		yourside = p[0];
836 		stdio = STDIN_FILENO;
837 	} else {
838 		myside = p[0];
839 		yourside = p[1];
840 		stdio = STDOUT_FILENO;
841 	}
842 
843 	error = posix_spawn_file_actions_addclose(fact, myside);
844 	if (yourside != stdio) {
845 		if (error == 0) {
846 			error = posix_spawn_file_actions_adddup2(fact,
847 			    yourside, stdio);
848 		}
849 		if (error == 0) {
850 			error = posix_spawn_file_actions_addclose(fact,
851 			    yourside);
852 		}
853 	}
854 
855 	if (error)
856 		return (error);
857 	error = posix_spawn(pidp, shpath, fact, attr,
858 	    (char *const *)argvec, (char *const *)_environ);
859 	(void) close(yourside);
860 	if (error) {
861 		(void) close(myside);
862 		return (error);
863 	}
864 
865 	*fdp = myside;
866 	return (0);
867 }
868