pipeline.c revision 79543
1/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 2 Free Software Foundation, Inc. 3 Written by James Clark (jjc@jclark.com) 4 5This file is part of groff. 6 7groff is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 2, or (at your option) any later 10version. 11 12groff is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17You should have received a copy of the GNU General Public License along 18with groff; see the file COPYING. If not, write to the Free Software 19Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 20 21/* 22Compile options are: 23 24-DWCOREFLAG=0200 (or whatever) 25-DHAVE_SYS_SIGLIST 26-DSYS_SIGLIST_DECLARED 27-DHAVE_UNISTD_H 28*/ 29 30#include <stdio.h> 31#include <signal.h> 32#include <errno.h> 33#include <sys/types.h> 34#ifdef HAVE_UNISTD_H 35#include <unistd.h> 36#endif 37 38#ifdef HAVE_STRERROR 39#include <string.h> 40#else 41extern char *strerror(); 42#endif 43 44#ifdef _POSIX_VERSION 45 46#include <sys/wait.h> 47 48#define PID_T pid_t 49 50#else /* not _POSIX_VERSION */ 51 52/* traditional Unix */ 53 54#define WIFEXITED(s) (((s) & 0377) == 0) 55#define WIFSTOPPED(s) (((s) & 0377) == 0177) 56#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177)) 57#define WEXITSTATUS(s) (((s) >> 8) & 0377) 58#define WTERMSIG(s) ((s) & 0177) 59#define WSTOPSIG(s) (((s) >> 8) & 0377) 60 61#ifndef WCOREFLAG 62#define WCOREFLAG 0200 63#endif 64 65#define PID_T int 66 67#endif /* not _POSIX_VERSION */ 68 69/* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */ 70#ifndef WCOREFLAG 71#ifdef WCOREFLG 72#define WCOREFLAG WCOREFLG 73#endif /* WCOREFLG */ 74#endif /* not WCOREFLAG */ 75 76#ifndef WCOREDUMP 77#ifdef WCOREFLAG 78#define WCOREDUMP(s) ((s) & WCOREFLAG) 79#else /* not WCOREFLAG */ 80#define WCOREDUMP(s) (0) 81#endif /* WCOREFLAG */ 82#endif /* not WCOREDUMP */ 83 84#include "pipeline.h" 85 86#ifdef __STDC__ 87#define P(parms) parms 88#else 89#define P(parms) () 90#define const /* as nothing */ 91#endif 92 93#define error c_error 94extern void error P((const char *, const char *, const char *, const char *)); 95extern void c_fatal P((const char *, const char *, const char *, const char *)); 96 97static void sys_fatal P((const char *)); 98static const char *xstrsignal P((int)); 99static char *i_to_a P((int)); 100 101/* MSVC can support asynchronous processes, but it's unlikely to have 102 fork(). So, until someone writes an emulation, let them at least 103 have a workable groff by using the good-ole DOS pipe simulation 104 via temporary files... */ 105 106#if defined(__MSDOS__) \ 107 || (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN32__)) 108 109#include <process.h> 110#include <fcntl.h> 111#include <ctype.h> 112#include <string.h> 113 114#include "nonposix.h" 115 116/* A signal handler that just records that a signal has happened. */ 117static int child_interrupted; 118 119static RETSIGTYPE signal_catcher (int signo) 120{ 121 child_interrupted++; 122} 123 124static const char *sh = "sh"; 125static const char *command = "command"; 126 127const char * 128system_shell_name (void) 129{ 130 static const char *shell_name; 131 132 /* We want them to be able to use a Unixy shell if they have it 133 installed. Let spawnlp try to find it, but if it fails, default 134 to COMMAND.COM. */ 135 if (shell_name == NULL) 136 { 137 int sh_found = spawnlp (P_WAIT, sh, sh, "-c", ":", NULL) == 0; 138 139 if (sh_found) 140 shell_name = sh; 141 else 142 shell_name = command; 143 } 144 return shell_name; 145} 146 147const char * 148system_shell_dash_c (void) 149{ 150 if (strcmp (system_shell_name(), sh) == 0) 151 return "-c"; 152 else 153 return "/c"; 154} 155 156int 157is_system_shell (const char *shell) 158{ 159 size_t shlen; 160 size_t ibase = 0, idot, i; 161 162 if (!shell) /* paranoia */ 163 return 0; 164 idot = shlen = strlen(shell); 165 166 for (i = 0; i < shlen; i++) 167 { 168 if (shell[i] == '.') 169 idot = i; 170 else if (shell[i] == '/' || shell[i] == '\\' || shell[i] == ':') 171 { 172 ibase = i + 1; 173 idot = shlen; 174 } 175 } 176 177 /* "sh" and "sh.exe" should compare equal. */ 178 return 179 (strncasecmp (shell + ibase, system_shell_name (), idot - ibase) == 0 180 && (idot == shlen 181 || strcasecmp (shell + idot, ".exe") == 0 182 || strcasecmp (shell + idot, ".com") == 0)); 183} 184 185/* MSDOS doesn't have `fork', so we need to simulate the pipe by 186 running the programs in sequence with redirected standard streams. */ 187 188int run_pipeline (ncommands, commands, no_pipe) 189 int ncommands; 190 char ***commands; 191 int no_pipe; 192{ 193 int save_stdin = dup(0); 194 int save_stdout = dup(1); 195 char *tmpfiles[2]; 196 char tem1[L_tmpnam], tem2[L_tmpnam]; 197 int infile = 0; 198 int outfile = 1; 199 int i, f, ret = 0; 200 201 tmpfiles[0] = tmpnam(tem1); 202 tmpfiles[1] = tmpnam(tem2); 203 204 for (i = 0; i < ncommands; i++) 205 { 206 int exit_status; 207 RETSIGTYPE (*prev_handler)(int); 208 209 if (i) 210 { 211 /* redirect stdin from temp file */ 212 f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666); 213 if (f < 0) 214 sys_fatal("open stdin"); 215 if (dup2(f, 0) < 0) 216 sys_fatal("dup2 stdin"); 217 if (close(f) < 0) 218 sys_fatal("close stdin"); 219 } 220 if ((i < ncommands - 1) && !no_pipe) 221 { 222 /* redirect stdout to temp file */ 223 f = open(tmpfiles[outfile], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666); 224 if (f < 0) 225 sys_fatal("open stdout"); 226 if (dup2(f, 1) < 0) 227 sys_fatal("dup2 stdout"); 228 if (close(f) < 0) 229 sys_fatal("close stdout"); 230 } 231 else if (dup2(save_stdout, 1) < 0) 232 sys_fatal("restore stdout"); 233 234 /* run the program */ 235 child_interrupted = 0; 236 prev_handler = signal(SIGINT, signal_catcher); 237 exit_status = spawnvp(P_WAIT, commands[i][0], commands[i]); 238 signal(SIGINT, prev_handler); 239 if (child_interrupted) 240 { 241 error("%1: Interrupted", commands[i][0], (char *)0, (char *)0); 242 ret |= 2; 243 } 244 else if (exit_status < 0) 245 { 246 error("couldn't exec %1: %2", commands[i][0], 247 strerror(errno), (char *)0); 248 fflush(stderr); /* just in case error() doesn't */ 249 ret |= 4; 250 } 251 if (exit_status != 0) 252 ret |= 1; 253 254 /* There's no sense to continue with the pipe if one of the 255 programs has ended abnormally, is there? */ 256 if (ret != 0) 257 break; 258 259 /* swap temp files: make output of this program be input for the next */ 260 infile = 1 - infile; 261 outfile = 1 - outfile; 262 } 263 264 if (dup2(save_stdin, 0) < 0) 265 sys_fatal("restore stdin"); 266 267 unlink(tmpfiles[0]); 268 unlink(tmpfiles[1]); 269 270 return ret; 271} 272 273#else /* not __MSDOS__, not _WIN32 */ 274 275int run_pipeline(ncommands, commands, no_pipe) 276 int ncommands; 277 char ***commands; 278 int no_pipe; 279{ 280 int i; 281 int last_input = 0; 282 PID_T pids[MAX_COMMANDS]; 283 int ret = 0; 284 int proc_count = ncommands; 285 286 for (i = 0; i < ncommands; i++) { 287 int pdes[2]; 288 PID_T pid; 289 if ((i != ncommands - 1) && !no_pipe) { 290 if (pipe(pdes) < 0) 291 sys_fatal("pipe"); 292 } 293 pid = fork(); 294 if (pid < 0) 295 sys_fatal("fork"); 296 if (pid == 0) { 297 /* child */ 298 if (last_input != 0) { 299 if (close(0) < 0) 300 sys_fatal("close"); 301 if (dup(last_input) < 0) 302 sys_fatal("dup"); 303 if (close(last_input) < 0) 304 sys_fatal("close"); 305 } 306 if ((i != ncommands - 1) && !no_pipe) { 307 if (close(1) < 0) 308 sys_fatal("close"); 309 if (dup(pdes[1]) < 0) 310 sys_fatal("dup"); 311 if (close(pdes[1]) < 0) 312 sys_fatal("close"); 313 if (close(pdes[0])) 314 sys_fatal("close"); 315 } 316 execvp(commands[i][0], commands[i]); 317 error("couldn't exec %1: %2", commands[i][0], 318 strerror(errno), (char *)0); 319 fflush(stderr); /* just in case error() doesn't */ 320 _exit(EXEC_FAILED_EXIT_STATUS); 321 } 322 /* in the parent */ 323 if (last_input != 0) { 324 if (close(last_input) < 0) 325 sys_fatal("close"); 326 } 327 if ((i != ncommands - 1) && !no_pipe) { 328 if (close(pdes[1]) < 0) 329 sys_fatal("close"); 330 last_input = pdes[0]; 331 } 332 pids[i] = pid; 333 } 334 while (proc_count > 0) { 335 int status; 336 PID_T pid = wait(&status); 337 if (pid < 0) 338 sys_fatal("wait"); 339 for (i = 0; i < ncommands; i++) 340 if (pids[i] == pid) { 341 pids[i] = -1; 342 --proc_count; 343 if (WIFSIGNALED(status)) { 344 int sig = WTERMSIG(status); 345#ifdef SIGPIPE 346 if (sig == SIGPIPE) { 347 if (i == ncommands - 1) { 348 349 /* This works around a problem that occurred when using the 350 rerasterize action in gxditview. What seemed to be 351 happening (on SunOS 4.1.1) was that pclose() closed the 352 pipe and waited for groff, gtroff got a SIGPIPE, but 353 gpic blocked writing to gtroff, and so groff blocked 354 waiting for gpic and gxditview blocked waiting for 355 groff. I don't understand why gpic wasn't getting a 356 SIGPIPE. */ 357 int j; 358 for (j = 0; j < ncommands; j++) 359 if (pids[j] > 0) 360 (void)kill(pids[j], SIGPIPE); 361 } 362 } 363 else 364#endif /* SIGPIPE */ 365 { 366 error("%1: %2%3", 367 commands[i][0], 368 xstrsignal(sig), 369 WCOREDUMP(status) ? " (core dumped)" : ""); 370 ret |= 2; 371 } 372 } 373 else if (WIFEXITED(status)) { 374 int exit_status = WEXITSTATUS(status); 375 if (exit_status == EXEC_FAILED_EXIT_STATUS) 376 ret |= 4; 377 else if (exit_status != 0) 378 ret |= 1; 379 } 380 else 381 error("unexpected status %1", 382 i_to_a(status), (char *)0, (char *)0); 383 break; 384 } 385 } 386 return ret; 387} 388 389#endif /* not __MSDOS__, not _WIN32 */ 390 391static void sys_fatal(s) 392 const char *s; 393{ 394 c_fatal("%1: %2", s, strerror(errno), (char *)0); 395} 396 397static char *i_to_a(n) 398 int n; 399{ 400 static char buf[12]; 401 sprintf(buf, "%d", n); 402 return buf; 403} 404 405static const char *xstrsignal(n) 406 int n; 407{ 408 static char buf[sizeof("Signal ") + 1 + sizeof(int)*3]; 409#ifdef NSIG 410#ifdef SYS_SIGLIST_DECLARED 411 if (n >= 0 && n < NSIG && sys_siglist[n] != 0) 412 return sys_siglist[n]; 413#endif /* SYS_SIGLIST_DECLARED */ 414#endif /* NSIG */ 415 sprintf(buf, "Signal %d", n); 416 return buf; 417} 418