1 /* $NetBSD: pipeline.c,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $ */
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
7 This file is part of groff.
8
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
13
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 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
38 extern 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
85 extern "C" {
86 #endif
87
88 extern void error(const char *, const char *, const char *, const char *);
89 extern void c_fatal(const char *, const char *, const char *, const char *);
90 extern const char *i_to_a(int); /* from libgroff */
91
92 #ifdef __cplusplus
93 }
94 #endif
95
96 static void sys_fatal(const char *);
97 static 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
111 static const char *sh = "sh";
112 static const char *cmd = "cmd";
113 static const char *command = "command";
114
115 extern int strcasecmp(const char *, const char *);
116
sbasename(const char * path)117 char *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 */
system_shell_name(void)142 char *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
system_shell_dash_c(void)166 const 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
is_system_shell(const char * prog)183 int 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
run_pipeline(int ncommands,char *** commands,int no_pipe)214 int 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. */
354 static int child_interrupted;
355
signal_catcher(int signo)356 static RETSIGTYPE signal_catcher(int signo)
357 {
358 child_interrupted++;
359 }
360
run_pipeline(int ncommands,char *** commands,int no_pipe)361 int 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
run_pipeline(int ncommands,char *** commands,int no_pipe)449 int 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
sys_fatal(const char * s)564 static void sys_fatal(const char *s)
565 {
566 c_fatal("%1: %2", s, strerror(errno), (char *)0);
567 }
568
xstrsignal(int n)569 static 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