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