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