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