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