]> git.saurik.com Git - apple/libc.git/blame - sys/posix_spawn.c
Libc-1044.40.1.tar.gz
[apple/libc.git] / sys / posix_spawn.c
CommitLineData
224c7076 1/*
6465356a 2 * Copyright (c) 2006-2012 Apple Inc. All rights reserved.
224c7076
A
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
224c7076 28#include <spawn.h>
34e8f829 29#include <spawn_private.h>
224c7076
A
30#include <sys/spawn_internal.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <limits.h> /* for OPEN_MAX, PATH_MAX */
224c7076
A
34#include <string.h> /* for strlcpy() */
35#include <paths.h> /* for _PATH_DEFPATH */
36#include <sys/stat.h> /* for struct stat */
224c7076
A
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}