1/* $NetBSD$ */ 2 3/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005 4 Free Software Foundation, Inc. 5 Written by James Clark (jjc@jclark.com) 6 7This file is part of groff. 8 9groff is free software; you can redistribute it and/or modify it under 10the terms of the GNU General Public License as published by the Free 11Software Foundation; either version 2, or (at your option) any later 12version. 13 14groff is distributed in the hope that it will be useful, but WITHOUT ANY 15WARRANTY; without even the implied warranty of MERCHANTABILITY or 16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17for more details. 18 19You should have received a copy of the GNU General Public License along 20with groff; see the file COPYING. If not, write to the Free Software 21Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 22 23#ifdef HAVE_CONFIG_H 24#include <config.h> 25#endif 26 27#include <stdio.h> 28#include <signal.h> 29#include <errno.h> 30#include <sys/types.h> 31#ifdef HAVE_UNISTD_H 32#include <unistd.h> 33#endif 34 35#ifdef HAVE_STRERROR 36#include <string.h> 37#else 38extern char *strerror(); 39#endif 40 41#ifdef _POSIX_VERSION 42 43#include <sys/wait.h> 44#define PID_T pid_t 45 46#else /* not _POSIX_VERSION */ 47 48/* traditional Unix */ 49 50#define WIFEXITED(s) (((s) & 0377) == 0) 51#define WIFSTOPPED(s) (((s) & 0377) == 0177) 52#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177)) 53#define WEXITSTATUS(s) (((s) >> 8) & 0377) 54#define WTERMSIG(s) ((s) & 0177) 55#define WSTOPSIG(s) (((s) >> 8) & 0377) 56 57#ifndef WCOREFLAG 58#define WCOREFLAG 0200 59#endif 60 61#define PID_T int 62 63#endif /* not _POSIX_VERSION */ 64 65/* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */ 66#ifndef WCOREFLAG 67#ifdef WCOREFLG 68#define WCOREFLAG WCOREFLG 69#endif /* WCOREFLG */ 70#endif /* not WCOREFLAG */ 71 72#ifndef WCOREDUMP 73#ifdef WCOREFLAG 74#define WCOREDUMP(s) ((s) & WCOREFLAG) 75#else /* not WCOREFLAG */ 76#define WCOREDUMP(s) (0) 77#endif /* WCOREFLAG */ 78#endif /* not WCOREDUMP */ 79 80#include "pipeline.h" 81 82#define error c_error 83 84#ifdef __cplusplus 85extern "C" { 86#endif 87 88extern void error(const char *, const char *, const char *, const char *); 89extern void c_fatal(const char *, const char *, const char *, const char *); 90extern const char *i_to_a(int); /* from libgroff */ 91 92#ifdef __cplusplus 93} 94#endif 95 96static void sys_fatal(const char *); 97static const char *xstrsignal(int); 98 99 100#if defined(__MSDOS__) \ 101 || (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) \ 102 || defined(__EMX__) 103 104#include <process.h> 105#include <fcntl.h> 106#include <string.h> 107#include <stdlib.h> 108 109#include "nonposix.h" 110 111static const char *sh = "sh"; 112static const char *cmd = "cmd"; 113static const char *command = "command"; 114 115extern int strcasecmp(const char *, const char *); 116 117char *sbasename(const char *path) 118{ 119 char *base; 120 const char *p1, *p2; 121 122 p1 = path; 123 if ((p2 = strrchr(p1, '\\')) 124 || (p2 = strrchr(p1, '/')) 125 || (p2 = strrchr(p1, ':'))) 126 p1 = p2 + 1; 127 if ((p2 = strrchr(p1, '.')) 128 && ((strcasecmp(p2, ".exe") == 0) 129 || (strcasecmp(p2, ".com") == 0))) 130 ; 131 else 132 p2 = p1 + strlen(p1); 133 134 base = malloc((size_t)(p2 - p1)); 135 strncpy(base, p1, p2 - p1); 136 *(base + (p2 - p1)) = '\0'; 137 138 return(base); 139} 140 141/* Get the name of the system shell */ 142char *system_shell_name(void) 143{ 144 const char *shell_name; 145 146 /* 147 Use a Unixy shell if it's installed. Use SHELL if set; otherwise, 148 let spawnlp try to find sh; if that fails, use COMSPEC if set; if 149 not, try cmd.exe; if that fails, default to command.com. 150 */ 151 152 if ((shell_name = getenv("SHELL")) != NULL) 153 ; 154 else if (spawnlp(_P_WAIT, sh, sh, "-c", ":", NULL) == 0) 155 shell_name = sh; 156 else if ((shell_name = getenv("COMSPEC")) != NULL) 157 ; 158 else if (spawnlp(_P_WAIT, cmd, cmd, "/c", ";", NULL) == 0) 159 shell_name = cmd; 160 else 161 shell_name = command; 162 163 return sbasename(shell_name); 164} 165 166const char *system_shell_dash_c(void) 167{ 168 char *shell_name; 169 const char *dash_c; 170 171 shell_name = system_shell_name(); 172 173 /* Assume that if the shell name ends in "sh", it's Unixy */ 174 if (strcasecmp(shell_name + strlen(shell_name) - strlen("sh"), "sh") == 0) 175 dash_c = "-c"; 176 else 177 dash_c = "/c"; 178 179 free(shell_name); 180 return dash_c; 181} 182 183int is_system_shell(const char *prog) 184{ 185 int result; 186 char *this_prog, *system_shell; 187 188 if (!prog) /* paranoia */ 189 return 0; 190 191 this_prog = sbasename(prog); 192 system_shell = system_shell_name(); 193 194 result = strcasecmp(this_prog, system_shell) == 0; 195 196 free(this_prog); 197 free(system_shell); 198 199 return result; 200} 201 202#ifdef _WIN32 203 204/* 205 Windows 32 doesn't have fork(), so we need to start asynchronous child 206 processes with spawn() rather than exec(). If there is more than one 207 command, i.e., a pipeline, the parent must set up each child's I/O 208 redirection prior to the spawn. The original stdout must be restored 209 before spawning the last process in the pipeline, and the original 210 stdin must be restored in the parent after spawning the last process 211 and before waiting for any of the children. 212*/ 213 214int run_pipeline(int ncommands, char ***commands, int no_pipe) 215{ 216 int i; 217 int last_input = 0; /* pacify some compilers */ 218 int save_stdin = 0; 219 int save_stdout = 0; 220 int ret = 0; 221 char err_str[BUFSIZ]; 222 PID_T pids[MAX_COMMANDS]; 223 224 for (i = 0; i < ncommands; i++) { 225 int pdes[2]; 226 PID_T pid; 227 228 /* If no_pipe is set, just run the commands in sequence 229 to show the version numbers */ 230 if (ncommands > 1 && !no_pipe) { 231 /* last command doesn't need a new pipe */ 232 if (i < ncommands - 1) { 233 if (pipe(pdes) < 0) { 234 sprintf(err_str, "%s: pipe", commands[i][0]); 235 sys_fatal(err_str); 236 } 237 } 238 /* 1st command; writer */ 239 if (i == 0) { 240 /* save stdin */ 241 if ((save_stdin = dup(STDIN_FILENO)) < 0) 242 sys_fatal("dup stdin"); 243 /* save stdout */ 244 if ((save_stdout = dup(STDOUT_FILENO)) < 0) 245 sys_fatal("dup stdout"); 246 247 /* connect stdout to write end of pipe */ 248 if (dup2(pdes[1], STDOUT_FILENO) < 0) { 249 sprintf(err_str, "%s: dup2(stdout)", commands[i][0]); 250 sys_fatal(err_str); 251 } 252 if (close(pdes[1]) < 0) { 253 sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]); 254 sys_fatal(err_str); 255 } 256 /* 257 Save the read end of the pipe so that it can be connected to 258 stdin of the next program in the pipeline during the next 259 pass through the loop. 260 */ 261 last_input = pdes[0]; 262 } 263 /* reader and writer */ 264 else if (i < ncommands - 1) { 265 /* connect stdin to read end of last pipe */ 266 if (dup2(last_input, STDIN_FILENO) < 0) { 267 sprintf(err_str, " %s: dup2(stdin)", commands[i][0]); 268 sys_fatal(err_str); 269 } 270 if (close(last_input) < 0) { 271 sprintf(err_str, "%s: close(last_input)", commands[i][0]); 272 sys_fatal(err_str); 273 } 274 /* connect stdout to write end of new pipe */ 275 if (dup2(pdes[1], STDOUT_FILENO) < 0) { 276 sprintf(err_str, "%s: dup2(stdout)", commands[i][0]); 277 sys_fatal(err_str); 278 } 279 if (close(pdes[1]) < 0) { 280 sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]); 281 sys_fatal(err_str); 282 } 283 last_input = pdes[0]; 284 } 285 /* last command; reader */ 286 else { 287 /* connect stdin to read end of last pipe */ 288 if (dup2(last_input, STDIN_FILENO) < 0) { 289 sprintf(err_str, "%s: dup2(stdin)", commands[i][0]); 290 sys_fatal(err_str); 291 } 292 if (close(last_input) < 0) { 293 sprintf(err_str, "%s: close(last_input)", commands[i][0]); 294 sys_fatal(err_str); 295 } 296 /* restore original stdout */ 297 if (dup2(save_stdout, STDOUT_FILENO) < 0) { 298 sprintf(err_str, "%s: dup2(save_stdout))", commands[i][0]); 299 sys_fatal(err_str); 300 } 301 /* close stdout copy */ 302 if (close(save_stdout) < 0) { 303 sprintf(err_str, "%s: close(save_stdout)", commands[i][0]); 304 sys_fatal(err_str); 305 } 306 } 307 } 308 if ((pid = spawnvp(_P_NOWAIT, commands[i][0], commands[i])) < 0) { 309 error("couldn't exec %1: %2", 310 commands[i][0], strerror(errno), (char *)0); 311 fflush(stderr); /* just in case error() doesn't */ 312 _exit(EXEC_FAILED_EXIT_STATUS); 313 } 314 pids[i] = pid; 315 } 316 317 if (ncommands > 1 && !no_pipe) { 318 /* restore original stdin if it was redirected */ 319 if (dup2(save_stdin, STDIN_FILENO) < 0) { 320 sprintf(err_str, "dup2(save_stdin))"); 321 sys_fatal(err_str); 322 } 323 /* close stdin copy */ 324 if (close(save_stdin) < 0) { 325 sprintf(err_str, "close(save_stdin)"); 326 sys_fatal(err_str); 327 } 328 } 329 330 for (i = 0; i < ncommands; i++) { 331 int status; 332 PID_T pid; 333 334 pid = pids[i]; 335 if ((pid = WAIT(&status, pid, _WAIT_CHILD)) < 0) { 336 sprintf(err_str, "%s: wait", commands[i][0]); 337 sys_fatal(err_str); 338 } 339 else if (status != 0) 340 ret |= 1; 341 } 342 return ret; 343} 344 345#else /* not _WIN32 */ 346 347/* MSDOS doesn't have `fork', so we need to simulate the pipe by running 348 the programs in sequence with standard streams redirected to and 349 from temporary files. 350*/ 351 352 353/* A signal handler that just records that a signal has happened. */ 354static int child_interrupted; 355 356static RETSIGTYPE signal_catcher(int signo) 357{ 358 child_interrupted++; 359} 360 361int run_pipeline(int ncommands, char ***commands, int no_pipe) 362{ 363 int save_stdin = dup(0); 364 int save_stdout = dup(1); 365 char *tmpfiles[2]; 366 int infile = 0; 367 int outfile = 1; 368 int i, f, ret = 0; 369 370 /* Choose names for a pair of temporary files to implement the pipeline. 371 Microsoft's `tempnam' uses the directory specified by `getenv("TMP")' 372 if it exists; in case it doesn't, try the GROFF alternatives, or 373 `getenv("TEMP")' as last resort -- at least one of these had better 374 be set, since Microsoft's default has a high probability of failure. */ 375 char *tmpdir; 376 if ((tmpdir = getenv("GROFF_TMPDIR")) == NULL 377 && (tmpdir = getenv("TMPDIR")) == NULL) 378 tmpdir = getenv("TEMP"); 379 380 /* Don't use `tmpnam' here: Microsoft's implementation yields unusable 381 file names if current directory is on network share with read-only 382 root. */ 383 tmpfiles[0] = tempnam(tmpdir, NULL); 384 tmpfiles[1] = tempnam(tmpdir, NULL); 385 386 for (i = 0; i < ncommands; i++) { 387 int exit_status; 388 RETSIGTYPE (*prev_handler)(int); 389 390 if (i && !no_pipe) { 391 /* redirect stdin from temp file */ 392 f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666); 393 if (f < 0) 394 sys_fatal("open stdin"); 395 if (dup2(f, 0) < 0) 396 sys_fatal("dup2 stdin"); 397 if (close(f) < 0) 398 sys_fatal("close stdin"); 399 } 400 if ((i < ncommands - 1) && !no_pipe) { 401 /* redirect stdout to temp file */ 402 f = open(tmpfiles[outfile], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666); 403 if (f < 0) 404 sys_fatal("open stdout"); 405 if (dup2(f, 1) < 0) 406 sys_fatal("dup2 stdout"); 407 if (close(f) < 0) 408 sys_fatal("close stdout"); 409 } 410 else if (dup2(save_stdout, 1) < 0) 411 sys_fatal("restore stdout"); 412 413 /* run the program */ 414 child_interrupted = 0; 415 prev_handler = signal(SIGINT, signal_catcher); 416 exit_status = spawnvp(P_WAIT, commands[i][0], commands[i]); 417 signal(SIGINT, prev_handler); 418 if (child_interrupted) { 419 error("%1: Interrupted", commands[i][0], (char *)0, (char *)0); 420 ret |= 2; 421 } 422 else if (exit_status < 0) { 423 error("couldn't exec %1: %2", 424 commands[i][0], strerror(errno), (char *)0); 425 fflush(stderr); /* just in case error() doesn't */ 426 ret |= 4; 427 } 428 if (exit_status != 0) 429 ret |= 1; 430 /* There's no sense to continue with the pipe if one of the 431 programs has ended abnormally, is there? */ 432 if (ret != 0) 433 break; 434 /* swap temp files: make output of this program be input for the next */ 435 infile = 1 - infile; 436 outfile = 1 - outfile; 437 } 438 if (dup2(save_stdin, 0) < 0) 439 sys_fatal("restore stdin"); 440 unlink(tmpfiles[0]); 441 unlink(tmpfiles[1]); 442 return ret; 443} 444 445#endif /* not _WIN32 */ 446 447#else /* not __MSDOS__, not _WIN32 */ 448 449int run_pipeline(int ncommands, char ***commands, int no_pipe) 450{ 451 int i; 452 int last_input = 0; 453 PID_T pids[MAX_COMMANDS]; 454 int ret = 0; 455 int proc_count = ncommands; 456 457 for (i = 0; i < ncommands; i++) { 458 int pdes[2]; 459 PID_T pid; 460 461 if ((i != ncommands - 1) && !no_pipe) { 462 if (pipe(pdes) < 0) 463 sys_fatal("pipe"); 464 } 465 pid = fork(); 466 if (pid < 0) 467 sys_fatal("fork"); 468 if (pid == 0) { 469 /* child */ 470 if (last_input != 0) { 471 if (close(0) < 0) 472 sys_fatal("close"); 473 if (dup(last_input) < 0) 474 sys_fatal("dup"); 475 if (close(last_input) < 0) 476 sys_fatal("close"); 477 } 478 if ((i != ncommands - 1) && !no_pipe) { 479 if (close(1) < 0) 480 sys_fatal("close"); 481 if (dup(pdes[1]) < 0) 482 sys_fatal("dup"); 483 if (close(pdes[1]) < 0) 484 sys_fatal("close"); 485 if (close(pdes[0])) 486 sys_fatal("close"); 487 } 488 execvp(commands[i][0], commands[i]); 489 error("couldn't exec %1: %2", 490 commands[i][0], strerror(errno), (char *)0); 491 fflush(stderr); /* just in case error() doesn't */ 492 _exit(EXEC_FAILED_EXIT_STATUS); 493 } 494 /* in the parent */ 495 if (last_input != 0) { 496 if (close(last_input) < 0) 497 sys_fatal("close"); 498 } 499 if ((i != ncommands - 1) && !no_pipe) { 500 if (close(pdes[1]) < 0) 501 sys_fatal("close"); 502 last_input = pdes[0]; 503 } 504 pids[i] = pid; 505 } 506 while (proc_count > 0) { 507 int status; 508 PID_T pid = wait(&status); 509 510 if (pid < 0) 511 sys_fatal("wait"); 512 for (i = 0; i < ncommands; i++) 513 if (pids[i] == pid) { 514 pids[i] = -1; 515 --proc_count; 516 if (WIFSIGNALED(status)) { 517 int sig = WTERMSIG(status); 518#ifdef SIGPIPE 519 if (sig == SIGPIPE) { 520 if (i == ncommands - 1) { 521 /* This works around a problem that occurred when using the 522 rerasterize action in gxditview. What seemed to be 523 happening (on SunOS 4.1.1) was that pclose() closed the 524 pipe and waited for groff, gtroff got a SIGPIPE, but 525 gpic blocked writing to gtroff, and so groff blocked 526 waiting for gpic and gxditview blocked waiting for 527 groff. I don't understand why gpic wasn't getting a 528 SIGPIPE. */ 529 int j; 530 531 for (j = 0; j < ncommands; j++) 532 if (pids[j] > 0) 533 (void)kill(pids[j], SIGPIPE); 534 } 535 } 536 else 537#endif /* SIGPIPE */ 538 { 539 error("%1: %2%3", 540 commands[i][0], 541 xstrsignal(sig), 542 WCOREDUMP(status) ? " (core dumped)" : ""); 543 ret |= 2; 544 } 545 } 546 else if (WIFEXITED(status)) { 547 int exit_status = WEXITSTATUS(status); 548 549 if (exit_status == EXEC_FAILED_EXIT_STATUS) 550 ret |= 4; 551 else if (exit_status != 0) 552 ret |= 1; 553 } 554 else 555 error("unexpected status %1", i_to_a(status), (char *)0, (char *)0); 556 break; 557 } 558 } 559 return ret; 560} 561 562#endif /* not __MSDOS__, not _WIN32 */ 563 564static void sys_fatal(const char *s) 565{ 566 c_fatal("%1: %2", s, strerror(errno), (char *)0); 567} 568 569static const char *xstrsignal(int n) 570{ 571 static char buf[sizeof("Signal ") + 1 + sizeof(int) * 3]; 572 573#ifdef NSIG 574#if HAVE_DECL_SYS_SIGLIST 575 if (n >= 0 && n < NSIG && sys_siglist[n] != 0) 576 return sys_siglist[n]; 577#endif /* HAVE_DECL_SYS_SIGLIST */ 578#endif /* NSIG */ 579 sprintf(buf, "Signal %d", n); 580 return buf; 581} 582