Mercurial > hg > octave-kai > gnulib-hg
annotate lib/execute.c @ 5848:a48fb0e98c8c
*** empty log message ***
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Sat, 14 May 2005 06:03:57 +0000 (2005-05-14) |
parents | 19f7545b3c2b |
children | 1b0092424a44 |
rev | line source |
---|---|
4936 | 1 /* Creation of autonomous subprocesses. |
5580
19f7545b3c2b
Declare environ; not all systems declare it.
Bruno Haible <bruno@clisp.org>
parents:
4936
diff
changeset
|
2 Copyright (C) 2001-2004 Free Software Foundation, Inc. |
4936 | 3 Written by Bruno Haible <haible@clisp.cons.org>, 2001. |
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 2, or (at your option) | |
8 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, write to the Free Software Foundation, | |
5848
a48fb0e98c8c
*** empty log message ***
Paul Eggert <eggert@cs.ucla.edu>
parents:
5580
diff
changeset
|
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ |
4936 | 18 |
19 | |
20 #ifdef HAVE_CONFIG_H | |
21 # include "config.h" | |
22 #endif | |
23 | |
24 /* Specification. */ | |
25 #include "execute.h" | |
26 | |
27 #include <errno.h> | |
28 #include <fcntl.h> | |
29 #include <stdbool.h> | |
30 #include <stdlib.h> | |
31 #include <signal.h> | |
32 | |
33 #ifdef HAVE_UNISTD_H | |
34 # include <unistd.h> | |
35 #endif | |
36 | |
37 #include "error.h" | |
38 #include "exit.h" | |
39 #include "fatal-signal.h" | |
40 #include "wait-process.h" | |
41 #include "gettext.h" | |
42 | |
43 #define _(str) gettext (str) | |
44 | |
45 #if defined _MSC_VER || defined __MINGW32__ | |
46 | |
47 /* Native Woe32 API. */ | |
48 # include <process.h> | |
49 # include "w32spawn.h" | |
50 | |
51 #else | |
52 | |
53 /* Unix API. */ | |
54 # ifdef HAVE_POSIX_SPAWN | |
55 # include <spawn.h> | |
56 # else | |
57 # ifdef HAVE_VFORK_H | |
58 # include <vfork.h> | |
59 # endif | |
60 # endif | |
61 | |
62 #endif | |
63 | |
5580
19f7545b3c2b
Declare environ; not all systems declare it.
Bruno Haible <bruno@clisp.org>
parents:
4936
diff
changeset
|
64 #ifndef HAVE_ENVIRON_DECL |
19f7545b3c2b
Declare environ; not all systems declare it.
Bruno Haible <bruno@clisp.org>
parents:
4936
diff
changeset
|
65 extern char **environ; |
19f7545b3c2b
Declare environ; not all systems declare it.
Bruno Haible <bruno@clisp.org>
parents:
4936
diff
changeset
|
66 #endif |
19f7545b3c2b
Declare environ; not all systems declare it.
Bruno Haible <bruno@clisp.org>
parents:
4936
diff
changeset
|
67 |
4936 | 68 #ifndef STDIN_FILENO |
69 # define STDIN_FILENO 0 | |
70 #endif | |
71 #ifndef STDOUT_FILENO | |
72 # define STDOUT_FILENO 1 | |
73 #endif | |
74 #ifndef STDERR_FILENO | |
75 # define STDERR_FILENO 2 | |
76 #endif | |
77 | |
78 | |
79 #ifdef EINTR | |
80 | |
81 /* EINTR handling for close(), open(). | |
82 These functions can return -1/EINTR even though we don't have any | |
83 signal handlers set up, namely when we get interrupted via SIGSTOP. */ | |
84 | |
85 static inline int | |
86 nonintr_close (int fd) | |
87 { | |
88 int retval; | |
89 | |
90 do | |
91 retval = close (fd); | |
92 while (retval < 0 && errno == EINTR); | |
93 | |
94 return retval; | |
95 } | |
96 #define close nonintr_close | |
97 | |
98 static inline int | |
99 nonintr_open (const char *pathname, int oflag, mode_t mode) | |
100 { | |
101 int retval; | |
102 | |
103 do | |
104 retval = open (pathname, oflag, mode); | |
105 while (retval < 0 && errno == EINTR); | |
106 | |
107 return retval; | |
108 } | |
109 #undef open /* avoid warning on VMS */ | |
110 #define open nonintr_open | |
111 | |
112 #endif | |
113 | |
114 | |
115 /* Execute a command, optionally redirecting any of the three standard file | |
116 descriptors to /dev/null. Return its exit code. | |
117 If it didn't terminate correctly, exit if exit_on_error is true, otherwise | |
118 return 127. | |
119 If slave_process is true, the child process will be terminated when its | |
120 creator receives a catchable fatal signal. */ | |
121 int | |
122 execute (const char *progname, | |
123 const char *prog_path, char **prog_argv, | |
124 bool ignore_sigpipe, | |
125 bool null_stdin, bool null_stdout, bool null_stderr, | |
126 bool slave_process, bool exit_on_error) | |
127 { | |
128 #if defined _MSC_VER || defined __MINGW32__ | |
129 | |
130 /* Native Woe32 API. */ | |
131 int orig_stdin; | |
132 int orig_stdout; | |
133 int orig_stderr; | |
134 int exitcode; | |
135 int nullinfd; | |
136 int nulloutfd; | |
137 | |
138 prog_argv = prepare_spawn (prog_argv); | |
139 | |
140 /* Save standard file handles of parent process. */ | |
141 if (null_stdin) | |
142 orig_stdin = dup_noinherit (STDIN_FILENO); | |
143 if (null_stdout) | |
144 orig_stdout = dup_noinherit (STDOUT_FILENO); | |
145 if (null_stderr) | |
146 orig_stderr = dup_noinherit (STDERR_FILENO); | |
147 exitcode = -1; | |
148 | |
149 /* Create standard file handles of child process. */ | |
150 nullinfd = -1; | |
151 nulloutfd = -1; | |
152 if ((!null_stdin | |
153 || ((nullinfd = open ("NUL", O_RDONLY, 0)) >= 0 | |
154 && (nullinfd == STDIN_FILENO | |
155 || (dup2 (nullinfd, STDIN_FILENO) >= 0 | |
156 && close (nullinfd) >= 0)))) | |
157 && (!(null_stdout || null_stderr) | |
158 || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0 | |
159 && (!null_stdout | |
160 || nulloutfd == STDOUT_FILENO | |
161 || dup2 (nulloutfd, STDOUT_FILENO) >= 0) | |
162 && (!null_stderr | |
163 || nulloutfd == STDERR_FILENO | |
164 || dup2 (nulloutfd, STDERR_FILENO) >= 0) | |
165 && ((null_stdout && nulloutfd == STDOUT_FILENO) | |
166 || (null_stderr && nulloutfd == STDERR_FILENO) | |
167 || close (nulloutfd) >= 0)))) | |
168 exitcode = spawnvp (P_WAIT, prog_path, prog_argv); | |
169 if (nulloutfd >= 0) | |
170 close (nulloutfd); | |
171 if (nullinfd >= 0) | |
172 close (nullinfd); | |
173 | |
174 /* Restore standard file handles of parent process. */ | |
175 if (null_stderr) | |
176 dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr); | |
177 if (null_stdout) | |
178 dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout); | |
179 if (null_stdin) | |
180 dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin); | |
181 | |
182 if (exitcode == -1) | |
183 { | |
184 if (exit_on_error || !null_stderr) | |
185 error (exit_on_error ? EXIT_FAILURE : 0, errno, | |
186 _("%s subprocess failed"), progname); | |
187 return 127; | |
188 } | |
189 | |
190 return exitcode; | |
191 | |
192 #else | |
193 | |
194 /* Unix API. */ | |
195 /* Note about 127: Some errors during posix_spawnp() cause the function | |
196 posix_spawnp() to return an error code; some other errors cause the | |
197 subprocess to exit with return code 127. It is implementation | |
198 dependent which error is reported which way. We treat both cases as | |
199 equivalent. */ | |
200 #if HAVE_POSIX_SPAWN | |
201 sigset_t blocked_signals; | |
202 posix_spawn_file_actions_t actions; | |
203 bool actions_allocated; | |
204 posix_spawnattr_t attrs; | |
205 bool attrs_allocated; | |
206 int err; | |
207 pid_t child; | |
208 #else | |
209 int child; | |
210 #endif | |
211 | |
212 #if HAVE_POSIX_SPAWN | |
213 if (slave_process) | |
214 { | |
215 sigprocmask (SIG_SETMASK, NULL, &blocked_signals); | |
216 block_fatal_signals (); | |
217 } | |
218 actions_allocated = false; | |
219 attrs_allocated = false; | |
220 if ((err = posix_spawn_file_actions_init (&actions)) != 0 | |
221 || (actions_allocated = true, | |
222 (null_stdin | |
223 && (err = posix_spawn_file_actions_addopen (&actions, | |
224 STDIN_FILENO, | |
225 "/dev/null", O_RDONLY, | |
226 0)) | |
227 != 0) | |
228 || (null_stdout | |
229 && (err = posix_spawn_file_actions_addopen (&actions, | |
230 STDOUT_FILENO, | |
231 "/dev/null", O_RDWR, | |
232 0)) | |
233 != 0) | |
234 || (null_stderr | |
235 && (err = posix_spawn_file_actions_addopen (&actions, | |
236 STDERR_FILENO, | |
237 "/dev/null", O_RDWR, | |
238 0)) | |
239 != 0) | |
240 || (slave_process | |
241 && ((err = posix_spawnattr_init (&attrs)) != 0 | |
242 || (attrs_allocated = true, | |
243 (err = posix_spawnattr_setsigmask (&attrs, | |
244 &blocked_signals)) | |
245 != 0 | |
246 || (err = posix_spawnattr_setflags (&attrs, | |
247 POSIX_SPAWN_SETSIGMASK)) | |
248 != 0))) | |
249 || (err = posix_spawnp (&child, prog_path, &actions, | |
250 attrs_allocated ? &attrs : NULL, prog_argv, | |
251 environ)) | |
252 != 0)) | |
253 { | |
254 if (actions_allocated) | |
255 posix_spawn_file_actions_destroy (&actions); | |
256 if (attrs_allocated) | |
257 posix_spawnattr_destroy (&attrs); | |
258 if (slave_process) | |
259 unblock_fatal_signals (); | |
260 if (exit_on_error || !null_stderr) | |
261 error (exit_on_error ? EXIT_FAILURE : 0, err, | |
262 _("%s subprocess failed"), progname); | |
263 return 127; | |
264 } | |
265 posix_spawn_file_actions_destroy (&actions); | |
266 if (attrs_allocated) | |
267 posix_spawnattr_destroy (&attrs); | |
268 #else | |
269 if (slave_process) | |
270 block_fatal_signals (); | |
271 /* Use vfork() instead of fork() for efficiency. */ | |
272 if ((child = vfork ()) == 0) | |
273 { | |
274 /* Child process code. */ | |
275 int nullinfd; | |
276 int nulloutfd; | |
277 | |
278 if ((!null_stdin | |
279 || ((nullinfd = open ("/dev/null", O_RDONLY, 0)) >= 0 | |
280 && (nullinfd == STDIN_FILENO | |
281 || (dup2 (nullinfd, STDIN_FILENO) >= 0 | |
282 && close (nullinfd) >= 0)))) | |
283 && (!(null_stdout || null_stderr) | |
284 || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0 | |
285 && (!null_stdout | |
286 || nulloutfd == STDOUT_FILENO | |
287 || dup2 (nulloutfd, STDOUT_FILENO) >= 0) | |
288 && (!null_stderr | |
289 || nulloutfd == STDERR_FILENO | |
290 || dup2 (nulloutfd, STDERR_FILENO) >= 0) | |
291 && ((null_stdout && nulloutfd == STDOUT_FILENO) | |
292 || (null_stderr && nulloutfd == STDERR_FILENO) | |
293 || close (nulloutfd) >= 0))) | |
294 && (!slave_process || (unblock_fatal_signals (), true))) | |
295 execvp (prog_path, prog_argv); | |
296 _exit (127); | |
297 } | |
298 if (child == -1) | |
299 { | |
300 if (slave_process) | |
301 unblock_fatal_signals (); | |
302 if (exit_on_error || !null_stderr) | |
303 error (exit_on_error ? EXIT_FAILURE : 0, errno, | |
304 _("%s subprocess failed"), progname); | |
305 return 127; | |
306 } | |
307 #endif | |
308 if (slave_process) | |
309 { | |
310 register_slave_subprocess (child); | |
311 unblock_fatal_signals (); | |
312 } | |
313 | |
314 return wait_subprocess (child, progname, ignore_sigpipe, null_stderr, | |
315 slave_process, exit_on_error); | |
316 | |
317 #endif | |
318 } |