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