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