Mercurial > hg > octave-kai > gnulib-hg
comparison lib/spawni.c @ 10505:bb42f3fdba16
New module 'posix_spawn-internal'.
author | Bruno Haible <bruno@clisp.org> |
---|---|
date | Sun, 28 Sep 2008 13:30:49 +0200 |
parents | |
children | f65529c86553 |
comparison
equal
deleted
inserted
replaced
10504:21e72b03bb51 | 10505:bb42f3fdba16 |
---|---|
1 /* Guts of POSIX spawn interface. Generic POSIX.1 version. | |
2 Copyright (C) 2000-2005, 2006, 2008 Free Software Foundation, Inc. | |
3 This file is part of the GNU C Library. | |
4 | |
5 This program is free software: you can redistribute it and/or modify | |
6 it under the terms of the GNU General Public License as published by | |
7 the Free Software Foundation; either version 3 of the License, or | |
8 (at your option) any later version. | |
9 | |
10 This program is distributed in the hope that it will be useful, | |
11 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 GNU General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU General Public License | |
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | |
18 #include <config.h> | |
19 | |
20 /* Specification. */ | |
21 #include <spawn.h> | |
22 #include "spawn_int.h" | |
23 | |
24 #include <alloca.h> | |
25 #include <errno.h> | |
26 | |
27 #include <fcntl.h> | |
28 #ifndef O_LARGEFILE | |
29 # define O_LARGEFILE 0 | |
30 #endif | |
31 | |
32 #if _LIBC || HAVE_PATHS_H | |
33 # include <paths.h> | |
34 #else | |
35 # define _PATH_BSHELL "/bin/sh" | |
36 #endif | |
37 | |
38 #include <signal.h> | |
39 #include <stdlib.h> | |
40 #include <string.h> | |
41 #include <unistd.h> | |
42 | |
43 #if _LIBC | |
44 # include <not-cancel.h> | |
45 #else | |
46 # define close_not_cancel close | |
47 # define open_not_cancel open | |
48 #endif | |
49 | |
50 #if _LIBC | |
51 # include <local-setxid.h> | |
52 #else | |
53 # if !HAVE_SETEUID | |
54 # define seteuid(id) setresuid (-1, id, -1) | |
55 # endif | |
56 # if !HAVE_SETEGID | |
57 # define setegid(id) setresgid (-1, id, -1) | |
58 # endif | |
59 # define local_seteuid(id) seteuid (id) | |
60 # define local_setegid(id) setegid (id) | |
61 #endif | |
62 | |
63 #if _LIBC | |
64 # define alloca __alloca | |
65 # define execve __execve | |
66 # define dup2 __dup2 | |
67 # define fork __fork | |
68 # define getgid __getgid | |
69 # define getuid __getuid | |
70 # define sched_setparam __sched_setparam | |
71 # define sched_setscheduler __sched_setscheduler | |
72 # define setpgid __setpgid | |
73 # define sigaction __sigaction | |
74 # define sigismember __sigismember | |
75 # define sigprocmask __sigprocmask | |
76 # define strchrnul __strchrnul | |
77 # define vfork __vfork | |
78 #else | |
79 # undef internal_function | |
80 # define internal_function /* empty */ | |
81 #endif | |
82 | |
83 | |
84 /* The Unix standard contains a long explanation of the way to signal | |
85 an error after the fork() was successful. Since no new wait status | |
86 was wanted there is no way to signal an error using one of the | |
87 available methods. The committee chose to signal an error by a | |
88 normal program exit with the exit code 127. */ | |
89 #define SPAWN_ERROR 127 | |
90 | |
91 | |
92 /* The file is accessible but it is not an executable file. Invoke | |
93 the shell to interpret it as a script. */ | |
94 static void | |
95 internal_function | |
96 script_execute (const char *file, char *const argv[], char *const envp[]) | |
97 { | |
98 /* Count the arguments. */ | |
99 int argc = 0; | |
100 while (argv[argc++]) | |
101 ; | |
102 | |
103 /* Construct an argument list for the shell. */ | |
104 { | |
105 char **new_argv = (char **) alloca ((argc + 1) * sizeof (char *)); | |
106 new_argv[0] = (char *) _PATH_BSHELL; | |
107 new_argv[1] = (char *) file; | |
108 while (argc > 1) | |
109 { | |
110 new_argv[argc] = argv[argc - 1]; | |
111 --argc; | |
112 } | |
113 | |
114 /* Execute the shell. */ | |
115 execve (new_argv[0], new_argv, envp); | |
116 } | |
117 } | |
118 | |
119 | |
120 /* Spawn a new process executing PATH with the attributes describes in *ATTRP. | |
121 Before running the process perform the actions described in FILE-ACTIONS. */ | |
122 int | |
123 __spawni (pid_t *pid, const char *file, | |
124 const posix_spawn_file_actions_t *file_actions, | |
125 const posix_spawnattr_t *attrp, char *const argv[], | |
126 char *const envp[], int use_path) | |
127 { | |
128 pid_t new_pid; | |
129 char *path, *p, *name; | |
130 size_t len; | |
131 size_t pathlen; | |
132 | |
133 /* Do this once. */ | |
134 short int flags = attrp == NULL ? 0 : attrp->_flags; | |
135 | |
136 /* Generate the new process. */ | |
137 #if HAVE_VFORK | |
138 if ((flags & POSIX_SPAWN_USEVFORK) != 0 | |
139 /* If no major work is done, allow using vfork. Note that we | |
140 might perform the path searching. But this would be done by | |
141 a call to execvp(), too, and such a call must be OK according | |
142 to POSIX. */ | |
143 || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | |
144 | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER | |
145 | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0 | |
146 && file_actions == NULL)) | |
147 new_pid = vfork (); | |
148 else | |
149 #endif | |
150 new_pid = fork (); | |
151 | |
152 if (new_pid != 0) | |
153 { | |
154 if (new_pid < 0) | |
155 return errno; | |
156 | |
157 /* The call was successful. Store the PID if necessary. */ | |
158 if (pid != NULL) | |
159 *pid = new_pid; | |
160 | |
161 return 0; | |
162 } | |
163 | |
164 /* Set signal mask. */ | |
165 if ((flags & POSIX_SPAWN_SETSIGMASK) != 0 | |
166 && sigprocmask (SIG_SETMASK, &attrp->_ss, NULL) != 0) | |
167 _exit (SPAWN_ERROR); | |
168 | |
169 /* Set signal default action. */ | |
170 if ((flags & POSIX_SPAWN_SETSIGDEF) != 0) | |
171 { | |
172 /* We have to iterate over all signals. This could possibly be | |
173 done better but it requires system specific solutions since | |
174 the sigset_t data type can be very different on different | |
175 architectures. */ | |
176 int sig; | |
177 struct sigaction sa; | |
178 | |
179 memset (&sa, '\0', sizeof (sa)); | |
180 sa.sa_handler = SIG_DFL; | |
181 | |
182 for (sig = 1; sig <= NSIG; ++sig) | |
183 if (sigismember (&attrp->_sd, sig) != 0 | |
184 && sigaction (sig, &sa, NULL) != 0) | |
185 _exit (SPAWN_ERROR); | |
186 | |
187 } | |
188 | |
189 #if (_LIBC ? defined _POSIX_PRIORITY_SCHEDULING : HAVE_SCHED_SETPARAM && HAVE_SCHED_SETSCHEDULER) | |
190 /* Set the scheduling algorithm and parameters. */ | |
191 if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) | |
192 == POSIX_SPAWN_SETSCHEDPARAM) | |
193 { | |
194 if (sched_setparam (0, &attrp->_sp) == -1) | |
195 _exit (SPAWN_ERROR); | |
196 } | |
197 else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) | |
198 { | |
199 if (sched_setscheduler (0, attrp->_policy, | |
200 (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0 | |
201 ? &attrp->_sp : NULL) == -1) | |
202 _exit (SPAWN_ERROR); | |
203 } | |
204 #endif | |
205 | |
206 /* Set the process group ID. */ | |
207 if ((flags & POSIX_SPAWN_SETPGROUP) != 0 | |
208 && setpgid (0, attrp->_pgrp) != 0) | |
209 _exit (SPAWN_ERROR); | |
210 | |
211 /* Set the effective user and group IDs. */ | |
212 if ((flags & POSIX_SPAWN_RESETIDS) != 0 | |
213 && (local_seteuid (getuid ()) != 0 | |
214 || local_setegid (getgid ()) != 0)) | |
215 _exit (SPAWN_ERROR); | |
216 | |
217 /* Execute the file actions. */ | |
218 if (file_actions != NULL) | |
219 { | |
220 int cnt; | |
221 | |
222 for (cnt = 0; cnt < file_actions->_used; ++cnt) | |
223 { | |
224 struct __spawn_action *action = &file_actions->_actions[cnt]; | |
225 | |
226 switch (action->tag) | |
227 { | |
228 case spawn_do_close: | |
229 if (close_not_cancel (action->action.close_action.fd) != 0) | |
230 /* Signal the error. */ | |
231 _exit (SPAWN_ERROR); | |
232 break; | |
233 | |
234 case spawn_do_open: | |
235 { | |
236 int new_fd = open_not_cancel (action->action.open_action.path, | |
237 action->action.open_action.oflag | |
238 | O_LARGEFILE, | |
239 action->action.open_action.mode); | |
240 | |
241 if (new_fd == -1) | |
242 /* The `open' call failed. */ | |
243 _exit (SPAWN_ERROR); | |
244 | |
245 /* Make sure the desired file descriptor is used. */ | |
246 if (new_fd != action->action.open_action.fd) | |
247 { | |
248 if (dup2 (new_fd, action->action.open_action.fd) | |
249 != action->action.open_action.fd) | |
250 /* The `dup2' call failed. */ | |
251 _exit (SPAWN_ERROR); | |
252 | |
253 if (close_not_cancel (new_fd) != 0) | |
254 /* The `close' call failed. */ | |
255 _exit (SPAWN_ERROR); | |
256 } | |
257 } | |
258 break; | |
259 | |
260 case spawn_do_dup2: | |
261 if (dup2 (action->action.dup2_action.fd, | |
262 action->action.dup2_action.newfd) | |
263 != action->action.dup2_action.newfd) | |
264 /* The `dup2' call failed. */ | |
265 _exit (SPAWN_ERROR); | |
266 break; | |
267 } | |
268 } | |
269 } | |
270 | |
271 if (! use_path || strchr (file, '/') != NULL) | |
272 { | |
273 /* The FILE parameter is actually a path. */ | |
274 execve (file, argv, envp); | |
275 | |
276 if (errno == ENOEXEC) | |
277 script_execute (file, argv, envp); | |
278 | |
279 /* Oh, oh. `execve' returns. This is bad. */ | |
280 _exit (SPAWN_ERROR); | |
281 } | |
282 | |
283 /* We have to search for FILE on the path. */ | |
284 path = getenv ("PATH"); | |
285 if (path == NULL) | |
286 { | |
287 #if HAVE_CONFSTR | |
288 /* There is no `PATH' in the environment. | |
289 The default search path is the current directory | |
290 followed by the path `confstr' returns for `_CS_PATH'. */ | |
291 len = confstr (_CS_PATH, (char *) NULL, 0); | |
292 path = (char *) alloca (1 + len); | |
293 path[0] = ':'; | |
294 (void) confstr (_CS_PATH, path + 1, len); | |
295 #else | |
296 /* Pretend that the PATH contains only the current directory. */ | |
297 path = ""; | |
298 #endif | |
299 } | |
300 | |
301 len = strlen (file) + 1; | |
302 pathlen = strlen (path); | |
303 name = alloca (pathlen + len + 1); | |
304 /* Copy the file name at the top. */ | |
305 name = (char *) memcpy (name + pathlen + 1, file, len); | |
306 /* And add the slash. */ | |
307 *--name = '/'; | |
308 | |
309 p = path; | |
310 do | |
311 { | |
312 char *startp; | |
313 | |
314 path = p; | |
315 p = strchrnul (path, ':'); | |
316 | |
317 if (p == path) | |
318 /* Two adjacent colons, or a colon at the beginning or the end | |
319 of `PATH' means to search the current directory. */ | |
320 startp = name + 1; | |
321 else | |
322 startp = (char *) memcpy (name - (p - path), path, p - path); | |
323 | |
324 /* Try to execute this name. If it works, execv will not return. */ | |
325 execve (startp, argv, envp); | |
326 | |
327 if (errno == ENOEXEC) | |
328 script_execute (startp, argv, envp); | |
329 | |
330 switch (errno) | |
331 { | |
332 case EACCES: | |
333 case ENOENT: | |
334 case ESTALE: | |
335 case ENOTDIR: | |
336 /* Those errors indicate the file is missing or not executable | |
337 by us, in which case we want to just try the next path | |
338 directory. */ | |
339 break; | |
340 | |
341 default: | |
342 /* Some other error means we found an executable file, but | |
343 something went wrong executing it; return the error to our | |
344 caller. */ | |
345 _exit (SPAWN_ERROR); | |
346 } | |
347 } | |
348 while (*p++ != '\0'); | |
349 | |
350 /* Return with an error. */ | |
351 _exit (SPAWN_ERROR); | |
352 } |