]>
Commit | Line | Data |
---|---|---|
1 | /* Subprocesses with pipes. | |
2 | ||
3 | Copyright (C) 2005, 2006 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 | int fd; | |
71 | ||
72 | strcpy(tmp_file_name[0], "/dev/env/TMPDIR/bnXXXXXX"); | |
73 | fd = mkstemp(tmp_file_name[0]); | |
74 | if (fd < 0) | |
75 | error(EXIT_FAILURE, 0, _("creation of a temporary file failed")); | |
76 | close (fd); | |
77 | ||
78 | strcpy(tmp_file_name[1], "/dev/env/TMPDIR/bnXXXXXX"); | |
79 | fd = mkstemp(tmp_file_name[1]); | |
80 | if (fd < 0) | |
81 | error(EXIT_FAILURE, 0, _("creation of a temporary file failed")); | |
82 | close (fd); | |
83 | } | |
84 | ||
85 | ||
86 | /* Create a subprocess that is run as a filter. ARGV is the | |
87 | NULL-terminated argument vector for the subprocess. Store read and | |
88 | write file descriptors for communication with the subprocess into | |
89 | FD[0] and FD[1]: input meant for the process can be written into | |
90 | FD[0], and output from the process can be read from FD[1]. Return | |
91 | the subprocess id. | |
92 | ||
93 | Because DOS has neither fork nor pipe functionality to run the subprocess | |
94 | as a filter, the filter is reproduced using temporary files. First bison's | |
95 | stdout is redirected to a temporary file. After bison has produced all of | |
96 | is output, this file is closed and connected to m4's stdin. All m4's output | |
97 | is redirected from m4's stdout to a second temporary file and reopened as | |
98 | bison's stdin. */ | |
99 | ||
100 | pid_t | |
101 | create_subpipe(char const *const *argv, int fd[2]) | |
102 | { | |
103 | int argc; | |
104 | int from_in_fd; /* pipe from bison to m4. */ | |
105 | pid_t pid; | |
106 | ||
107 | ||
108 | pid = getpid(); | |
109 | ||
110 | /* | |
111 | * Save original stdin and stdout | |
112 | * for later restauration. | |
113 | */ | |
114 | old_stdin = dup(STDIN_FILENO); | |
115 | if (old_stdin < 0) | |
116 | error(EXIT_FAILURE, 0, _("saving stdin failed")); | |
117 | ||
118 | old_stdout = dup(STDOUT_FILENO); | |
119 | if (old_stdout < 0) | |
120 | error(EXIT_FAILURE, 0, _("saving stdout failed")); | |
121 | ||
122 | /* | |
123 | * Save argv for later use. | |
124 | */ | |
125 | for (argc = 0; argv[argc]; argc++) | |
126 | ; | |
127 | argc++; | |
128 | arguments = xmalloc(argc * sizeof(arguments[0])); | |
129 | for (argc = 0; argv[argc]; argc++) | |
130 | { | |
131 | arguments[argc] = xmalloc((strlen(argv[argc]) + 1) * sizeof(arguments[0][0])); | |
132 | strcpy(arguments[argc], argv[argc]); | |
133 | } | |
134 | arguments[argc] = NULL; | |
135 | ||
136 | /* | |
137 | * All bison's output will be gathered in this temporary file | |
138 | * and will be redirected to m4's stdin. | |
139 | */ | |
140 | from_in_fd = open(tmp_file_name[0], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR); | |
141 | if (from_in_fd < 0) | |
142 | error(EXIT_FAILURE, 0, _("opening of tmpfile failed")); | |
143 | if (dup2(from_in_fd, STDOUT_FILENO) < 0) | |
144 | { | |
145 | remove_tmp_file(from_in_fd, tmp_file_name[0]); | |
146 | error(EXIT_FAILURE, 0, _("redirecting bison's stdout to the temporary file failed")); | |
147 | } | |
148 | close(from_in_fd); | |
149 | ||
150 | ||
151 | fd[0] = STDOUT_FILENO; | |
152 | return pid; | |
153 | } | |
154 | ||
155 | ||
156 | /* A signal handler that just records that a signal has happened. */ | |
157 | static int child_interrupted; | |
158 | ||
159 | static void | |
160 | signal_catcher(int signo) | |
161 | { | |
162 | child_interrupted++; | |
163 | } | |
164 | ||
165 | ||
166 | void | |
167 | end_of_output_subpipe(pid_t pid, int fd[2]) | |
168 | { | |
169 | char *program; | |
170 | int from_out_fd = open(tmp_file_name[0], O_RDONLY, S_IRUSR); /* pipe from bison to m4. */ | |
171 | int to_in_fd = open(tmp_file_name[1], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR); /* pipe from m4 to bison. */ | |
172 | int status; | |
173 | void (*previous_handler)(int); | |
174 | ||
175 | ||
176 | program = strrchr(arguments[0], '/'); | |
177 | if (program) | |
178 | program++; | |
179 | else | |
180 | program = arguments[0]; | |
181 | ||
182 | /* | |
183 | * Redirect bison's output to m4's stdin. | |
184 | */ | |
185 | if (from_out_fd < 0) | |
186 | error(EXIT_FAILURE, 0, _("opening of tmpfile failed")); | |
187 | if (dup2(from_out_fd, STDIN_FILENO) < 0) | |
188 | { | |
189 | remove_tmp_file(from_out_fd, tmp_file_name[0]); | |
190 | error(EXIT_FAILURE, 0, _("redirecting m4's stdin from the temporary file failed")); | |
191 | } | |
192 | close(from_out_fd); | |
193 | ||
194 | /* | |
195 | * All m4's output will be gathered in this temporary file | |
196 | * and will be redirected to bison's stdin. | |
197 | */ | |
198 | if (to_in_fd < 0) | |
199 | { | |
200 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
201 | error(EXIT_FAILURE, 0, _("opening of a temporary file failed")); | |
202 | } | |
203 | if (dup2(to_in_fd, STDOUT_FILENO) < 0) | |
204 | { | |
205 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
206 | remove_tmp_file(to_in_fd, tmp_file_name[1]); | |
207 | error(EXIT_FAILURE, 0, _("redirecting m4's stdout to a temporary file failed")); | |
208 | } | |
209 | close(to_in_fd); | |
210 | ||
211 | /* | |
212 | * Run m4. | |
213 | */ | |
214 | child_interrupted = 0; | |
215 | errno = 0; | |
216 | previous_handler = signal(SIGINT, signal_catcher); | |
217 | status = spawnvp(P_WAIT, program, arguments); | |
218 | signal(SIGINT, previous_handler); | |
219 | if (child_interrupted) | |
220 | { | |
221 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
222 | remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]); | |
223 | error(EXIT_FAILURE, 0, _("subsidiary program `%s' interrupted"), program); | |
224 | } | |
225 | if (status) | |
226 | { | |
227 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
228 | remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]); | |
229 | error(EXIT_FAILURE, 0, _(errno == ENOENT | |
230 | ? "subsidiary program `%s' not found" | |
231 | : status < 1 | |
232 | ? "subsidiary program `%s' failed" | |
233 | : "subsidiary program `%s' failed (status=%i, errno=%i)"), program, status, errno); | |
234 | } | |
235 | ||
236 | ||
237 | /* | |
238 | * Redirect m4's output to bison's stdin. | |
239 | */ | |
240 | if (dup2(old_stdout, STDOUT_FILENO) < 0) | |
241 | error(EXIT_FAILURE, 0, "restore of bison's stdout failed"); | |
242 | close(old_stdout); | |
243 | to_in_fd = open(tmp_file_name[1], O_RDONLY, S_IRUSR); /* pipe from m4 to bison. */ | |
244 | if (to_in_fd < 0) | |
245 | { | |
246 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
247 | error(EXIT_FAILURE, 0, _("opening of tmpfile failed")); | |
248 | } | |
249 | if (dup2(to_in_fd, STDIN_FILENO) < 0) | |
250 | { | |
251 | remove_tmp_file(STDIN_FILENO, tmp_file_name[0]); | |
252 | remove_tmp_file(to_in_fd, tmp_file_name[1]); | |
253 | error(EXIT_FAILURE, -1, "dup2"); | |
254 | error(EXIT_FAILURE, 0, _("redirecting bison's stdin from the temporary file failed")); | |
255 | } | |
256 | close(to_in_fd); | |
257 | ||
258 | ||
259 | fd[1] = STDIN_FILENO; | |
260 | } | |
261 | ||
262 | ||
263 | /* Free resources, unlink temporary files and restore stdin and stdout. */ | |
264 | ||
265 | void | |
266 | reap_subpipe(pid_t pid, char const *program) | |
267 | { | |
268 | int argc; | |
269 | ||
270 | for (argc = 0; arguments[argc]; argc++) | |
271 | free(arguments[argc]); | |
272 | free(arguments); | |
273 | ||
274 | if (unlink(tmp_file_name[0])) | |
275 | error(EXIT_FAILURE, 0, _("removing of `%s' failed"), tmp_file_name[0]); | |
276 | if (unlink(tmp_file_name[1])) | |
277 | error(EXIT_FAILURE, 0, _("removing of `%s' failed"), tmp_file_name[1]); | |
278 | ||
279 | if (dup2(old_stdin, STDIN_FILENO) < 0) | |
280 | error(EXIT_FAILURE, 0, "restore of bison's stdin failed"); | |
281 | close(old_stdin); | |
282 | } |