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