]>
Commit | Line | Data |
---|---|---|
6bebb373 JMG |
1 | /* Subprocesses with pipes. |
2 | ||
401b73af JD |
3 | Copyright (C) 2005, 2006, 2007, 2008-2009 Free Software Foundation, |
4 | Inc. | |
6bebb373 | 5 | |
f16b0819 | 6 | This program is free software: you can redistribute it and/or modify |
6bebb373 | 7 | it under the terms of the GNU General Public License as published by |
f16b0819 PE |
8 | the Free Software Foundation, either version 3 of the License, or |
9 | (at your option) any later version. | |
6bebb373 JMG |
10 | |
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
f16b0819 | 17 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
6bebb373 JMG |
18 | |
19 | /* Written by Juan Manuel Guerrero <juan.guerrero@gmx.de>. */ | |
20 | ||
21 | ||
231ed89a | 22 | #include <config.h> |
6bebb373 JMG |
23 | |
24 | #include "subpipe.h" | |
25 | ||
26 | #include <errno.h> | |
27 | #include <fcntl.h> | |
28 | #include <sys/stat.h> | |
29 | #include <process.h> | |
30 | #include <signal.h> | |
31 | #include <stdio.h> | |
32 | #include <stdlib.h> | |
33 | #include <string.h> | |
34 | #include <unistd.h> | |
35 | #include "xalloc.h" | |
36 | ||
37 | ||
38 | #ifndef STDIN_FILENO | |
39 | # define STDIN_FILENO 0 | |
40 | #endif | |
41 | #ifndef STDOUT_FILENO | |
42 | # define STDOUT_FILENO 1 | |
43 | #endif | |
44 | ||
45 | ||
46 | #include "error.h" | |
47 | ||
48 | #include "gettext.h" | |
49 | #define _(Msgid) gettext (Msgid) | |
50 | ||
51 | ||
52 | /* Initialize this module. */ | |
53 | ||
54 | ||
55 | static int old_stdin; | |
56 | static int old_stdout; | |
57 | static char **arguments; | |
58 | static char tmp_file_name[2][L_tmpnam]; | |
59 | ||
60 | #define remove_tmp_file(fd, name) \ | |
61 | do { \ | |
62 | close ((fd)); \ | |
63 | if (unlink ((name))) \ | |
64 | error (EXIT_FAILURE, 0, _("removing of `%s' failed"), (name)); \ | |
65 | } while (0) | |
66 | ||
67 | ||
68 | void | |
69 | init_subpipe(void) | |
70 | { | |
988a9d67 | 71 | char *tmpdir; |
6bebb373 JMG |
72 | int fd; |
73 | ||
988a9d67 JMG |
74 | tmpdir = getenv("TMPDIR"); |
75 | if (tmpdir == NULL) | |
76 | tmpdir = getenv("TMP"); | |
77 | if (tmpdir == NULL) | |
78 | tmpdir = getenv("TEMP"); | |
79 | if (access(tmpdir, D_OK)) | |
80 | tmpdir = "."; | |
81 | ||
82 | strcpy(tmp_file_name[0], tmpdir); | |
83 | strcat(tmp_file_name[0], "/bnXXXXXX"); | |
6bebb373 JMG |
84 | fd = mkstemp(tmp_file_name[0]); |
85 | if (fd < 0) | |
86 | error(EXIT_FAILURE, 0, _("creation of a temporary file failed")); | |
87 | close (fd); | |
02650b7f | 88 | |
988a9d67 JMG |
89 | strcpy(tmp_file_name[1], tmpdir); |
90 | strcat(tmp_file_name[1], "/bnXXXXXX"); | |
6bebb373 JMG |
91 | fd = mkstemp(tmp_file_name[1]); |
92 | if (fd < 0) | |
93 | error(EXIT_FAILURE, 0, _("creation of a temporary file failed")); | |
94 | close (fd); | |
95 | } | |
96 | ||
97 | ||
98 | /* Create a subprocess that is run as a filter. ARGV is the | |
99 | NULL-terminated argument vector for the subprocess. Store read and | |
100 | write file descriptors for communication with the subprocess into | |
101 | FD[0] and FD[1]: input meant for the process can be written into | |
102 | FD[0], and output from the process can be read from FD[1]. Return | |
103 | the subprocess id. | |
104 | ||
105 | Because DOS has neither fork nor pipe functionality to run the subprocess | |
106 | as a filter, the filter is reproduced using temporary files. First bison's | |
107 | stdout is redirected to a temporary file. After bison has produced all of | |
108 | is output, this file is closed and connected to m4's stdin. All m4's output | |
109 | is redirected from m4's stdout to a second temporary file and reopened as | |
110 | bison's stdin. */ | |
111 | ||
112 | pid_t | |
113 | create_subpipe(char const *const *argv, int fd[2]) | |
114 | { | |
115 | int argc; | |
116 | int from_in_fd; /* pipe from bison to m4. */ | |
117 | pid_t pid; | |
118 | ||
119 | ||
120 | pid = getpid(); | |
121 | ||
122 | /* | |
123 | * Save original stdin and stdout | |
124 | * for later restauration. | |
125 | */ | |
126 | old_stdin = dup(STDIN_FILENO); | |
127 | if (old_stdin < 0) | |
128 | error(EXIT_FAILURE, 0, _("saving stdin failed")); | |
129 | ||
130 | old_stdout = dup(STDOUT_FILENO); | |
131 | if (old_stdout < 0) | |
132 | error(EXIT_FAILURE, 0, _("saving stdout failed")); | |
133 | ||
134 | /* | |
135 | * Save argv for later use. | |
136 | */ | |
137 | for (argc = 0; argv[argc]; argc++) | |
138 | ; | |
139 | argc++; | |
140 | arguments = xmalloc(argc * sizeof(arguments[0])); | |
141 | for (argc = 0; argv[argc]; argc++) | |
142 | { | |
143 | arguments[argc] = xmalloc((strlen(argv[argc]) + 1) * sizeof(arguments[0][0])); | |
144 | strcpy(arguments[argc], argv[argc]); | |
145 | } | |
146 | arguments[argc] = NULL; | |
147 | ||
148 | /* | |
149 | * All bison's output will be gathered in this temporary file | |
150 | * and will be redirected to m4's stdin. | |
151 | */ | |
152 | from_in_fd = open(tmp_file_name[0], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR); | |
153 | if (from_in_fd < 0) | |
154 | error(EXIT_FAILURE, 0, _("opening of tmpfile failed")); | |
155 | if (dup2(from_in_fd, STDOUT_FILENO) < 0) | |
156 | { | |
157 | remove_tmp_file(from_in_fd, tmp_file_name[0]); | |
158 | error(EXIT_FAILURE, 0, _("redirecting bison's stdout to the temporary file failed")); | |
159 | } | |
160 | close(from_in_fd); | |
161 | ||
162 | ||
163 | fd[0] = STDOUT_FILENO; | |
164 | return pid; | |
165 | } | |
166 | ||
167 | ||
168 | /* A signal handler that just records that a signal has happened. */ | |
169 | static int child_interrupted; | |
170 | ||
171 | static void | |
172 | signal_catcher(int signo) | |
173 | { | |
174 | child_interrupted++; | |
175 | } | |
176 | ||
177 | ||
178 | void | |
179 | end_of_output_subpipe(pid_t pid, int fd[2]) | |
180 | { | |
181 | char *program; | |
182 | int from_out_fd = open(tmp_file_name[0], O_RDONLY, S_IRUSR); /* pipe from bison to m4. */ | |
183 | int to_in_fd = open(tmp_file_name[1], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR); /* pipe from m4 to bison. */ | |
184 | int status; | |
185 | void (*previous_handler)(int); | |
186 | ||
187 | ||
188 | program = strrchr(arguments[0], '/'); | |
189 | if (program) | |
190 | program++; | |
191 | else | |
192 | program = arguments[0]; | |
193 | ||
194 | /* | |
195 | * Redirect bison's output to m4's stdin. | |
196 | */ | |
197 | if (from_out_fd < 0) | |
198 | error(EXIT_FAILURE, 0, _("opening of tmpfile failed")); | |
199 | if (dup2(from_out_fd, STDIN_FILENO) < 0) | |
200 | { | |
201 | remove_tmp_file(from_out_fd, tmp_file_name[0]); | |
202 | error(EXIT_FAILURE, 0, _("redirecting m4's stdin from the temporary file failed")); | |
203 | } | |
204 | close(from_out_fd); | |
205 | ||
206 | /* | |
207 | * All m4's output will be gathered in this temporary file | |
208 | * and will be redirected to bison's stdin. | |
209 | */ | |
210 | if (to_in_fd < 0) | |
211 | { | |
212 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
213 | error(EXIT_FAILURE, 0, _("opening of a temporary file failed")); | |
214 | } | |
215 | if (dup2(to_in_fd, STDOUT_FILENO) < 0) | |
216 | { | |
217 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
218 | remove_tmp_file(to_in_fd, tmp_file_name[1]); | |
219 | error(EXIT_FAILURE, 0, _("redirecting m4's stdout to a temporary file failed")); | |
220 | } | |
221 | close(to_in_fd); | |
222 | ||
223 | /* | |
224 | * Run m4. | |
225 | */ | |
226 | child_interrupted = 0; | |
227 | errno = 0; | |
228 | previous_handler = signal(SIGINT, signal_catcher); | |
229 | status = spawnvp(P_WAIT, program, arguments); | |
230 | signal(SIGINT, previous_handler); | |
231 | if (child_interrupted) | |
232 | { | |
233 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
234 | remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]); | |
235 | error(EXIT_FAILURE, 0, _("subsidiary program `%s' interrupted"), program); | |
236 | } | |
237 | if (status) | |
238 | { | |
239 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
240 | remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]); | |
241 | error(EXIT_FAILURE, 0, _(errno == ENOENT | |
02650b7f PE |
242 | ? "subsidiary program `%s' not found" |
243 | : status < 1 | |
244 | ? "subsidiary program `%s' failed" | |
245 | : "subsidiary program `%s' failed (status=%i, errno=%i)"), program, status, errno); | |
6bebb373 JMG |
246 | } |
247 | ||
248 | ||
249 | /* | |
250 | * Redirect m4's output to bison's stdin. | |
251 | */ | |
252 | if (dup2(old_stdout, STDOUT_FILENO) < 0) | |
253 | error(EXIT_FAILURE, 0, "restore of bison's stdout failed"); | |
254 | close(old_stdout); | |
255 | to_in_fd = open(tmp_file_name[1], O_RDONLY, S_IRUSR); /* pipe from m4 to bison. */ | |
256 | if (to_in_fd < 0) | |
257 | { | |
258 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
259 | error(EXIT_FAILURE, 0, _("opening of tmpfile failed")); | |
260 | } | |
261 | if (dup2(to_in_fd, STDIN_FILENO) < 0) | |
262 | { | |
263 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
264 | remove_tmp_file(to_in_fd, tmp_file_name[1]); | |
265 | error(EXIT_FAILURE, -1, "dup2"); | |
266 | error(EXIT_FAILURE, 0, _("redirecting bison's stdin from the temporary file failed")); | |
267 | } | |
268 | close(to_in_fd); | |
269 | ||
270 | ||
271 | fd[1] = STDIN_FILENO; | |
272 | } | |
273 | ||
274 | ||
275 | /* Free resources, unlink temporary files and restore stdin and stdout. */ | |
276 | ||
277 | void | |
278 | reap_subpipe(pid_t pid, char const *program) | |
279 | { | |
280 | int argc; | |
281 | ||
282 | for (argc = 0; arguments[argc]; argc++) | |
283 | free(arguments[argc]); | |
284 | free(arguments); | |
285 | ||
286 | if (unlink(tmp_file_name[0])) | |
287 | error(EXIT_FAILURE, 0, _("removing of `%s' failed"), tmp_file_name[0]); | |
288 | if (unlink(tmp_file_name[1])) | |
289 | error(EXIT_FAILURE, 0, _("removing of `%s' failed"), tmp_file_name[1]); | |
290 | ||
291 | if (dup2(old_stdin, STDIN_FILENO) < 0) | |
292 | error(EXIT_FAILURE, 0, "restore of bison's stdin failed"); | |
293 | close(old_stdin); | |
294 | } |