1 /* Subprocesses with pipes. 2 3 Copyright (C) 2005-2012 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 (at) 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 } 294