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