Home | History | Annotate | Download | only in djgpp
      1 /* Subprocesses with pipes.
      2 
      3    Copyright (C) 2005-2012 Free Software Foundation, Inc.
      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 3 of the License, or
      8    (at your option) 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, see <http://www.gnu.org/licenses/>.  */
     17 
     18 /* Written by Juan Manuel Guerrero <juan.guerrero (at) gmx.de>. */
     19 
     20 
     21 #include <config.h>
     22 
     23 #include "subpipe.h"
     24 
     25 #include <errno.h>
     26 #include <fcntl.h>
     27 #include <sys/stat.h>
     28 #include <process.h>
     29 #include <signal.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <unistd.h>
     34 #include "xalloc.h"
     35 
     36 
     37 #ifndef STDIN_FILENO
     38 # define STDIN_FILENO 0
     39 #endif
     40 #ifndef STDOUT_FILENO
     41 # define STDOUT_FILENO 1
     42 #endif
     43 
     44 
     45 #include "error.h"
     46 
     47 #include "gettext.h"
     48 #define _(Msgid)  gettext (Msgid)
     49 
     50 
     51 /* Initialize this module. */
     52 
     53 
     54 static int old_stdin;
     55 static int old_stdout;
     56 static char **arguments;
     57 static char tmp_file_name[2][L_tmpnam];
     58 
     59 #define remove_tmp_file(fd, name)                                     \
     60   do {                                                                \
     61     close ((fd));                                                     \
     62     if (unlink ((name)))                                              \
     63       error (EXIT_FAILURE, 0, _("removing of '%s' failed"), (name));  \
     64   } while (0)
     65 
     66 
     67 void
     68 init_subpipe(void)
     69 {
     70   char *tmpdir;
     71   int fd;
     72 
     73   tmpdir = getenv("TMPDIR");
     74   if (tmpdir == NULL)
     75     tmpdir = getenv("TMP");
     76   if (tmpdir == NULL)
     77     tmpdir = getenv("TEMP");
     78   if (access(tmpdir, D_OK))
     79     tmpdir = ".";
     80 
     81   strcpy(tmp_file_name[0], tmpdir);
     82   strcat(tmp_file_name[0], "/bnXXXXXX");
     83   fd = mkstemp(tmp_file_name[0]);
     84   if (fd < 0)
     85     error(EXIT_FAILURE, 0, _("creation of a temporary file failed"));
     86   close (fd);
     87 
     88   strcpy(tmp_file_name[1], tmpdir);
     89   strcat(tmp_file_name[1], "/bnXXXXXX");
     90   fd = mkstemp(tmp_file_name[1]);
     91   if (fd < 0)
     92     error(EXIT_FAILURE, 0, _("creation of a temporary file failed"));
     93   close (fd);
     94 }
     95 
     96 
     97 /* Create a subprocess that is run as a filter.  ARGV is the
     98    NULL-terminated argument vector for the subprocess.  Store read and
     99    write file descriptors for communication with the subprocess into
    100    FD[0] and FD[1]: input meant for the process can be written into
    101    FD[0], and output from the process can be read from FD[1].  Return
    102    the subprocess id.
    103 
    104    Because DOS has neither fork nor pipe functionality to run the subprocess
    105    as a filter, the filter is reproduced using temporary files. First bison's
    106    stdout is redirected to a temporary file. After bison has produced all of
    107    is output, this file is closed and connected to m4's stdin. All m4's output
    108    is redirected from m4's stdout to a second temporary file and reopened as
    109    bison's stdin. */
    110 
    111 pid_t
    112 create_subpipe(char const *const *argv, int fd[2])
    113 {
    114   int argc;
    115   int from_in_fd;  /* pipe from bison to m4. */
    116   pid_t pid;
    117 
    118 
    119   pid = getpid();
    120 
    121   /*
    122    *  Save original stdin and stdout
    123    *  for later restauration.
    124    */
    125   old_stdin = dup(STDIN_FILENO);
    126   if (old_stdin < 0)
    127     error(EXIT_FAILURE, 0, _("saving stdin failed"));
    128 
    129   old_stdout = dup(STDOUT_FILENO);
    130   if (old_stdout < 0)
    131     error(EXIT_FAILURE, 0, _("saving stdout failed"));
    132 
    133   /*
    134    *  Save argv for later use.
    135    */
    136   for (argc = 0; argv[argc]; argc++)
    137     ;
    138   argc++;
    139   arguments = xmalloc(argc * sizeof(arguments[0]));
    140   for (argc = 0; argv[argc]; argc++)
    141   {
    142     arguments[argc] = xmalloc((strlen(argv[argc]) + 1) * sizeof(arguments[0][0]));
    143     strcpy(arguments[argc], argv[argc]);
    144   }
    145   arguments[argc] = NULL;
    146 
    147   /*
    148    *  All bison's output will be gathered in this temporary file
    149    *  and will be redirected to m4's stdin.
    150    */
    151   from_in_fd = open(tmp_file_name[0], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR);
    152   if (from_in_fd < 0)
    153     error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
    154   if (dup2(from_in_fd, STDOUT_FILENO) < 0)
    155   {
    156     remove_tmp_file(from_in_fd, tmp_file_name[0]);
    157     error(EXIT_FAILURE, 0, _("redirecting bison's stdout to the temporary file failed"));
    158   }
    159   close(from_in_fd);
    160 
    161 
    162   fd[0] = STDOUT_FILENO;
    163   return pid;
    164 }
    165 
    166 
    167 /* A signal handler that just records that a signal has happened. */
    168 static int child_interrupted;
    169 
    170 static void
    171 signal_catcher(int signo)
    172 {
    173   child_interrupted++;
    174 }
    175 
    176 
    177 void
    178 end_of_output_subpipe(pid_t pid, int fd[2])
    179 {
    180   char *program;
    181   int from_out_fd = open(tmp_file_name[0], O_RDONLY, S_IRUSR);                   /* pipe from bison to m4. */
    182   int to_in_fd = open(tmp_file_name[1], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR);  /* pipe from m4 to bison. */
    183   int status;
    184   void (*previous_handler)(int);
    185 
    186 
    187   program = strrchr(arguments[0], '/');
    188   if (program)
    189     program++;
    190   else
    191     program = arguments[0];
    192 
    193   /*
    194    *  Redirect bison's output to m4's stdin.
    195    */
    196   if (from_out_fd < 0)
    197     error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
    198   if (dup2(from_out_fd, STDIN_FILENO) < 0)
    199   {
    200     remove_tmp_file(from_out_fd, tmp_file_name[0]);
    201     error(EXIT_FAILURE, 0, _("redirecting m4's stdin from the temporary file failed"));
    202   }
    203   close(from_out_fd);
    204 
    205   /*
    206    *  All m4's output will be gathered in this temporary file
    207    *  and will be redirected to bison's stdin.
    208    */
    209   if (to_in_fd < 0)
    210   {
    211     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    212     error(EXIT_FAILURE, 0, _("opening of a temporary file failed"));
    213   }
    214   if (dup2(to_in_fd, STDOUT_FILENO) < 0)
    215   {
    216     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    217     remove_tmp_file(to_in_fd, tmp_file_name[1]);
    218     error(EXIT_FAILURE, 0, _("redirecting m4's stdout to a temporary file failed"));
    219   }
    220   close(to_in_fd);
    221 
    222   /*
    223    *  Run m4.
    224    */
    225   child_interrupted = 0;
    226   errno = 0;
    227   previous_handler = signal(SIGINT, signal_catcher);
    228   status = spawnvp(P_WAIT, program, arguments);
    229   signal(SIGINT, previous_handler);
    230   if (child_interrupted)
    231   {
    232     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    233     remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]);
    234     error(EXIT_FAILURE, 0, _("subsidiary program '%s' interrupted"), program);
    235   }
    236   if (status)
    237   {
    238     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    239     remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]);
    240     error(EXIT_FAILURE, 0, _(errno == ENOENT
    241 			     ? "subsidiary program '%s' not found"
    242 			     : status < 1
    243 			     ? "subsidiary program '%s' failed"
    244 			     : "subsidiary program '%s' failed (status=%i, errno=%i)"), program, status, errno);
    245   }
    246 
    247 
    248   /*
    249    *  Redirect m4's output to bison's stdin.
    250    */
    251   if (dup2(old_stdout, STDOUT_FILENO) < 0)
    252     error(EXIT_FAILURE, 0, "restore of bison's stdout failed");
    253   close(old_stdout);
    254   to_in_fd = open(tmp_file_name[1], O_RDONLY, S_IRUSR);  /* pipe from m4 to bison. */
    255   if (to_in_fd < 0)
    256   {
    257     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    258     error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
    259   }
    260   if (dup2(to_in_fd, STDIN_FILENO) < 0)
    261   {
    262     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    263     remove_tmp_file(to_in_fd, tmp_file_name[1]);
    264     error(EXIT_FAILURE, -1, "dup2");
    265     error(EXIT_FAILURE, 0, _("redirecting bison's stdin from the temporary file failed"));
    266   }
    267   close(to_in_fd);
    268 
    269 
    270   fd[1] = STDIN_FILENO;
    271 }
    272 
    273 
    274 /* Free resources, unlink temporary files and restore stdin and stdout. */
    275 
    276 void
    277 reap_subpipe(pid_t pid, char const *program)
    278 {
    279   int argc;
    280 
    281   for (argc = 0; arguments[argc]; argc++)
    282     free(arguments[argc]);
    283   free(arguments);
    284 
    285   if (unlink(tmp_file_name[0]))
    286     error(EXIT_FAILURE, 0, _("removing of '%s' failed"), tmp_file_name[0]);
    287   if (unlink(tmp_file_name[1]))
    288     error(EXIT_FAILURE, 0, _("removing of '%s' failed"), tmp_file_name[1]);
    289 
    290   if (dup2(old_stdin, STDIN_FILENO) < 0)
    291     error(EXIT_FAILURE, 0, "restore of bison's stdin failed");
    292   close(old_stdin);
    293 }
    294