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