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