Add parameter checks and extend documentation of estream.
[gnupg.git] / common / exechelp-posix.c
index 5a8e028..5479fe3 100644 (file)
@@ -32,7 +32,7 @@
 #ifdef HAVE_SIGNAL_H
 # include <signal.h>
 #endif
-#include <unistd.h> 
+#include <unistd.h>
 #include <fcntl.h>
 
 #ifdef WITHOUT_GNU_PTH /* Give the Makefile a chance to build without Pth.  */
@@ -40,7 +40,7 @@
 #undef USE_GNU_PTH
 #endif
 
-#ifdef USE_GNU_PTH      
+#ifdef USE_GNU_PTH
 #include <pth.h>
 #endif
 #include <sys/wait.h>
@@ -64,7 +64,7 @@
    and some are not.  However we want to use pth_fork and pth_waitpid
    here. Using a weak symbol works but is not portable - we should
    provide a an explicit dummy pth module instead of using the
-   pragma.  */ 
+   pragma.  */
 #pragma weak pth_fork
 #pragma weak pth_waitpid
 
@@ -181,7 +181,7 @@ get_all_open_fds (void)
   array = calloc (narray, sizeof *array);
   if (!array)
     return NULL;
-  
+
   /* Note:  The list we return is ordered.  */
   for (idx=0, fd=0; fd < max_fd; fd++)
     if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
@@ -258,7 +258,7 @@ do_exec (const char *pgmname, const char *argv[],
 
   /* Close all other files. */
   close_all_fds (3, NULL);
-  
+
   if (preexec)
     preexec ();
   execv (pgmname, arg_list);
@@ -299,79 +299,141 @@ gnupg_create_outbound_pipe (int filedes[2])
 }
 
 
