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 57static int old_stdin; 58static int old_stdout; 59static char **arguments; 60static 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 70void 71init_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 103pid_t 104create_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. */ 160static int child_interrupted; 161 162static void 163signal_catcher(int signo) 164{ 165 child_interrupted++; 166} 167 168 169void 170end_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 268void 269reap_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