1/*
2 * Copyright (c) 2006-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * [SPN] Support for _POSIX_SPAWN
26 */
27
28#include <spawn.h>
29#include <spawn_private.h>
30#include <sys/spawn_internal.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <limits.h>	/* for OPEN_MAX, PATH_MAX */
34#include <string.h>	/* for strlcpy() */
35#include <paths.h>	/* for _PATH_DEFPATH */
36#include <sys/stat.h>	/* for struct stat */
37
38/*
39 * posix_spawnp
40 *
41 * Description:	Create a new process from the process image corresponding to
42 *		the supplied 'file' argument and the parent processes path
43 *		environment.
44 *
45 * Parameters:	pid				Pointer to pid_t to receive the
46 *						PID of the spawned process, if
47 *						successful and 'pid' != NULL
48 *		file				Name of image file to spawn
49 *		file_actions			spawn file actions object which
50 *						describes file actions to be
51 *						performed during the spawn
52 *		attrp				spawn attributes object which
53 *						describes attributes to be
54 *						applied during the spawn
55 *		argv				argument vector array; NULL
56 *						terminated
57 *		envp				environment vector array; NULL
58 *						terminated
59 *
60 * Returns:	0				Success
61 *		!0				An errno value indicating the
62 *						cause of the failure to spawn
63 *
64 * Notes:	Much of this function is derived from code from execvP() from
65 *		exec.c in libc; this common code should be factored out at
66 *		some point to prevent code duplication or desynchronization vs.
67 *		bug fixes applied to one set of code but not the other.
68 */
69int
70posix_spawnp(pid_t * __restrict pid, const char * __restrict file,
71		const posix_spawn_file_actions_t *file_actions,
72		const posix_spawnattr_t * __restrict attrp,
73		char *const argv[ __restrict], char *const envp[ __restrict])
74{
75	const char *env_path;
76	char *bp;
77	char *cur;
78	char *p;
79	char **memp;
80	int lp;
81	int ln;
82	int cnt;
83	int err = 0;
84	int eacces = 0;
85	struct stat sb;
86	char path_buf[PATH_MAX];
87
88	if ((env_path = getenv("PATH")) == NULL)
89		env_path = _PATH_DEFPATH;
90
91	/* If it's an absolute or relative path name, it's easy. */
92	if (index(file, '/')) {
93		bp = (char *)file;
94		cur = NULL;
95		goto retry;
96	}
97	bp = path_buf;
98
99	/* If it's an empty path name, fail in the usual POSIX way. */
100	if (*file == '\0')
101		return (ENOENT);
102
103	if ((cur = alloca(strlen(env_path) + 1)) == NULL)
104		return ENOMEM;
105	strcpy(cur, env_path);
106	while ((p = strsep(&cur, ":")) != NULL) {
107		/*
108		 * It's a SHELL path -- double, leading and trailing colons
109		 * mean the current directory.
110		 */
111		if (*p == '\0') {
112			p = ".";
113			lp = 1;
114		} else {
115			lp = strlen(p);
116		}
117		ln = strlen(file);
118
119		/*
120		 * If the path is too long complain.  This is a possible
121		 * security issue; given a way to make the path too long
122		 * the user may spawn the wrong program.
123		 */
124		if (lp + ln + 2 > sizeof(path_buf)) {
125			err = ENAMETOOLONG;
126			goto done;
127		}
128		bcopy(p, path_buf, lp);
129		path_buf[lp] = '/';
130		bcopy(file, path_buf + lp + 1, ln);
131		path_buf[lp + ln + 1] = '\0';
132
133retry:		err = posix_spawn(pid, bp, file_actions, attrp, argv, envp);
134		switch (err) {
135		case E2BIG:
136		case ENOMEM:
137		case ETXTBSY:
138			goto done;
139		case ELOOP:
140		case ENAMETOOLONG:
141		case ENOENT:
142		case ENOTDIR:
143			break;
144		case ENOEXEC:
145			for (cnt = 0; argv[cnt]; ++cnt)
146				;
147			memp = alloca((cnt + 2) * sizeof(char *));
148			if (memp == NULL) {
149				/* errno = ENOMEM; XXX override ENOEXEC? */
150				goto done;
151			}
152			memp[0] = "sh";
153			memp[1] = bp;
154			bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
155			err = posix_spawn(pid, _PATH_BSHELL, file_actions, attrp, memp, envp);
156			goto done;
157		default:
158			/*
159			 * EACCES may be for an inaccessible directory or
160			 * a non-executable file.  Call stat() to decide
161			 * which.  This also handles ambiguities for EFAULT
162			 * and EIO, and undocumented errors like ESTALE.
163			 * We hope that the race for a stat() is unimportant.
164			 */
165			if (stat(bp, &sb) != 0)
166				break;
167			if (err == EACCES) {
168				eacces = 1;
169				continue;
170			}
171			goto done;
172		}
173	}
174	if (eacces)
175		err = EACCES;
176	else
177		err = ENOENT;
178done:
179	return (err);
180}
181