xref: /titanic_41/usr/src/lib/libc/port/threads/spawn.c (revision 92a0208178405fef708b0283ffcaa02fbc3468ff)
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;		/* 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 	_exit(_EVAPORATE);
392 	return (0);	/* not reached */
393 }
394 
395 int
396 posix_spawn_file_actions_init(
397 	posix_spawn_file_actions_t *file_actions)
398 {
399 	file_actions->__file_attrp = NULL;
400 	return (0);
401 }
402 
403 int
404 posix_spawn_file_actions_destroy(
405 	posix_spawn_file_actions_t *file_actions)
406 {
407 	file_attr_t *froot = file_actions->__file_attrp;
408 	file_attr_t *fap;
409 	file_attr_t *next;
410 
411 	if ((fap = froot) != NULL) {
412 		do {
413 			next = fap->fa_next;
414 			if (fap-> fa_type == FA_OPEN)
415 				lfree(fap->fa_path, fap->fa_pathsize);
416 			lfree(fap, sizeof (*fap));
417 		} while ((fap = next) != froot);
418 	}
419 	file_actions->__file_attrp = NULL;
420 	return (0);
421 }
422 
423 static void
424 add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap)
425 {
426 	file_attr_t *froot = file_actions->__file_attrp;
427 
428 	if (froot == NULL) {
429 		fap->fa_next = fap->fa_prev = fap;
430 		file_actions->__file_attrp = fap;
431 	} else {
432 		fap->fa_next = froot;
433 		fap->fa_prev = froot->fa_prev;
434 		froot->fa_prev->fa_next = fap;
435 		froot->fa_prev = fap;
436 	}
437 }
438 
439 int
440 posix_spawn_file_actions_addopen(
441 	posix_spawn_file_actions_t *file_actions,
442 	int filedes,
443 	const char *path,
444 	int oflag,
445 	mode_t mode)
446 {
447 	file_attr_t *fap;
448 
449 	if (filedes < 0)
450 		return (EBADF);
451 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
452 		return (ENOMEM);
453 
454 	fap->fa_pathsize = strlen(path) + 1;
455 	if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
456 		lfree(fap, sizeof (*fap));
457 		return (ENOMEM);
458 	}
459 	(void) strcpy(fap->fa_path, path);
460 
461 	fap->fa_type = FA_OPEN;
462 	fap->fa_oflag = oflag;
463 	fap->fa_mode = mode;
464 	fap->fa_filedes = filedes;
465 	add_file_attr(file_actions, fap);
466 
467 	return (0);
468 }
469 
470 int
471 posix_spawn_file_actions_addclose(
472 	posix_spawn_file_actions_t *file_actions,
473 	int filedes)
474 {
475 	file_attr_t *fap;
476 
477 	if (filedes < 0)
478 		return (EBADF);
479 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
480 		return (ENOMEM);
481 
482 	fap->fa_type = FA_CLOSE;
483 	fap->fa_filedes = filedes;
484 	add_file_attr(file_actions, fap);
485 
486 	return (0);
487 }
488 
489 int
490 posix_spawn_file_actions_adddup2(
491 	posix_spawn_file_actions_t *file_actions,
492 	int filedes,
493 	int newfiledes)
494 {
495 	file_attr_t *fap;
496 
497 	if (filedes < 0 || newfiledes < 0)
498 		return (EBADF);
499 	if ((fap = lmalloc(sizeof (*fap))) == NULL)
500 		return (ENOMEM);
501 
502 	fap->fa_type = FA_DUP2;
503 	fap->fa_filedes = filedes;
504 	fap->fa_newfiledes = newfiledes;
505 	add_file_attr(file_actions, fap);
506 
507 	return (0);
508 }
509 
510 int
511 posix_spawnattr_init(
512 	posix_spawnattr_t *attr)
513 {
514 	if ((attr->__spawn_attrp = lmalloc(sizeof (posix_spawnattr_t))) == NULL)
515 		return (ENOMEM);
516 	/*
517 	 * Add default stuff here?
518 	 */
519 	return (0);
520 }
521 
522 int
523 posix_spawnattr_destroy(
524 	posix_spawnattr_t *attr)
525 {
526 	spawn_attr_t *sap = attr->__spawn_attrp;
527 
528 	if (sap == NULL)
529 		return (EINVAL);
530 
531 	/*
532 	 * deallocate stuff here?
533 	 */
534 	lfree(sap, sizeof (*sap));
535 	attr->__spawn_attrp = NULL;
536 	return (0);
537 }
538 
539 int
540 posix_spawnattr_setflags(
541 	posix_spawnattr_t *attr,
542 	short flags)
543 {
544 	spawn_attr_t *sap = attr->__spawn_attrp;
545 
546 	if (sap == NULL ||
547 	    (flags & ~ALL_POSIX_SPAWN_FLAGS))
548 		return (EINVAL);
549 
550 	sap->sa_psflags = flags;
551 	return (0);
552 }
553 
554 int
555 posix_spawnattr_getflags(
556 	const posix_spawnattr_t *attr,
557 	short *flags)
558 {
559 	spawn_attr_t *sap = attr->__spawn_attrp;
560 
561 	if (sap == NULL)
562 		return (EINVAL);
563 
564 	*flags = sap->sa_psflags;
565 	return (0);
566 }
567 
568 int
569 posix_spawnattr_setpgroup(
570 	posix_spawnattr_t *attr,
571 	pid_t pgroup)
572 {
573 	spawn_attr_t *sap = attr->__spawn_attrp;
574 
575 	if (sap == NULL)
576 		return (EINVAL);
577 
578 	sap->sa_pgroup = pgroup;
579 	return (0);
580 }
581 
582 int
583 posix_spawnattr_getpgroup(
584 	const posix_spawnattr_t *attr,
585 	pid_t *pgroup)
586 {
587 	spawn_attr_t *sap = attr->__spawn_attrp;
588 
589 	if (sap == NULL)
590 		return (EINVAL);
591 
592 	*pgroup = sap->sa_pgroup;
593 	return (0);
594 }
595 
596 int
597 posix_spawnattr_setschedparam(
598 	posix_spawnattr_t *attr,
599 	const struct sched_param *schedparam)
600 {
601 	spawn_attr_t *sap = attr->__spawn_attrp;
602 
603 	if (sap == NULL)
604 		return (EINVAL);
605 
606 	/*
607 	 * Check validity?
608 	 */
609 	sap->sa_priority = schedparam->sched_priority;
610 	return (0);
611 }
612 
613 int
614 posix_spawnattr_getschedparam(
615 	const posix_spawnattr_t *attr,
616 	struct sched_param *schedparam)
617 {
618 	spawn_attr_t *sap = attr->__spawn_attrp;
619 
620 	if (sap == NULL)
621 		return (EINVAL);
622 
623 	schedparam->sched_priority = sap->sa_priority;
624 	return (0);
625 }
626 
627 int
628 posix_spawnattr_setschedpolicy(
629 	posix_spawnattr_t *attr,
630 	int schedpolicy)
631 {
632 	spawn_attr_t *sap = attr->__spawn_attrp;
633 
634 	if (sap == NULL || schedpolicy == SCHED_SYS)
635 		return (EINVAL);
636 
637 	/*
638 	 * Cache the policy information for later use
639 	 * by the vfork() child of posix_spawn().
640 	 */
641 	if (get_info_by_policy(schedpolicy) == NULL)
642 		return (errno);
643 
644 	sap->sa_schedpolicy = schedpolicy;
645 	return (0);
646 }
647 
648 int
649 posix_spawnattr_getschedpolicy(
650 	const posix_spawnattr_t *attr,
651 	int *schedpolicy)
652 {
653 	spawn_attr_t *sap = attr->__spawn_attrp;
654 
655 	if (sap == NULL)
656 		return (EINVAL);
657 
658 	*schedpolicy = sap->sa_schedpolicy;
659 	return (0);
660 }
661 
662 int
663 posix_spawnattr_setsigdefault(
664 	posix_spawnattr_t *attr,
665 	const sigset_t *sigdefault)
666 {
667 	spawn_attr_t *sap = attr->__spawn_attrp;
668 
669 	if (sap == NULL)
670 		return (EINVAL);
671 
672 	sap->sa_sigdefault = *sigdefault;
673 	return (0);
674 }
675 
676 int
677 posix_spawnattr_getsigdefault(
678 	const posix_spawnattr_t *attr,
679 	sigset_t *sigdefault)
680 {
681 	spawn_attr_t *sap = attr->__spawn_attrp;
682 
683 	if (sap == NULL)
684 		return (EINVAL);
685 
686 	*sigdefault = sap->sa_sigdefault;
687 	return (0);
688 }
689 
690 int
691 posix_spawnattr_setsigmask(
692 	posix_spawnattr_t *attr,
693 	const sigset_t *sigmask)
694 {
695 	spawn_attr_t *sap = attr->__spawn_attrp;
696 
697 	if (sap == NULL)
698 		return (EINVAL);
699 
700 	sap->sa_sigmask = *sigmask;
701 	return (0);
702 }
703 
704 int
705 posix_spawnattr_getsigmask(
706 	const posix_spawnattr_t *attr,
707 	sigset_t *sigmask)
708 {
709 	spawn_attr_t *sap = attr->__spawn_attrp;
710 
711 	if (sap == NULL)
712 		return (EINVAL);
713 
714 	*sigmask = sap->sa_sigmask;
715 	return (0);
716 }
717