xref: /illumos-gate/usr/src/lib/libc/port/threads/spawn.c (revision 3114379f81d5ab88054ea9e72c8874984ea8c263)
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  */
30 
31 #include "lint.h"
32 #include "thr_uberdata.h"
33 #include <sys/libc_kernel.h>
34 #include <sys/procset.h>
35 #include <sys/fork.h>
36 #include <dirent.h>
37 #include <alloca.h>
38 #include <spawn.h>
39 #include <paths.h>
40 
41 #define	ALL_POSIX_SPAWN_FLAGS			\
42 		(POSIX_SPAWN_RESETIDS |		\
43 		POSIX_SPAWN_SETPGROUP |		\
44 		POSIX_SPAWN_SETSIGDEF |		\
45 		POSIX_SPAWN_SETSIGMASK |	\
46 		POSIX_SPAWN_SETSCHEDPARAM |	\
47 		POSIX_SPAWN_SETSCHEDULER |	\
48 		POSIX_SPAWN_SETSID |		\
49 		POSIX_SPAWN_SETSIGIGN_NP |	\
50 		POSIX_SPAWN_NOSIGCHLD_NP |	\
51 		POSIX_SPAWN_WAITPID_NP |	\
52 		POSIX_SPAWN_NOEXECERR_NP)
53 
54 typedef struct {
55 	int		sa_psflags;	/* POSIX_SPAWN_* flags */
56 	int		sa_priority;
57 	int		sa_schedpolicy;
58 	pid_t		sa_pgroup;
59 	sigset_t	sa_sigdefault;
60 	sigset_t	sa_sigignore;
61 	sigset_t	sa_sigmask;
62 } spawn_attr_t;
63 
64 typedef enum file_action {
65 	FA_OPEN,
66 	FA_CLOSE,
67 	FA_DUP2,
68 	FA_CLOSEFROM,
69 	FA_CHDIR,
70 	FA_FCHDIR
71 } file_action_t;
72 
73 typedef struct file_attr {
74 	struct file_attr *fa_next;	/* circular list of file actions */
75 	struct file_attr *fa_prev;
76 	file_action_t	fa_type;	/* type of action */
77 	int		fa_need_dirbuf;	/* only consulted in the head action */
78 	char		*fa_path;	/* copied pathname for open() */
79 	uint_t		fa_pathsize;	/* size of fa_path[] array */
80 	int		fa_oflag;	/* oflag for open() */
81 	mode_t		fa_mode;	/* mode for open() */
82 	int		fa_filedes;	/* file descriptor for open()/close() */
83 	int		fa_newfiledes;	/* new file descriptor for dup2() */
84 } file_attr_t;
85 
86 #if defined(_LP64)
87 #define	__open64	__open
88 #define	getdents64	getdents
89 #define	dirent64_t	dirent_t
90 #else
91 extern int getdents64(int, dirent64_t *, size_t);
92 #endif
93 
94 extern const char **_environ;
95 
96 /*
97  * Support function:
98  * Close all open file descriptors greater than or equal to lowfd.
99  * This is executed in the child of vfork(), so we must not call
100  * opendir() / readdir() because that would alter the parent's
101  * address space.  We use the low-level getdents64() system call.
102  * Return non-zero on error.
103  */
104 static int
spawn_closefrom(int lowfd,void * buf)105 spawn_closefrom(int lowfd, void *buf)
106 {
107 	int procfd;
108 	int fd;
109 	int buflen;
110 	dirent64_t *dp;
111 	dirent64_t *dpend;
112 
113 	if (lowfd <  0)
114 		lowfd = 0;
115 
116 	/*
117 	 * Close lowfd right away as a hedge against failing
118 	 * to open the /proc file descriptor directory due
119 	 * all file descriptors being currently used up.
120 	 */
121 	(void) __close(lowfd++);
122 
123 	if ((procfd = __open64("/proc/self/fd", O_RDONLY, 0)) < 0) {
124 		/*
125 		 * We could not open the /proc file descriptor directory.
126 		 * Just fail and be done with it.
127 		 */
128 		return (-1);
129 	}
130 
131 	for (;;) {
132 		/*
133 		 * Collect a bunch of open file descriptors and close them.
134 		 * Repeat until the directory is exhausted.
135 		 */
136 		dp = (dirent64_t *)buf;
137 		if ((buflen = getdents64(procfd, dp, DIRBUF)) <= 0) {
138 			(void) __close(procfd);
139 			break;
140 		}
141 		dpend = (dirent64_t *)((uintptr_t)buf + buflen);
142 		do {
143 			/* skip '.', '..' and procfd */
144 			if (dp->d_name[0] != '.' &&
145 			    (fd = atoi(dp->d_name)) != procfd &&
146 			    fd >= lowfd)
147 				(void) __close(fd);
148 			dp = (dirent64_t *)((uintptr_t)dp + dp->d_reclen);
149 		} while (dp < dpend);
150 	}
151 
152 	return (0);
153 }
154 
155 static int
perform_flag_actions(spawn_attr_t * sap)156 perform_flag_actions(spawn_attr_t *sap)
157 {
158 	int sig;
159 	struct sigaction action;
160 
161 	if (sap->sa_psflags & POSIX_SPAWN_SETSIGMASK) {
162 		(void) __lwp_sigmask(SIG_SETMASK, &sap->sa_sigmask);
163 	}
164 
165 	if (sap->sa_psflags & POSIX_SPAWN_SETSIGIGN_NP) {
166 		(void) memset(&action, 0, sizeof (action));
167 		action.sa_handler = SIG_IGN;
168 		for (sig = 1; sig < NSIG; sig++) {
169 			if (sigismember(&sap->sa_sigignore, sig))
170 				(void) __sigaction(sig, &action, NULL);
171 		}
172 	}
173 
174 	if (sap->sa_psflags & POSIX_SPAWN_SETSIGDEF) {
175 		(void) memset(&action, 0, sizeof (action));
176 		action.sa_handler = SIG_DFL;
177 		for (sig = 1; sig < NSIG; sig++) {
178 			if (sigismember(&sap->sa_sigdefault, sig))
179 				(void) __sigaction(sig, &action, NULL);
180 		}
181 	}
182 
183 	if (sap->sa_psflags & POSIX_SPAWN_RESETIDS) {
184 		if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
185 			return (errno);
186 	}
187 
188 	if (sap->sa_psflags & POSIX_SPAWN_SETSID) {
189 		if (setsid() == (pid_t)-1) {
190 			return (errno);
191 		}
192 	}
193 
194 	if (sap->sa_psflags & POSIX_SPAWN_SETPGROUP) {
195 		if (setpgid(0, sap->sa_pgroup) != 0)
196 			return (errno);
197 	}
198 
199 	if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDULER) {
200 		if (setparam(P_LWPID, P_MYID,
201 		    sap->sa_schedpolicy, sap->sa_priority) == -1)
202 			return (errno);
203 	} else if (sap->sa_psflags & POSIX_SPAWN_SETSCHEDPARAM) {
204 		if (setprio(P_LWPID, P_MYID, sap->sa_priority, NULL) == -1)
205 			return (errno);
206 	}
207 
208 	return (0);
209 }
210 
211 static int
perform_file_actions(file_attr_t * fap,void * dirbuf)212 perform_file_actions(file_attr_t *fap, void *dirbuf)
213 {
214 	file_attr_t *froot = fap;
215 	int fd;
216 
217 	do {
218 		switch (fap->fa_type) {
219 		case FA_OPEN:
220 			fd = __open(fap->fa_path, fap->fa_oflag, fap->fa_mode);
221 			if (fd < 0)
222 				return (errno);
223 			if (fd != fap->fa_filedes) {
224 				if (__fcntl(fd, F_DUP2FD, fap->fa_filedes) < 0)
225 					return (errno);
226 				(void) __close(fd);
227 			}
228 			break;
229 		case FA_CHDIR:
230 			if (chdir(fap->fa_path) == -1)
231 				return (errno);
232 			break;
233 		case FA_CLOSE:
234 			if (__close(fap->fa_filedes) == -1 &&
235 			    errno != EBADF)	/* already closed, no error */
236 				return (errno);
237 			break;
238 		case FA_DUP2:
239 			fd = __fcntl(fap->fa_filedes, F_DUP2FD,
240 			    fap->fa_newfiledes);
241 			if (fd < 0)
242 				return (errno);
243 			break;
244 		case FA_CLOSEFROM:
245 			if (spawn_closefrom(fap->fa_filedes, dirbuf))
246 				return (errno);
247 			break;
248 		case FA_FCHDIR:
249 			if (fchdir(fap->fa_filedes) == -1)
250 				return (errno);
251 			break;
252 		}
253 	} while ((fap = fap->fa_next) != froot);
254 
255 	return (0);
256 }
257 
258 static int
forkflags(spawn_attr_t * sap)259 forkflags(spawn_attr_t *sap)
260 {
261 	int flags = 0;
262 
263 	if (sap != NULL) {
264 		if (sap->sa_psflags & POSIX_SPAWN_NOSIGCHLD_NP)
265 			flags |= FORK_NOSIGCHLD;
266 		if (sap->sa_psflags & POSIX_SPAWN_WAITPID_NP)
267 			flags |= FORK_WAITPID;
268 	}
269 
270 	return (flags);
271 }
272 
273 /*
274  * set_error() / get_error() are used to guarantee that the local variable
275  * 'error' is set correctly in memory on return from vfork() in the parent.
276  */
277 
278 static int
set_error(int * errp,int err)279 set_error(int *errp, int err)
280 {
281 	return (*errp = err);
282 }
283 
284 static int
get_error(int * errp)285 get_error(int *errp)
286 {
287 	return (*errp);
288 }
289 
290 /*
291  * For MT safety, do not invoke the dynamic linker after calling vfork().
292  * If some other thread was in the dynamic linker when this thread's parent
293  * called vfork() then the dynamic linker's lock would still be held here
294  * (with a defunct owner) and we would deadlock ourself if we invoked it.
295  *
296  * Therefore, all of the functions we call here after returning from
297  * vforkx() in the child are not and must never be exported from libc
298  * as global symbols.  To do so would risk invoking the dynamic linker.
299  */
300 
301 int
posix_spawn(pid_t * pidp,const char * path,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * attrp,char * const * argv,char * const * envp)302 posix_spawn(
303 	pid_t *pidp,
304 	const char *path,
305 	const posix_spawn_file_actions_t *file_actions,
306 	const posix_spawnattr_t *attrp,
307 	char *const *argv,
308 	char *const *envp)
309 {
310 	spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL;
311 	file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL;
312 	void *dirbuf = NULL;
313 	int error;		/* this will be set by the child */
314 	pid_t pid;
315 
316 	if (attrp != NULL && sap == NULL)
317 		return (EINVAL);
318 
319 	if (fap != NULL && fap->fa_need_dirbuf) {
320 		/*
321 		 * Preallocate the buffer for the call to getdents64() in
322 		 * spawn_closefrom() since we can't do it in the vfork() child.
323 		 */
324 		if ((dirbuf = lmalloc(DIRBUF)) == NULL)
325 			return (ENOMEM);
326 	}
327 
328 	switch (pid = vforkx(forkflags(sap))) {
329 	case 0:			/* child */
330 		break;
331 	case -1:		/* parent, failure */
332 		if (dirbuf)
333 			lfree(dirbuf, DIRBUF);
334 		return (errno);
335 	default:		/* parent, success */
336 		/*
337 		 * We don't get here until the child exec()s or exit()s
338 		 */
339 		if (pidp != NULL && get_error(&error) == 0)
340 			*pidp = pid;
341 		if (dirbuf)
342 			lfree(dirbuf, DIRBUF);
343 		return (get_error(&error));
344 	}
345 
346 	if (sap != NULL)
347 		if (set_error(&error, perform_flag_actions(sap)) != 0)
348 			_exit(_EVAPORATE);
349 
350 	if (fap != NULL)
351 		if (set_error(&error, perform_file_actions(fap, dirbuf)) != 0)
352 			_exit(_EVAPORATE);
353 
354 	(void) set_error(&error, 0);
355 	(void) execve(path, argv, envp);
356 	if (sap != NULL && (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP))
357 		_exit(127);
358 	(void) set_error(&error, errno);
359 	_exit(_EVAPORATE);
360 	return (0);	/* not reached */
361 }
362 
363 /*
364  * Much of posix_spawnp() blatently stolen from execvp() (port/gen/execvp.c).
365  */
366 
367 extern int libc__xpg4;
368 
369 static const char *
execat(const char * s1,const char * s2,char * si)370 execat(const char *s1, const char *s2, char *si)
371 {
372 	int cnt = PATH_MAX + 1;
373 	char *s;
374 	char c;
375 
376 	for (s = si; (c = *s1) != '\0' && c != ':'; s1++) {
377 		if (cnt > 0) {
378 			*s++ = c;
379 			cnt--;
380 		}
381 	}
382 	if (si != s && cnt > 0) {
383 		*s++ = '/';
384 		cnt--;
385 	}
386 	for (; (c = *s2) != '\0' && cnt > 0; s2++) {
387 		*s++ = c;
388 		cnt--;
389 	}
390 	*s = '\0';
391 	return (*s1? ++s1: NULL);
392 }
393 
394 /* ARGSUSED */
395 int
posix_spawnp(pid_t * pidp,const char * file,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * attrp,char * const * argv,char * const * envp)396 posix_spawnp(
397 	pid_t *pidp,
398 	const char *file,
399 	const posix_spawn_file_actions_t *file_actions,
400 	const posix_spawnattr_t *attrp,
401 	char *const *argv,
402 	char *const *envp)
403 {
404 	spawn_attr_t *sap = attrp? attrp->__spawn_attrp : NULL;
405 	file_attr_t *fap = file_actions? file_actions->__file_attrp : NULL;
406 	void *dirbuf = NULL;
407 	const char *pathstr = (strchr(file, '/') == NULL)? getenv("PATH") : "";
408 	int xpg4 = libc__xpg4;
409 	int error = 0;		/* this will be set by the child */
410 	char path[PATH_MAX+4];
411 	const char *cp;
412 	pid_t pid;
413 	char **newargs;
414 	int argc;
415 	int i;
416 
417 	if (attrp != NULL && sap == NULL)
418 		return (EINVAL);
419 
420 	if (*file == '\0')
421 		return (EACCES);
422 
423 	if (fap != NULL && fap->fa_need_dirbuf) {
424 		/*
425 		 * Preallocate the buffer for the call to getdents64() in
426 		 * spawn_closefrom() since we can't do it in the vfork() child.
427 		 */
428 		if ((dirbuf = lmalloc(DIRBUF)) == NULL)
429 			return (ENOMEM);
430 	}
431 
432 	/*
433 	 * We may need to invoke the shell with a slightly modified
434 	 * argv[] array.  To do this we need to preallocate the array.
435 	 * We must call alloca() before calling vfork() because doing
436 	 * it after vfork() (in the child) would corrupt the parent.
437 	 */
438 	for (argc = 0; argv[argc] != NULL; argc++)
439 		continue;
440 	newargs = alloca((argc + 2) * sizeof (char *));
441 
442 	switch (pid = vforkx(forkflags(sap))) {
443 	case 0:			/* child */
444 		break;
445 	case -1:		/* parent, failure */
446 		if (dirbuf)
447 			lfree(dirbuf, DIRBUF);
448 		return (errno);
449 	default:		/* parent, success */
450 		/*
451 		 * We don't get here until the child exec()s or exit()s
452 		 */
453 		if (pidp != NULL && get_error(&error) == 0)
454 			*pidp = pid;
455 		if (dirbuf)
456 			lfree(dirbuf, DIRBUF);
457 		return (get_error(&error));
458 	}
459 
460 	if (sap != NULL)
461 		if (set_error(&error, perform_flag_actions(sap)) != 0)
462 			_exit(_EVAPORATE);
463 
464 	if (fap != NULL)
465 		if (set_error(&error, perform_file_actions(fap, dirbuf)) != 0)
466 			_exit(_EVAPORATE);
467 
468 	if (pathstr == NULL) {
469 		/*
470 		 * XPG4:  pathstr is equivalent to _CS_PATH, except that
471 		 * :/usr/sbin is appended when root, and pathstr must end
472 		 * with a colon when not root.  Keep these paths in sync
473 		 * with _CS_PATH in confstr.c.  Note that pathstr must end
474 		 * with a colon when not root so that when file doesn't
475 		 * contain '/', the last call to execat() will result in an
476 		 * attempt to execv file from the current directory.
477 		 */
478 		if (geteuid() == 0 || getuid() == 0) {
479 			if (!xpg4)
480 				pathstr = "/usr/sbin:/usr/ccs/bin:/usr/bin";
481 			else
482 				pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
483 				    "/usr/bin:/opt/SUNWspro/bin:/usr/sbin";
484 		} else {
485 			if (!xpg4)
486 				pathstr = "/usr/ccs/bin:/usr/bin:";
487 			else
488 				pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
489 				    "/usr/bin:/opt/SUNWspro/bin:";
490 		}
491 	}
492 
493 	cp = pathstr;
494 	do {
495 		cp = execat(cp, file, path);
496 		/*
497 		 * 4025035 and 4038378
498 		 * if a filename begins with a "-" prepend "./" so that
499 		 * the shell can't interpret it as an option
500 		 */
501 		if (*path == '-') {
502 			char *s;
503 
504 			for (s = path; *s != '\0'; s++)
505 				continue;
506 			for (; s >= path; s--)
507 				*(s + 2) = *s;
508 			path[0] = '.';
509 			path[1] = '/';
510 		}
511 		(void) set_error(&error, 0);
512 		(void) execve(path, argv, envp);
513 		if (set_error(&error, errno) == ENOEXEC) {
514 			newargs[0] = "sh";
515 			newargs[1] = path;
516 			for (i = 1; i <= argc; i++)
517 				newargs[i + 1] = argv[i];
518 			(void) set_error(&error, 0);
519 			(void) execve(_PATH_BSHELL, newargs, envp);
520 			if (sap != NULL &&
521 			    (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP))
522 				_exit(127);
523 			(void) set_error(&error, errno);
524 			_exit(_EVAPORATE);
525 		}
526 	} while (cp);
527 
528 	if (sap != NULL &&
529 	    (sap->sa_psflags & POSIX_SPAWN_NOEXECERR_NP)) {
530 		(void) set_error(&error, 0);
531 		_exit(127);
532 	}
533 	_exit(_EVAPORATE);
534 	return (0);	/* not reached */
535 }
536 
537 int
posix_spawn_file_actions_init(posix_spawn_file_actions_t * file_actions)538 posix_spawn_file_actions_init(
539 	posix_spawn_file_actions_t *file_actions)
540 {
541 	file_actions->__file_attrp = NULL;
542 	return (0);
543 }
544 
545 int
posix_spawn_file_actions_destroy(posix_spawn_file_actions_t * file_actions)546 posix_spawn_file_actions_destroy(
547 	posix_spawn_file_actions_t *file_actions)
548 {
549 	file_attr_t *froot = file_actions->__file_attrp;
550 	file_attr_t *fap;
551 	file_attr_t *next;
552 
553 	if ((fap = froot) != NULL) {
554 		do {
555 			next = fap->fa_next;
556 			if (fap->fa_type == FA_OPEN || fap->fa_type == FA_CHDIR)
557 				lfree(fap->fa_path, fap->fa_pathsize);
558 			lfree(fap, sizeof (*fap));
559 		} while ((fap = next) != froot);
560 	}
561 	file_actions->__file_attrp = NULL;
562 	return (0);
563 }
564 
565 static void
add_file_attr(posix_spawn_file_actions_t * file_actions,file_attr_t * fap)566 add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap)
567 {
568 	file_attr_t *froot = file_actions->__file_attrp;
569 
570 	if (froot == NULL) {
571 		fap->fa_next = fap->fa_prev = fap;
572 		file_actions->__file_attrp = froot = fap;
573 	} else {
574 		fap->fa_next = froot;
575 		fap->fa_prev = froot->fa_prev;
576 		froot->fa_prev->fa_next = fap;
577 		froot->fa_prev = fap;
578 	}
579 
580 	/*
581 	 * Once set, __file_attrp no longer changes, so this assignment
582 	 * always goes into the first element in the list, as required.
583 	 */
584 	if (fap->fa_type == FA_CLOSEFROM)
585 		froot->fa_need_dirbuf = 1;
586 }
587 
588 int
posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * restrict file_actions,int filedes,const char * restrict path,int oflag,mode_t mode)589 posix_spawn_file_actions_addopen(
590 	posix_spawn_file_actions_t *restrict file_actions,
591 	int filedes,
592 	const char *restrict path,
593 	int oflag,
594 	mode_t mode)
595 {
596 	file_attr_t *fap;
597 
598 	if (filedes < 0)
599 		return (EBADF);
600 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
601 		return (ENOMEM);
602 
603 	fap->fa_pathsize = strlen(path) + 1;
604 	if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
605 		lfree(fap, sizeof (*fap));
606 		return (ENOMEM);
607 	}
608 	(void) memcpy(fap->fa_path, path, fap->fa_pathsize);
609 
610 	fap->fa_type = FA_OPEN;
611 	fap->fa_oflag = oflag;
612 	fap->fa_mode = mode;
613 	fap->fa_filedes = filedes;
614 	add_file_attr(file_actions, fap);
615 
616 	return (0);
617 }
618 
619 int
posix_spawn_file_actions_addclose(posix_spawn_file_actions_t * restrict file_actions,int filedes)620 posix_spawn_file_actions_addclose(
621 	posix_spawn_file_actions_t *restrict file_actions,
622 	int filedes)
623 {
624 	file_attr_t *fap;
625 
626 	if (filedes < 0)
627 		return (EBADF);
628 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
629 		return (ENOMEM);
630 
631 	fap->fa_type = FA_CLOSE;
632 	fap->fa_filedes = filedes;
633 	add_file_attr(file_actions, fap);
634 
635 	return (0);
636 }
637 
638 int
posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t * restrict file_actions,int filedes,int newfiledes)639 posix_spawn_file_actions_adddup2(
640 	posix_spawn_file_actions_t *restrict file_actions,
641 	int filedes,
642 	int newfiledes)
643 {
644 	file_attr_t *fap;
645 
646 	if (filedes < 0 || newfiledes < 0)
647 		return (EBADF);
648 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
649 		return (ENOMEM);
650 
651 	fap->fa_type = FA_DUP2;
652 	fap->fa_filedes = filedes;
653 	fap->fa_newfiledes = newfiledes;
654 	add_file_attr(file_actions, fap);
655 
656 	return (0);
657 }
658 
659 int
posix_spawn_file_actions_addclosefrom_np(posix_spawn_file_actions_t * restrict file_actions,int lowfiledes)660 posix_spawn_file_actions_addclosefrom_np(
661 	posix_spawn_file_actions_t *restrict file_actions,
662 	int lowfiledes)
663 {
664 	file_attr_t *fap;
665 
666 	if (lowfiledes < 0)
667 		return (EBADF);
668 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
669 		return (ENOMEM);
670 	fap->fa_type = FA_CLOSEFROM;
671 	fap->fa_filedes = lowfiledes;
672 	add_file_attr(file_actions, fap);
673 
674 	return (0);
675 }
676 
677 int
posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t * restrict file_actions,const char * restrict path)678 posix_spawn_file_actions_addchdir(
679     posix_spawn_file_actions_t *restrict file_actions,
680     const char *restrict path)
681 {
682 	file_attr_t *fap;
683 
684 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
685 		return (ENOMEM);
686 
687 	fap->fa_pathsize = strlen(path) + 1;
688 	if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
689 		lfree(fap, sizeof (*fap));
690 		return (ENOMEM);
691 	}
692 	(void) memcpy(fap->fa_path, path, fap->fa_pathsize);
693 
694 	fap->fa_type = FA_CHDIR;
695 	add_file_attr(file_actions, fap);
696 
697 	return (0);
698 }
699 
700 int
posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t * restrict file_actions,const char * restrict path)701 posix_spawn_file_actions_addchdir_np(
702     posix_spawn_file_actions_t *restrict file_actions,
703     const char *restrict path)
704 {
705 	return (posix_spawn_file_actions_addchdir(file_actions, path));
706 }
707 
708 int
posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t * restrict file_actions,int fd)709 posix_spawn_file_actions_addfchdir(
710     posix_spawn_file_actions_t *restrict file_actions,
711     int fd)
712 {
713 	file_attr_t *fap;
714 
715 	if (fd < 0)
716 		return (EBADF);
717 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
718 		return (ENOMEM);
719 	fap->fa_type = FA_FCHDIR;
720 	fap->fa_filedes = fd;
721 	add_file_attr(file_actions, fap);
722 	return (0);
723 }
724 
725 int
posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t * restrict file_actions,int fd)726 posix_spawn_file_actions_addfchdir_np(
727     posix_spawn_file_actions_t *restrict file_actions,
728     int fd)
729 {
730 	return (posix_spawn_file_actions_addfchdir(file_actions, fd));
731 }
732 
733 int
posix_spawnattr_init(posix_spawnattr_t * attr)734 posix_spawnattr_init(
735 	posix_spawnattr_t *attr)
736 {
737 	if ((attr->__spawn_attrp = lmalloc(sizeof (posix_spawnattr_t))) == NULL)
738 		return (ENOMEM);
739 	/*
740 	 * Add default stuff here?
741 	 */
742 	return (0);
743 }
744 
745 int
posix_spawnattr_destroy(posix_spawnattr_t * attr)746 posix_spawnattr_destroy(
747 	posix_spawnattr_t *attr)
748 {
749 	spawn_attr_t *sap = attr->__spawn_attrp;
750 
751 	if (sap == NULL)
752 		return (EINVAL);
753 
754 	/*
755 	 * deallocate stuff here?
756 	 */
757 	lfree(sap, sizeof (*sap));
758 	attr->__spawn_attrp = NULL;
759 	return (0);
760 }
761 
762 int
posix_spawnattr_setflags(posix_spawnattr_t * attr,short flags)763 posix_spawnattr_setflags(
764 	posix_spawnattr_t *attr,
765 	short flags)
766 {
767 	spawn_attr_t *sap = attr->__spawn_attrp;
768 
769 	if (sap == NULL ||
770 	    (flags & ~ALL_POSIX_SPAWN_FLAGS))
771 		return (EINVAL);
772 
773 	sap->sa_psflags = flags;
774 	return (0);
775 }
776 
777 int
posix_spawnattr_getflags(const posix_spawnattr_t * attr,short * flags)778 posix_spawnattr_getflags(
779 	const posix_spawnattr_t *attr,
780 	short *flags)
781 {
782 	spawn_attr_t *sap = attr->__spawn_attrp;
783 
784 	if (sap == NULL)
785 		return (EINVAL);
786 
787 	*flags = sap->sa_psflags;
788 	return (0);
789 }
790 
791 int
posix_spawnattr_setpgroup(posix_spawnattr_t * attr,pid_t pgroup)792 posix_spawnattr_setpgroup(
793 	posix_spawnattr_t *attr,
794 	pid_t pgroup)
795 {
796 	spawn_attr_t *sap = attr->__spawn_attrp;
797 
798 	if (sap == NULL)
799 		return (EINVAL);
800 
801 	sap->sa_pgroup = pgroup;
802 	return (0);
803 }
804 
805 int
posix_spawnattr_getpgroup(const posix_spawnattr_t * attr,pid_t * pgroup)806 posix_spawnattr_getpgroup(
807 	const posix_spawnattr_t *attr,
808 	pid_t *pgroup)
809 {
810 	spawn_attr_t *sap = attr->__spawn_attrp;
811 
812 	if (sap == NULL)
813 		return (EINVAL);
814 
815 	*pgroup = sap->sa_pgroup;
816 	return (0);
817 }
818 
819 int
posix_spawnattr_setschedparam(posix_spawnattr_t * attr,const struct sched_param * schedparam)820 posix_spawnattr_setschedparam(
821 	posix_spawnattr_t *attr,
822 	const struct sched_param *schedparam)
823 {
824 	spawn_attr_t *sap = attr->__spawn_attrp;
825 
826 	if (sap == NULL)
827 		return (EINVAL);
828 
829 	/*
830 	 * Check validity?
831 	 */
832 	sap->sa_priority = schedparam->sched_priority;
833 	return (0);
834 }
835 
836 int
posix_spawnattr_getschedparam(const posix_spawnattr_t * attr,struct sched_param * schedparam)837 posix_spawnattr_getschedparam(
838 	const posix_spawnattr_t *attr,
839 	struct sched_param *schedparam)
840 {
841 	spawn_attr_t *sap = attr->__spawn_attrp;
842 
843 	if (sap == NULL)
844 		return (EINVAL);
845 
846 	schedparam->sched_priority = sap->sa_priority;
847 	return (0);
848 }
849 
850 int
posix_spawnattr_setschedpolicy(posix_spawnattr_t * attr,int schedpolicy)851 posix_spawnattr_setschedpolicy(
852 	posix_spawnattr_t *attr,
853 	int schedpolicy)
854 {
855 	spawn_attr_t *sap = attr->__spawn_attrp;
856 
857 	if (sap == NULL || schedpolicy == SCHED_SYS)
858 		return (EINVAL);
859 
860 	/*
861 	 * Cache the policy information for later use
862 	 * by the vfork() child of posix_spawn().
863 	 */
864 	if (get_info_by_policy(schedpolicy) == NULL)
865 		return (errno);
866 
867 	sap->sa_schedpolicy = schedpolicy;
868 	return (0);
869 }
870 
871 int
posix_spawnattr_getschedpolicy(const posix_spawnattr_t * attr,int * schedpolicy)872 posix_spawnattr_getschedpolicy(
873 	const posix_spawnattr_t *attr,
874 	int *schedpolicy)
875 {
876 	spawn_attr_t *sap = attr->__spawn_attrp;
877 
878 	if (sap == NULL)
879 		return (EINVAL);
880 
881 	*schedpolicy = sap->sa_schedpolicy;
882 	return (0);
883 }
884 
885 int
posix_spawnattr_setsigdefault(posix_spawnattr_t * attr,const sigset_t * sigdefault)886 posix_spawnattr_setsigdefault(
887 	posix_spawnattr_t *attr,
888 	const sigset_t *sigdefault)
889 {
890 	spawn_attr_t *sap = attr->__spawn_attrp;
891 
892 	if (sap == NULL)
893 		return (EINVAL);
894 
895 	sap->sa_sigdefault = *sigdefault;
896 	return (0);
897 }
898 
899 int
posix_spawnattr_getsigdefault(const posix_spawnattr_t * attr,sigset_t * sigdefault)900 posix_spawnattr_getsigdefault(
901 	const posix_spawnattr_t *attr,
902 	sigset_t *sigdefault)
903 {
904 	spawn_attr_t *sap = attr->__spawn_attrp;
905 
906 	if (sap == NULL)
907 		return (EINVAL);
908 
909 	*sigdefault = sap->sa_sigdefault;
910 	return (0);
911 }
912 
913 int
posix_spawnattr_setsigignore_np(posix_spawnattr_t * attr,const sigset_t * sigignore)914 posix_spawnattr_setsigignore_np(
915 	posix_spawnattr_t *attr,
916 	const sigset_t *sigignore)
917 {
918 	spawn_attr_t *sap = attr->__spawn_attrp;
919 
920 	if (sap == NULL)
921 		return (EINVAL);
922 
923 	sap->sa_sigignore = *sigignore;
924 	return (0);
925 }
926 
927 int
posix_spawnattr_getsigignore_np(const posix_spawnattr_t * attr,sigset_t * sigignore)928 posix_spawnattr_getsigignore_np(
929 	const posix_spawnattr_t *attr,
930 	sigset_t *sigignore)
931 {
932 	spawn_attr_t *sap = attr->__spawn_attrp;
933 
934 	if (sap == NULL)
935 		return (EINVAL);
936 
937 	*sigignore = sap->sa_sigignore;
938 	return (0);
939 }
940 
941 int
posix_spawnattr_setsigmask(posix_spawnattr_t * attr,const sigset_t * sigmask)942 posix_spawnattr_setsigmask(
943 	posix_spawnattr_t *attr,
944 	const sigset_t *sigmask)
945 {
946 	spawn_attr_t *sap = attr->__spawn_attrp;
947 
948 	if (sap == NULL)
949 		return (EINVAL);
950 
951 	sap->sa_sigmask = *sigmask;
952 	return (0);
953 }
954 
955 int
posix_spawnattr_getsigmask(const posix_spawnattr_t * attr,sigset_t * sigmask)956 posix_spawnattr_getsigmask(
957 	const posix_spawnattr_t *attr,
958 	sigset_t *sigmask)
959 {
960 	spawn_attr_t *sap = attr->__spawn_attrp;
961 
962 	if (sap == NULL)
963 		return (EINVAL);
964 
965 	*sigmask = sap->sa_sigmask;
966 	return (0);
967 }
968 
969 /*
970  * Spawn a process to run "sh -c <cmd>".  Return the child's pid (in
971  * *pidp), and a file descriptor (in *fdp) for reading or writing to the
972  * child process, depending on the 'write' argument.
973  * Return 0 on success; otherwise return an error code.
974  */
975 int
posix_spawn_pipe_np(pid_t * pidp,int * fdp,const char * cmd,boolean_t write,posix_spawn_file_actions_t * fact,posix_spawnattr_t * attr)976 posix_spawn_pipe_np(pid_t *pidp, int *fdp,
977     const char *cmd, boolean_t write,
978     posix_spawn_file_actions_t *fact, posix_spawnattr_t *attr)
979 {
980 	int	p[2];
981 	int	myside, yourside, stdio;
982 	const char *shpath = _PATH_BSHELL;
983 	const char *argvec[4] = { "sh", "-c", cmd, NULL };
984 	int	error;
985 
986 	if (pipe(p) < 0)
987 		return (errno);
988 
989 	if (access(shpath, X_OK))	/* XPG4 Requirement: */
990 		shpath = "";		/* force child to fail immediately */
991 
992 	if (write) {
993 		/*
994 		 * Data is read from p[0] and written to p[1].
995 		 * 'stdio' is the fd in the child process that should be
996 		 * connected to the pipe.
997 		 */
998 		myside = p[1];
999 		yourside = p[0];
1000 		stdio = STDIN_FILENO;
1001 	} else {
1002 		myside = p[0];
1003 		yourside = p[1];
1004 		stdio = STDOUT_FILENO;
1005 	}
1006 
1007 	error = posix_spawn_file_actions_addclose(fact, myside);
1008 	if (yourside != stdio) {
1009 		if (error == 0) {
1010 			error = posix_spawn_file_actions_adddup2(fact,
1011 			    yourside, stdio);
1012 		}
1013 		if (error == 0) {
1014 			error = posix_spawn_file_actions_addclose(fact,
1015 			    yourside);
1016 		}
1017 	}
1018 
1019 	if (error)
1020 		return (error);
1021 	error = posix_spawn(pidp, shpath, fact, attr,
1022 	    (char *const *)argvec, (char *const *)_environ);
1023 	(void) close(yourside);
1024 	if (error) {
1025 		(void) close(myside);
1026 		return (error);
1027 	}
1028 
1029 	*fdp = myside;
1030 	return (0);
1031 }
1032