1 /* Copyright (C) 2000, 2011 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #include <errno.h>
19 #include <alloca.h>
20 #include <unistd.h>
21 #include <signal.h>
22 #include <stdbool.h>
23 #include <fcntl.h>
24
25 #include <sys/resource.h>
26 #include <not-cancel.h>
27
28 #include <spawn.h>
29 #include "spawn_int.h"
30
31 /* The Unix standard contains a long explanation of the way to signal
32 an error after the fork() was successful. Since no new wait status
33 was wanted there is no way to signal an error using one of the
34 available methods. The committee chose to signal an error by a
35 normal program exit with the exit code 127. */
36 #define SPAWN_ERROR 127
37
38 /* Execute file actions.
39 * Returns true on error.
40 */
execute_file_actions(const posix_spawn_file_actions_t * fa)41 inline static bool execute_file_actions(const posix_spawn_file_actions_t *fa)
42 {
43 struct rlimit64 fdlimit;
44 bool have_fdlimit = false;
45 int cnt;
46
47 for (cnt = 0; cnt < fa->__used; ++cnt) {
48 struct __spawn_action *action = &fa->__actions[cnt];
49
50 switch (action->tag) {
51 case spawn_do_close:
52 if (close_not_cancel(action->action.close_action.fd) != 0) {
53 if (!have_fdlimit) {
54 getrlimit64(RLIMIT_NOFILE, &fdlimit);
55 have_fdlimit = true;
56 }
57
58 /* Only signal errors for file descriptors out of range. */
59 if (0 > action->action.close_action.fd
60 || action->action.close_action.fd >= fdlimit.rlim_cur)
61 /* Signal the error. */
62 return true;
63 }
64 break;
65
66 case spawn_do_open:;
67 int new_fd = open_not_cancel(action->action.open_action.path,
68 action->action.open_action.oflag
69 | O_LARGEFILE,
70 action->action.open_action.mode);
71
72 if (new_fd == -1)
73 return true;
74
75 /* Make sure the desired file descriptor is used. */
76 if (new_fd != action->action.open_action.fd) {
77 if (dup2(new_fd, action->action.open_action.fd)
78 != action->action.open_action.fd)
79 return true;
80
81 if (close_not_cancel(new_fd) != 0)
82 return true;
83 }
84 break;
85
86 case spawn_do_dup2:
87 if (dup2(action->action.dup2_action.fd,
88 action->action.dup2_action.newfd)
89 != action->action.dup2_action.newfd)
90 return true;
91 break;
92 }
93 }
94
95 return false;
96 }
97
98 #define DANGEROUS (POSIX_SPAWN_SETSIGMASK \
99 | POSIX_SPAWN_SETSIGDEF \
100 | POSIX_SPAWN_SETSCHEDPARAM \
101 | POSIX_SPAWN_SETSCHEDULER \
102 | POSIX_SPAWN_SETPGROUP \
103 | POSIX_SPAWN_RESETIDS)
is_vfork_safe(short int flags)104 inline static bool is_vfork_safe(short int flags)
105 {
106 return ((flags & POSIX_SPAWN_USEVFORK) || !(flags & DANGEROUS));
107 }
108
109
110 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
111 Before running the process perform the actions described in FILE-ACTIONS. */
112 static int
__spawni(pid_t * pid,const char * file,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[],const char * path)113 __spawni(pid_t *pid, const char *file,
114 const posix_spawn_file_actions_t *fa,
115 const posix_spawnattr_t *attrp, char *const argv[],
116 char *const envp[], const char *path)
117 {
118 short int flags = attrp ? attrp->__flags : 0;
119
120 pid_t new_pid;
121 if (is_vfork_safe(flags) && !fa)
122 new_pid = vfork();
123 else {
124 #ifdef __ARCH_USE_MMU__
125 new_pid = fork();
126 #else
127 return ENOSYS;
128 #endif
129 }
130
131 if (new_pid) {
132 if (new_pid < 0)
133 return errno;
134
135 if (pid)
136 *pid = new_pid;
137
138 return 0;
139 }
140
141 if (flags & POSIX_SPAWN_SETSIGMASK) {
142 if (sigprocmask(SIG_SETMASK, &attrp->__ss, NULL) != 0)
143 goto error;
144 }
145
146 if (flags & POSIX_SPAWN_SETSIGDEF) {
147 /* We have to iterate over all signals. This could possibly be
148 done better but it requires system specific solutions since
149 the sigset_t data type can be very different on different
150 architectures. */
151 struct sigaction sa;
152 int sig;
153
154 memset(&sa, 0, sizeof(sa));
155 sa.sa_handler = SIG_DFL;
156
157 for (sig = 1; sig <= _NSIG; ++sig) {
158 if (sigismember(&attrp->__sd, sig)) {
159 if (sigaction(sig, &sa, NULL) != 0)
160 goto error;
161 }
162 }
163 }
164
165 if (flags & POSIX_SPAWN_SETSCHEDULER) {
166 if (sched_setscheduler(0, attrp->__policy, &attrp->__sp) == -1)
167 goto error;
168 } else if (flags & POSIX_SPAWN_SETSCHEDPARAM) {
169 if (sched_setparam(0, &attrp->__sp) == -1)
170 goto error;
171 }
172
173 if (flags & POSIX_SPAWN_SETPGROUP) {
174 if (setpgid(0, attrp->__pgrp) != 0)
175 goto error;
176 }
177
178 if (flags & POSIX_SPAWN_RESETIDS) {
179 if (seteuid(getuid()) || setegid(getgid()))
180 goto error;
181 }
182
183 if (fa && execute_file_actions(fa))
184 goto error;
185
186 if (!path || strchr(file, '/')) {
187 execve(file, argv, envp);
188 goto error;
189 }
190
191
192 char *name;
193 {
194 size_t filelen = strlen(file) + 1;
195 size_t pathlen = strlen(path) + 1;
196 name = alloca(pathlen + filelen);
197
198 /* Copy the file name at the top. */
199 name = (char *) memcpy(name + pathlen, file, filelen);
200
201 /* And add the slash. */
202 *--name = '/';
203 }
204
205 char *p;
206 do {
207 char *startp;
208 p = strchrnul(path, ':');
209
210 /* Two adjacent colons, or a colon at the beginning or the end
211 of `PATH' means to search the current directory. */
212 if (p == path)
213 startp = name + 1;
214 else
215 startp = (char *) memcpy(name - (p - path), path, p - path);
216
217 execve(startp, argv, envp);
218
219 switch (errno) {
220 case EACCES:
221 case ENOENT:
222 case ESTALE:
223 case ENOTDIR:
224 /* Those errors indicate the file is missing or not
225 executable by us, in which case we want to just try
226 the next path directory. */
227 break;
228 default:
229 /* Some other error means we found an executable file,
230 but something went wrong executing it; return the
231 error to our caller. */
232 goto error;
233 }
234
235 path = p;
236 } while (*p++ != '\0');
237
238 error:
239 _exit(SPAWN_ERROR);
240 }
241
242 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
243 Before running the process perform the actions described in FILE-ACTIONS. */
posix_spawn(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[])244 int posix_spawn (pid_t *pid, const char *path,
245 const posix_spawn_file_actions_t *fa,
246 const posix_spawnattr_t *attrp, char *const argv[],
247 char *const envp[])
248 {
249 return __spawni(pid, path, fa, attrp, argv, envp, NULL);
250 }
251
252 /* Spawn a new process executing FILE with the attributes describes in *ATTRP.
253 Before running the process perform the actions described in FILE-ACTIONS. */
254 int
posix_spawnp(pid_t * pid,const char * file,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[])255 posix_spawnp(pid_t *pid, const char *file,
256 const posix_spawn_file_actions_t *fa,
257 const posix_spawnattr_t *attrp, char *const argv[],
258 char *const envp[])
259 {
260 const char *path = getenv("PATH");
261
262 if (!path)
263 path = ":/bin:/usr/bin";
264
265 return __spawni(pid, file, fa, attrp, argv, envp, path);
266 }
267