xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/gnulib-lib/pipe.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /* Creation of subprocesses, communicating via pipes.
2    Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc.
3    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18 
19 
20 #include <config.h>
21 
22 /* Specification.  */
23 #include "pipe.h"
24 
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <signal.h>
29 #include <unistd.h>
30 
31 #include "error.h"
32 #include "exit.h"
33 #include "fatal-signal.h"
34 #include "wait-process.h"
35 #include "gettext.h"
36 
37 #define _(str) gettext (str)
38 
39 #if defined _MSC_VER || defined __MINGW32__
40 
41 /* Native Woe32 API.  */
42 # include <process.h>
43 # include "w32spawn.h"
44 
45 #else
46 
47 /* Unix API.  */
48 # ifdef HAVE_POSIX_SPAWN
49 #  include <spawn.h>
50 # else
51 #  ifdef HAVE_VFORK_H
52 #   include <vfork.h>
53 #  endif
54 # endif
55 
56 #endif
57 
58 #ifndef HAVE_ENVIRON_DECL
59 extern char **environ;
60 #endif
61 
62 #ifndef STDIN_FILENO
63 # define STDIN_FILENO 0
64 #endif
65 #ifndef STDOUT_FILENO
66 # define STDOUT_FILENO 1
67 #endif
68 #ifndef STDERR_FILENO
69 # define STDERR_FILENO 2
70 #endif
71 
72 
73 #ifdef EINTR
74 
75 /* EINTR handling for close().
76    These functions can return -1/EINTR even though we don't have any
77    signal handlers set up, namely when we get interrupted via SIGSTOP.  */
78 
79 static inline int
nonintr_close(int fd)80 nonintr_close (int fd)
81 {
82   int retval;
83 
84   do
85     retval = close (fd);
86   while (retval < 0 && errno == EINTR);
87 
88   return retval;
89 }
90 #define close nonintr_close
91 
92 static inline int
nonintr_open(const char * pathname,int oflag,mode_t mode)93 nonintr_open (const char *pathname, int oflag, mode_t mode)
94 {
95   int retval;
96 
97   do
98     retval = open (pathname, oflag, mode);
99   while (retval < 0 && errno == EINTR);
100 
101   return retval;
102 }
103 #undef open /* avoid warning on VMS */
104 #define open nonintr_open
105 
106 #endif
107 
108 
109 /* Open a pipe connected to a child process.
110  *
111  *           write       system                read
112  *    parent  ->   fd[1]   ->   STDIN_FILENO    ->   child       if pipe_stdin
113  *    parent  <-   fd[0]   <-   STDOUT_FILENO   <-   child       if pipe_stdout
114  *           read        system                write
115  *
116  * At least one of pipe_stdin, pipe_stdout must be true.
117  * pipe_stdin and prog_stdin together determine the child's standard input.
118  * pipe_stdout and prog_stdout together determine the child's standard output.
119  * If pipe_stdin is true, prog_stdin is ignored.
120  * If pipe_stdout is true, prog_stdout is ignored.
121  */
122 static pid_t
create_pipe(const char * progname,const char * prog_path,char ** prog_argv,bool pipe_stdin,bool pipe_stdout,const char * prog_stdin,const char * prog_stdout,bool null_stderr,bool slave_process,bool exit_on_error,int fd[2])123 create_pipe (const char *progname,
124 	     const char *prog_path, char **prog_argv,
125 	     bool pipe_stdin, bool pipe_stdout,
126 	     const char *prog_stdin, const char *prog_stdout,
127 	     bool null_stderr,
128 	     bool slave_process, bool exit_on_error,
129 	     int fd[2])
130 {
131 #if defined _MSC_VER || defined __MINGW32__
132 
133   /* Native Woe32 API.
134      This uses _pipe(), dup2(), and spawnv().  It could also be implemented
135      using the low-level functions CreatePipe(), DuplicateHandle(),
136      CreateProcess() and _open_osfhandle(); see the GNU make and GNU clisp
137      and cvs source code.  */
138   int ifd[2];
139   int ofd[2];
140   int orig_stdin;
141   int orig_stdout;
142   int orig_stderr;
143   int child;
144   int nulloutfd;
145   int stdinfd;
146   int stdoutfd;
147 
148   prog_argv = prepare_spawn (prog_argv);
149 
150   if (pipe_stdout)
151     if (_pipe (ifd, 4096, O_BINARY | O_NOINHERIT) < 0)
152       error (EXIT_FAILURE, errno, _("cannot create pipe"));
153   if (pipe_stdin)
154     if (_pipe (ofd, 4096, O_BINARY | O_NOINHERIT) < 0)
155       error (EXIT_FAILURE, errno, _("cannot create pipe"));
156 /* Data flow diagram:
157  *
158  *           write        system         read
159  *    parent  ->   ofd[1]   ->   ofd[0]   ->   child       if pipe_stdin
160  *    parent  <-   ifd[0]   <-   ifd[1]   <-   child       if pipe_stdout
161  *           read         system         write
162  *
163  */
164 
165   /* Save standard file handles of parent process.  */
166   if (pipe_stdin || prog_stdin != NULL)
167     orig_stdin = dup_noinherit (STDIN_FILENO);
168   if (pipe_stdout || prog_stdout != NULL)
169     orig_stdout = dup_noinherit (STDOUT_FILENO);
170   if (null_stderr)
171     orig_stderr = dup_noinherit (STDERR_FILENO);
172   child = -1;
173 
174   /* Create standard file handles of child process.  */
175   nulloutfd = -1;
176   stdinfd = -1;
177   stdoutfd = -1;
178   if ((!pipe_stdin || dup2 (ofd[0], STDIN_FILENO) >= 0)
179       && (!pipe_stdout || dup2 (ifd[1], STDOUT_FILENO) >= 0)
180       && (!null_stderr
181 	  || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
182 	      && (nulloutfd == STDERR_FILENO
183 		  || (dup2 (nulloutfd, STDERR_FILENO) >= 0
184 		      && close (nulloutfd) >= 0))))
185       && (pipe_stdin
186 	  || prog_stdin == NULL
187 	  || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
188 	      && (stdinfd == STDIN_FILENO
189 		  || (dup2 (stdinfd, STDIN_FILENO) >= 0
190 		      && close (stdinfd) >= 0))))
191       && (pipe_stdout
192 	  || prog_stdout == NULL
193 	  || ((stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0
194 	      && (stdoutfd == STDOUT_FILENO
195 		  || (dup2 (stdoutfd, STDOUT_FILENO) >= 0
196 		      && close (stdoutfd) >= 0)))))
197     /* The child process doesn't inherit ifd[0], ifd[1], ofd[0], ofd[1],
198        but it inherits all open()ed or dup2()ed file handles (which is what
199        we want in the case of STD*_FILENO) and also orig_stdin,
200        orig_stdout, orig_stderr (which is not explicitly wanted but
201        harmless).  */
202     child = spawnvp (P_NOWAIT, prog_path, prog_argv);
203   if (stdinfd >= 0)
204     close (stdinfd);
205   if (stdoutfd >= 0)
206     close (stdoutfd);
207   if (nulloutfd >= 0)
208     close (nulloutfd);
209 
210   /* Restore standard file handles of parent process.  */
211   if (null_stderr)
212     dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr);
213   if (pipe_stdout || prog_stdout != NULL)
214     dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout);
215   if (pipe_stdin || prog_stdin != NULL)
216     dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin);
217 
218   if (pipe_stdin)
219     close (ofd[0]);
220   if (pipe_stdout)
221     close (ifd[1]);
222   if (child == -1)
223     {
224       if (exit_on_error || !null_stderr)
225 	error (exit_on_error ? EXIT_FAILURE : 0, errno,
226 	       _("%s subprocess failed"), progname);
227       if (pipe_stdout)
228 	close (ifd[0]);
229       if (pipe_stdin)
230 	close (ofd[1]);
231       return -1;
232     }
233 
234   if (pipe_stdout)
235     fd[0] = ifd[0];
236   if (pipe_stdin)
237     fd[1] = ofd[1];
238   return child;
239 
240 #else
241 
242   /* Unix API.  */
243   int ifd[2];
244   int ofd[2];
245 # if HAVE_POSIX_SPAWN
246   sigset_t blocked_signals;
247   posix_spawn_file_actions_t actions;
248   bool actions_allocated;
249   posix_spawnattr_t attrs;
250   bool attrs_allocated;
251   int err;
252   pid_t child;
253 # else
254   int child;
255 # endif
256 
257   if (pipe_stdout)
258     if (pipe (ifd) < 0)
259       error (EXIT_FAILURE, errno, _("cannot create pipe"));
260   if (pipe_stdin)
261     if (pipe (ofd) < 0)
262       error (EXIT_FAILURE, errno, _("cannot create pipe"));
263 /* Data flow diagram:
264  *
265  *           write        system         read
266  *    parent  ->   ofd[1]   ->   ofd[0]   ->   child       if pipe_stdin
267  *    parent  <-   ifd[0]   <-   ifd[1]   <-   child       if pipe_stdout
268  *           read         system         write
269  *
270  */
271 
272 # if HAVE_POSIX_SPAWN
273   if (slave_process)
274     {
275       sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
276       block_fatal_signals ();
277     }
278   actions_allocated = false;
279   attrs_allocated = false;
280   if ((err = posix_spawn_file_actions_init (&actions)) != 0
281       || (actions_allocated = true,
282 	  (pipe_stdin
283 	   && (err = posix_spawn_file_actions_adddup2 (&actions,
284 						       ofd[0], STDIN_FILENO))
285 	      != 0)
286 	  || (pipe_stdout
287 	      && (err = posix_spawn_file_actions_adddup2 (&actions,
288 							  ifd[1], STDOUT_FILENO))
289 		 != 0)
290 	  || (pipe_stdin
291 	      && (err = posix_spawn_file_actions_addclose (&actions, ofd[0]))
292 		 != 0)
293 	  || (pipe_stdout
294 	      && (err = posix_spawn_file_actions_addclose (&actions, ifd[1]))
295 		 != 0)
296 	  || (pipe_stdin
297 	      && (err = posix_spawn_file_actions_addclose (&actions, ofd[1]))
298 		 != 0)
299 	  || (pipe_stdout
300 	      && (err = posix_spawn_file_actions_addclose (&actions, ifd[0]))
301 		 != 0)
302 	  || (null_stderr
303 	      && (err = posix_spawn_file_actions_addopen (&actions,
304 							  STDERR_FILENO,
305 							  "/dev/null", O_RDWR,
306 							  0))
307 		 != 0)
308 	  || (!pipe_stdin
309 	      && prog_stdin != NULL
310 	      && (err = posix_spawn_file_actions_addopen (&actions,
311 							  STDIN_FILENO,
312 							  prog_stdin, O_RDONLY,
313 							  0))
314 		 != 0)
315 	  || (!pipe_stdout
316 	      && prog_stdout != NULL
317 	      && (err = posix_spawn_file_actions_addopen (&actions,
318 							  STDOUT_FILENO,
319 							  prog_stdout, O_WRONLY,
320 							  0))
321 		 != 0)
322 	  || (slave_process
323 	      && ((err = posix_spawnattr_init (&attrs)) != 0
324 		  || (attrs_allocated = true,
325 		      (err = posix_spawnattr_setsigmask (&attrs,
326 							 &blocked_signals))
327 		      != 0
328 		      || (err = posix_spawnattr_setflags (&attrs,
329 							POSIX_SPAWN_SETSIGMASK))
330 			 != 0)))
331 	  || (err = posix_spawnp (&child, prog_path, &actions,
332 				  attrs_allocated ? &attrs : NULL, prog_argv,
333 				  environ))
334 	     != 0))
335     {
336       if (actions_allocated)
337 	posix_spawn_file_actions_destroy (&actions);
338       if (attrs_allocated)
339 	posix_spawnattr_destroy (&attrs);
340       if (slave_process)
341 	unblock_fatal_signals ();
342       if (exit_on_error || !null_stderr)
343 	error (exit_on_error ? EXIT_FAILURE : 0, err,
344 	       _("%s subprocess failed"), progname);
345       if (pipe_stdout)
346 	{
347 	  close (ifd[0]);
348 	  close (ifd[1]);
349 	}
350       if (pipe_stdin)
351 	{
352 	  close (ofd[0]);
353 	  close (ofd[1]);
354 	}
355       return -1;
356     }
357   posix_spawn_file_actions_destroy (&actions);
358   if (attrs_allocated)
359     posix_spawnattr_destroy (&attrs);
360 # else
361   if (slave_process)
362     block_fatal_signals ();
363   /* Use vfork() instead of fork() for efficiency.  */
364   if ((child = vfork ()) == 0)
365     {
366       /* Child process code.  */
367       int nulloutfd;
368       int stdinfd;
369       int stdoutfd;
370 
371       if ((!pipe_stdin || dup2 (ofd[0], STDIN_FILENO) >= 0)
372 	  && (!pipe_stdout || dup2 (ifd[1], STDOUT_FILENO) >= 0)
373 	  && (!pipe_stdin || close (ofd[0]) >= 0)
374 	  && (!pipe_stdout || close (ifd[1]) >= 0)
375 	  && (!pipe_stdin || close (ofd[1]) >= 0)
376 	  && (!pipe_stdout || close (ifd[0]) >= 0)
377 	  && (!null_stderr
378 	      || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
379 		  && (nulloutfd == STDERR_FILENO
380 		      || (dup2 (nulloutfd, STDERR_FILENO) >= 0
381 			  && close (nulloutfd) >= 0))))
382 	  && (pipe_stdin
383 	      || prog_stdin == NULL
384 	      || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
385 		  && (stdinfd == STDIN_FILENO
386 		      || (dup2 (stdinfd, STDIN_FILENO) >= 0
387 			  && close (stdinfd) >= 0))))
388 	  && (pipe_stdout
389 	      || prog_stdout == NULL
390 	      || ((stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0
391 		  && (stdoutfd == STDOUT_FILENO
392 		      || (dup2 (stdoutfd, STDOUT_FILENO) >= 0
393 			  && close (stdoutfd) >= 0))))
394 	  && (!slave_process || (unblock_fatal_signals (), true)))
395 	execvp (prog_path, prog_argv);
396       _exit (127);
397     }
398   if (child == -1)
399     {
400       if (slave_process)
401 	unblock_fatal_signals ();
402       if (exit_on_error || !null_stderr)
403 	error (exit_on_error ? EXIT_FAILURE : 0, errno,
404 	       _("%s subprocess failed"), progname);
405       if (pipe_stdout)
406 	{
407 	  close (ifd[0]);
408 	  close (ifd[1]);
409 	}
410       if (pipe_stdin)
411 	{
412 	  close (ofd[0]);
413 	  close (ofd[1]);
414 	}
415       return -1;
416     }
417 # endif
418   if (slave_process)
419     {
420       register_slave_subprocess (child);
421       unblock_fatal_signals ();
422     }
423   if (pipe_stdin)
424     close (ofd[0]);
425   if (pipe_stdout)
426     close (ifd[1]);
427 
428   if (pipe_stdout)
429     fd[0] = ifd[0];
430   if (pipe_stdin)
431     fd[1] = ofd[1];
432   return child;
433 
434 #endif
435 }
436 
437 /* Open a bidirectional pipe.
438  *
439  *           write       system                read
440  *    parent  ->   fd[1]   ->   STDIN_FILENO    ->   child
441  *    parent  <-   fd[0]   <-   STDOUT_FILENO   <-   child
442  *           read        system                write
443  *
444  */
445 pid_t
create_pipe_bidi(const char * progname,const char * prog_path,char ** prog_argv,bool null_stderr,bool slave_process,bool exit_on_error,int fd[2])446 create_pipe_bidi (const char *progname,
447 		  const char *prog_path, char **prog_argv,
448 		  bool null_stderr,
449 		  bool slave_process, bool exit_on_error,
450 		  int fd[2])
451 {
452   pid_t result = create_pipe (progname, prog_path, prog_argv,
453 			      true, true, NULL, NULL,
454 			      null_stderr, slave_process, exit_on_error,
455 			      fd);
456   return result;
457 }
458 
459 /* Open a pipe for input from a child process.
460  * The child's stdin comes from a file.
461  *
462  *           read        system                write
463  *    parent  <-   fd[0]   <-   STDOUT_FILENO   <-   child
464  *
465  */
466 pid_t
create_pipe_in(const char * progname,const char * prog_path,char ** prog_argv,const char * prog_stdin,bool null_stderr,bool slave_process,bool exit_on_error,int fd[1])467 create_pipe_in (const char *progname,
468 		const char *prog_path, char **prog_argv,
469 		const char *prog_stdin, bool null_stderr,
470 		bool slave_process, bool exit_on_error,
471 		int fd[1])
472 {
473   int iofd[2];
474   pid_t result = create_pipe (progname, prog_path, prog_argv,
475 			      false, true, prog_stdin, NULL,
476 			      null_stderr, slave_process, exit_on_error,
477 			      iofd);
478   if (result != -1)
479     fd[0] = iofd[0];
480   return result;
481 }
482 
483 /* Open a pipe for output to a child process.
484  * The child's stdout goes to a file.
485  *
486  *           write       system                read
487  *    parent  ->   fd[0]   ->   STDIN_FILENO    ->   child
488  *
489  */
490 pid_t
create_pipe_out(const char * progname,const char * prog_path,char ** prog_argv,const char * prog_stdout,bool null_stderr,bool slave_process,bool exit_on_error,int fd[1])491 create_pipe_out (const char *progname,
492 		 const char *prog_path, char **prog_argv,
493 		 const char *prog_stdout, bool null_stderr,
494 		 bool slave_process, bool exit_on_error,
495 		 int fd[1])
496 {
497   int iofd[2];
498   pid_t result = create_pipe (progname, prog_path, prog_argv,
499 			      true, false, NULL, prog_stdout,
500 			      null_stderr, slave_process, exit_on_error,
501 			      iofd);
502   if (result != -1)
503     fd[0] = iofd[1];
504   return result;
505 }
506