+
+static gpg_error_t
+create_pipe_and_estream (int filedes[2], estream_t *r_fp,
+                         gpg_err_source_t errsource)
+{
+  gpg_error_t err;
+
+  if (pipe (filedes) == -1)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
+      filedes[0] = filedes[1] = -1;
+      *r_fp = NULL;
+      return err;
+    }
+
+  *r_fp = es_fdopen (filedes[0], "r");
+  if (!*r_fp)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      log_error (_("error creating a stream for a pipe: %s\n"),
+                 gpg_strerror (err));
+      close (filedes[0]);
+      close (filedes[1]);
+      filedes[0] = filedes[1] = -1;
+      return err;
+    }
+  return 0;
+}
+
+
+
 /* Fork and exec the PGMNAME, see exechelp.h for details.  */
 gpg_error_t
 gnupg_spawn_process (const char *pgmname, const char *argv[],
-                     estream_t infile, estream_t outfile,
+                     gpg_err_source_t errsource,
                      void (*preexec)(void), unsigned int flags,
-                     estream_t *statusfile, pid_t *pid)
+                     estream_t infp,
+                     estream_t *r_outfp,
+                     estream_t *r_errfp,
+                     pid_t *pid)
 {
   gpg_error_t err;
-  int fd, fdout, rp[2];
+  int infd = -1;
+  int outpipe[2] = {-1, -1};
+  int errpipe[2] = {-1, -1};
+  estream_t outfp = NULL;
+  estream_t errfp = NULL;
 
   (void)flags; /* Currently not used.  */
 
-  *statusfile = NULL;
-  *pid = (pid_t)(-1);
+  if (r_outfp)
+    *r_outfp = NULL;
+  if (r_errfp)
+    *r_errfp = NULL;
+  *pid = (pid_t)(-1); /* Always required.  */
 
-  if (infile)
+  if (infp)
     {
-      es_fflush (infile);
-      es_rewind (infile);
-      fd = es_fileno (infile);
+      es_fflush (infp);
+      es_rewind (infp);
+      infd = es_fileno (infp);
+      if (infd == -1)
+        return gpg_err_make (errsource, GPG_ERR_INV_VALUE);
     }
-  else
-    fd = -1;
 
-  if (outfile)
-    fdout = es_fileno (outfile);
-  else
-    fdout = -1;
-
-  if ((infile && fd == -1) || (outfile && fdout == -1))
-    log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n");
+  if (r_outfp)
+    {
+      err = create_pipe_and_estream (outpipe, &outfp, errsource);
+      if (err)
+        return err;
+    }
 
-  if (pipe (rp) == -1)
+  if (r_errfp)
     {
-      err = gpg_error_from_syserror ();
-      log_error (_("error creating a pipe: %s\n"), strerror (errno));
-      return err;
+      err = create_pipe_and_estream (errpipe, &errfp, errsource);
+      if (err)
+        {
+          if (outfp)
+            es_fclose (outfp);
+          else if (outpipe[0] != -1)
+            close (outpipe[0]);
+          if (outpipe[1] != -1)
+            close (outpipe[1]);
+          return err;
+        }
     }
 
-#ifdef USE_GNU_PTH      
+
+#ifdef USE_GNU_PTH
   *pid = pth_fork? pth_fork () : fork ();
 #else
   *pid = fork ();
 #endif
   if (*pid == (pid_t)(-1))
     {
-      err = gpg_error_from_syserror ();
-      log_error (_("error forking process: %s\n"), strerror (errno));
-      close (rp[0]);
-      close (rp[1]);
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      log_error (_("error forking process: %s\n"), gpg_strerror (err));
+
+      if (outfp)
+        es_fclose (outfp);
+      else if (outpipe[0] != -1)
+        close (outpipe[0]);
+      if (outpipe[1] != -1)
+        close (outpipe[1]);
+
+      if (errfp)
+        es_fclose (errfp);
+      else if (errpipe[0] != -1)
+        close (errpipe[0]);
+      if (errpipe[1] != -1)
+        close (errpipe[1]);
       return err;
     }
 
   if (!*pid)
-    { 
+    {
+      /* This is the child. */
       gcry_control (GCRYCTL_TERM_SECMEM);
-      /* Run child. */
-      do_exec (pgmname, argv, fd, fdout, rp[1], preexec);
+      es_fclose (outfp);
+      es_fclose (errfp);
+      do_exec (pgmname, argv, infd, outpipe[1], errpipe[1], preexec);
       /*NOTREACHED*/
     }
 
-  /* Parent. */
-  close (rp[1]);
+  /* This is the parent. */
+  if (outpipe[1] != -1)
+    close (outpipe[1]);
+  if (errpipe[1] != -1)
+    close (errpipe[1]);
 
-  *statusfile = es_fdopen (rp[0], "r");
-  if (!*statusfile)
-    {
-      err = gpg_error_from_syserror ();
-      log_error (_("can't fdopen pipe for reading: %s\n"), strerror (errno));
-      kill (*pid, SIGTERM);
-      *pid = (pid_t)(-1);
-      return err;
-    }
+  if (r_outfp)
+    *r_outfp = outfp;
+  if (r_errfp)
+    *r_errfp = errfp;
 
   return 0;
 }
@@ -392,7 +454,7 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
 {
   gpg_error_t err;
 
-#ifdef USE_GNU_PTH      
+#ifdef USE_GNU_PTH
   *pid = pth_fork? pth_fork () : fork ();
 #else
   *pid = fork ();
@@ -405,7 +467,7 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
     }
 
   if (!*pid)
-    { 
+    {
       gcry_control (GCRYCTL_TERM_SECMEM);
       /* Run child. */
       do_exec (pgmname, argv, infd, outfd, errfd, NULL);
@@ -439,7 +501,7 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
              && errno == EINTR)
         ;
     }
-  
+
   if (i == (pid_t)(-1))
     {
       ec = gpg_err_code_from_errno (errno);
@@ -469,7 +531,7 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
       log_error (_("error running `%s': terminated\n"), pgmname);
       ec = GPG_ERR_GENERAL;
     }
-  else 
+  else
     {
       if (r_exitcode)
         *r_exitcode = 0;
@@ -507,7 +569,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
   if (access (pgmname, X_OK))
     return gpg_error_from_syserror ();
 
-#ifdef USE_GNU_PTH      
+#ifdef USE_GNU_PTH
   pid = pth_fork? pth_fork () : fork ();
 #else
   pid = fork ();
@@ -534,12 +596,12 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
       if (envp)
         for (i=0; envp[i]; i++)
           putenv (xstrdup (envp[i]));
-      
+
       do_exec (pgmname, argv, -1, -1, -1, NULL);
 
       /*NOTREACHED*/
     }
-  
+
   if (waitpid (pid, NULL, 0) == -1)
     log_error ("waitpid failed in gnupg_spawn_process_detached: %s",
                strerror (errno));
@@ -556,6 +618,6 @@ gnupg_kill_process (pid_t pid)
 {
   if (pid != (pid_t)(-1))
     {
-      kill (pid, SIGTERM); 
+      kill (pid, SIGTERM);
     }
 }