tests: Fix t-gettime for a time_t of 64 and a long of 32 bit.
[gnupg.git] / common / exechelp-w32.c
index bc9b5b4..e79ee5b 100644 (file)
@@ -25,7 +25,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -65,7 +65,7 @@
 #include "exechelp.h"
 
 /* Define to 1 do enable debugging.  */
-#define DEBUG_W32_SPAWN 1
+#define DEBUG_W32_SPAWN 0
 
 
 /* It seems Vista doesn't grok X_OK and so fails access() tests.
 # define handle_to_pid(a) ((int)(a))
 
 
+/* Helper */
+static inline gpg_error_t
+my_error_from_syserror (void)
+{
+  return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+}
+
+static inline gpg_error_t
+my_error (int errcode)
+{
+  return gpg_err_make (default_errsource, errcode);
+}
+
+
 /* Return the maximum number of currently allowed open file
    descriptors.  Only useful on POSIX systems but returns a value on
    other systems too.  */
@@ -114,11 +128,14 @@ close_all_fds (int first, int *except)
 
 
 /* Returns an array with all currently open file descriptors.  The end
-   of the array is marked by -1.  The caller needs to release this
-   array using the *standard free* and not with xfree.  This allow the
-   use of this function right at startup even before libgcrypt has
-   been initialized.  Returns NULL on error and sets ERRNO
-   accordingly.  */
+ * of the array is marked by -1.  The caller needs to release this
+ * array using the *standard free* and not with xfree.  This allow the
+ * use of this function right at startup even before libgcrypt has
+ * been initialized.  Returns NULL on error and sets ERRNO
+ * accordingly.  Note that fstat prints a warning to DebugView for all
+ * invalid fds which is a bit annoying.  We actually do not need this
+ * function in real code (close_all_fds is a dummy anyway) but we keep
+ * it for use by t-exechelp.c.  */
 int *
 get_all_open_fds (void)
 {
@@ -219,7 +236,7 @@ build_w32_commandline (const char *pgmname, const char * const *argv,
 
   buf = p = xtrymalloc (n);
   if (!buf)
-    return gpg_error_from_syserror ();
+    return my_error_from_syserror ();
 
   p = build_w32_commandline_copy (p, pgmname);
   for (i=0; argv[i]; i++)
@@ -233,45 +250,41 @@ build_w32_commandline (const char *pgmname, const char * const *argv,
 }
 
 
-/* Create pipe where one end is inheritable: With an INHERIT_IDX of 0
-   the read end is inheritable, with 1 the write end is inheritable.  */
+#define INHERIT_READ   1
+#define INHERIT_WRITE  2
+#define INHERIT_BOTH   (INHERIT_READ|INHERIT_WRITE)
+
+/* Create pipe.  FLAGS indicates which ends are inheritable.  */
 static int
-create_inheritable_pipe (HANDLE filedes[2], int inherit_idx)
+create_inheritable_pipe (HANDLE filedes[2], int flags)
 {
-  HANDLE r, w, h;
+  HANDLE r, w;
   SECURITY_ATTRIBUTES sec_attr;
 
   memset (&sec_attr, 0, sizeof sec_attr );
   sec_attr.nLength = sizeof sec_attr;
-  sec_attr.bInheritHandle = FALSE;
+  sec_attr.bInheritHandle = TRUE;
 
   if (!CreatePipe (&r, &w, &sec_attr, 0))
     return -1;
 
-  if (!DuplicateHandle (GetCurrentProcess(), inherit_idx? w : r,
-                        GetCurrentProcess(), &h, 0,
-                        TRUE, DUPLICATE_SAME_ACCESS ))
-    {
-      log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1));
-      CloseHandle (r);
-      CloseHandle (w);
-      return -1;
-    }
+  if ((flags & INHERIT_READ) == 0)
+    if (! SetHandleInformation (r, HANDLE_FLAG_INHERIT, 0))
+      goto fail;
 
-  if (inherit_idx)
-    {
-      CloseHandle (w);
-      w = h;
-    }
-  else
-    {
-      CloseHandle (r);
-      r = h;
-    }
+  if ((flags & INHERIT_WRITE) == 0)
+    if (! SetHandleInformation (w, HANDLE_FLAG_INHERIT, 0))
+      goto fail;
 
   filedes[0] = r;
   filedes[1] = w;
   return 0;
+
+ fail:
+  log_error ("SetHandleInformation failed: %s\n", w32_strerror (-1));
+  CloseHandle (r);
+  CloseHandle (w);
+  return -1;
 }
 
 
@@ -291,16 +304,17 @@ w32_open_null (int for_write)
 
 
 static gpg_error_t
-do_create_pipe (int filedes[2], int inherit_idx)
+create_pipe_and_estream (int filedes[2], int flags,
+                         estream_t *r_fp, int outbound, int nonblock)
 {
   gpg_error_t err = 0;
   HANDLE fds[2];
 
   filedes[0] = filedes[1] = -1;
-  err = gpg_error (GPG_ERR_GENERAL);
-  if (!create_inheritable_pipe (fds, inherit_idx))
+  err = my_error (GPG_ERR_GENERAL);
+  if (!create_inheritable_pipe (fds, flags))
     {
-      filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), 0);
+      filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY);
       if (filedes[0] == -1)
         {
           log_error ("failed to translate osfhandle %p\n", fds[0]);
@@ -308,7 +322,7 @@ do_create_pipe (int filedes[2], int inherit_idx)
         }
       else
         {
-          filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), 1);
+          filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), O_APPEND);
           if (filedes[1] == -1)
             {
               log_error ("failed to translate osfhandle %p\n", fds[1]);
@@ -320,32 +334,64 @@ do_create_pipe (int filedes[2], int inherit_idx)
             err = 0;
         }
     }
+
+  if (! err && r_fp)
+    {
+      if (!outbound)
+        *r_fp = es_fdopen (filedes[0], nonblock? "r,nonblock" : "r");
+      else
+        *r_fp = es_fdopen (filedes[1], nonblock? "w,nonblock" : "w");
+      if (!*r_fp)
+        {
+          err = my_error_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 err;
 }
 
 /* Portable function to create a pipe.  Under Windows the write end is
-   inheritable.  */
+   inheritable.  If R_FP is not NULL, an estream is created for the
+   read end and stored at R_FP.  */
 gpg_error_t
-gnupg_create_inbound_pipe (int filedes[2])
+gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
 {
-  return do_create_pipe (filedes, 1);
+  return create_pipe_and_estream (filedes, INHERIT_WRITE,
+                                  r_fp, 0, nonblock);
 }
 
 
 /* Portable function to create a pipe.  Under Windows the read end is
+   inheritable.  If R_FP is not NULL, an estream is created for the
+   write end and stored at R_FP.  */
+gpg_error_t
+gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
+{
+  return create_pipe_and_estream (filedes, INHERIT_READ,
+                                  r_fp, 1, nonblock);
+}
+
+
+/* Portable function to create a pipe.  Under Windows both ends are
    inheritable.  */
 gpg_error_t
-gnupg_create_outbound_pipe (int filedes[2])
+gnupg_create_pipe (int filedes[2])
 {
-  return do_create_pipe (filedes, 0);
+  return create_pipe_and_estream (filedes, INHERIT_BOTH,
+                                  NULL, 0, 0);
 }
 
 
 /* Fork and exec the PGMNAME, see exechelp.h for details.  */
 gpg_error_t
 gnupg_spawn_process (const char *pgmname, const char *argv[],
-                     gpg_err_source_t errsource,
-                     void (*preexec)(void), unsigned int flags,
+                     int *except, void (*preexec)(void), unsigned int flags,
                      estream_t *r_infp,
                      estream_t *r_outfp,
                      estream_t *r_errfp,
@@ -374,6 +420,10 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
                       INVALID_HANDLE_VALUE};
   int i;
   es_syshd_t syshd;
+  gpg_err_source_t errsource = default_errsource;
+  int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
+
+  (void)except; /* Not yet used.  */
 
   if (r_infp)
     *r_infp = NULL;
@@ -383,9 +433,9 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
     *r_errfp = NULL;
   *pid = (pid_t)(-1); /* Always required.  */
 
-  if (infp)
+  if (r_infp)
     {
-      if (create_inheritable_pipe (inpipe, 0))
+      if (create_inheritable_pipe (inpipe, INHERIT_READ))
         {
           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
@@ -394,7 +444,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
       syshd.type = ES_SYSHD_HANDLE;
       syshd.u.handle = inpipe[1];
-      infp = es_sysopen (&syshd, "w");
+      infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
       if (!infp)
         {
           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
@@ -409,7 +459,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
   if (r_outfp)
     {
-      if (create_inheritable_pipe (outpipe, 1))
+      if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
         {
           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
@@ -418,7 +468,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
       syshd.type = ES_SYSHD_HANDLE;
       syshd.u.handle = outpipe[0];
-      outfp = es_sysopen (&syshd, "r");
+      outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
       if (!outfp)
         {
           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
@@ -430,7 +480,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
           if (infp)
             es_fclose (infp);
           else if (inpipe[1] != INVALID_HANDLE_VALUE)
-            CloseHandle (outpipe[1]);
+            CloseHandle (inpipe[1]);
           if (inpipe[0] != INVALID_HANDLE_VALUE)
             CloseHandle (inpipe[0]);
           return err;
@@ -439,7 +489,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
   if (r_errfp)
     {
-      if (create_inheritable_pipe (errpipe, 1))
+      if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
         {
           err = gpg_err_make (errsource, GPG_ERR_GENERAL);
           log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
@@ -448,7 +498,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 
       syshd.type = ES_SYSHD_HANDLE;
       syshd.u.handle = errpipe[0];
-      errfp = es_sysopen (&syshd, "r");
+      errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
       if (!errfp)
         {
           err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
@@ -466,7 +516,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
           if (infp)
             es_fclose (infp);
           else if (inpipe[1] != INVALID_HANDLE_VALUE)
-            CloseHandle (outpipe[1]);
+            CloseHandle (inpipe[1]);
           if (inpipe[0] != INVALID_HANDLE_VALUE)
             CloseHandle (inpipe[0]);
           return err;
@@ -483,12 +533,12 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
   if (err)
     return err;
 
-  if (inpipe[0] != INVALID_HANDLE_VALUE)
+  if (inpipe[0] == INVALID_HANDLE_VALUE)
     nullhd[0] = w32_open_null (0);
-  if (outpipe[1] != INVALID_HANDLE_VALUE)
-    nullhd[1] = w32_open_null (0);
-  if (errpipe[1] != INVALID_HANDLE_VALUE)
-    nullhd[2] = w32_open_null (0);
+  if (outpipe[1] == INVALID_HANDLE_VALUE)
+    nullhd[1] = w32_open_null (1);
+  if (errpipe[1] == INVALID_HANDLE_VALUE)
+    nullhd[2] = w32_open_null (1);
 
   /* Start the process.  Note that we can't run the PREEXEC function
      because this might change our own environment. */
@@ -647,7 +697,7 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
                       ))
     {
       log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
-      err = gpg_error (GPG_ERR_GENERAL);
+      err = my_error (GPG_ERR_GENERAL);
     }
   else
     err = 0;
@@ -677,63 +727,86 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
 gpg_error_t
 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
 {
-  gpg_err_code_t ec;
-  HANDLE proc = fd_to_handle (pid);
+  return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
+}
+
+/* See exechelp.h for a description.  */
+gpg_error_t
+gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
+                      int hang, int *r_exitcodes)
+{
+  gpg_err_code_t ec = 0;
+  size_t i;
+  HANDLE *procs;
   int code;
-  DWORD exc;
 
-  if (r_exitcode)
-    *r_exitcode = -1;
+  procs = xtrycalloc (count, sizeof *procs);
+  if (procs == NULL)
+    return my_error_from_syserror ();
+
+  for (i = 0; i < count; i++)
+    {
+      if (r_exitcodes)
+        r_exitcodes[i] = -1;
+
+      if (pids[i] == (pid_t)(-1))
+        return my_error (GPG_ERR_INV_VALUE);
 
-  if (pid == (pid_t)(-1))
-    return gpg_error (GPG_ERR_INV_VALUE);
+      procs[i] = fd_to_handle (pids[i]);
+    }
 
   /* FIXME: We should do a pth_waitpid here.  However this has not yet
      been implemented.  A special W32 pth system call would even be
      better.  */
-  code = WaitForSingleObject (proc, hang? INFINITE : 0);
+  code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
   switch (code)
     {
     case WAIT_TIMEOUT:
       ec = GPG_ERR_TIMEOUT;
-      break;
+      goto leave;
 
     case WAIT_FAILED:
-      log_error (_("waiting for process %d to terminate failed: %s\n"),
-                 (int)pid, w32_strerror (-1));
+      log_error (_("waiting for processes to terminate failed: %s\n"),
+                 w32_strerror (-1));
       ec = GPG_ERR_GENERAL;
-      break;
+      goto leave;
 
     case WAIT_OBJECT_0:
-      if (!GetExitCodeProcess (proc, &exc))
-        {
-          log_error (_("error getting exit code of process %d: %s\n"),
-                     (int)pid, w32_strerror (-1) );
-          ec = GPG_ERR_GENERAL;
-        }
-      else if (exc)
-        {
-          log_error (_("error running '%s': exit status %d\n"),
-                     pgmname, (int)exc );
-          if (r_exitcode)
-            *r_exitcode = (int)exc;
-          ec = GPG_ERR_GENERAL;
-        }
-      else
+      for (i = 0; i < count; i++)
         {
-          if (r_exitcode)
-            *r_exitcode = 0;
-          ec = 0;
+          DWORD exc;
+
+          if (! GetExitCodeProcess (procs[i], &exc))
+            {
+              log_error (_("error getting exit code of process %d: %s\n"),
+                         (int) pids[i], w32_strerror (-1) );
+              ec = GPG_ERR_GENERAL;
+            }
+          else if (exc)
+            {
+              if (!r_exitcodes)
+                log_error (_("error running '%s': exit status %d\n"),
+                           pgmnames[i], (int)exc);
+              else
+                r_exitcodes[i] = (int)exc;
+              ec = GPG_ERR_GENERAL;
+            }
+          else
+            {
+              if (r_exitcodes)
+                r_exitcodes[i] = 0;
+            }
         }
       break;
 
     default:
-      log_error ("WaitForSingleObject returned unexpected "
-                 "code %d for pid %d\n", code, (int)pid );
+      log_error ("WaitForMultipleObjects returned unexpected "
+                 "code %d\n", code);
       ec = GPG_ERR_GENERAL;
       break;
     }
 
+ leave:
   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
 }
 
@@ -780,7 +853,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
   (void)envp;
 
   if (access (pgmname, X_OK))
-    return gpg_error_from_syserror ();
+    return my_error_from_syserror ();
 
   /* Prepare security attributes.  */
   memset (&sec_attr, 0, sizeof sec_attr );
@@ -818,7 +891,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
     {
       log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
       xfree (cmdline);
-      return gpg_error (GPG_ERR_GENERAL);
+      return my_error (GPG_ERR_GENERAL);
     }
   xfree (cmdline);
   cmdline = NULL;
@@ -829,6 +902,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
 
   CloseHandle (pi.hThread);
+  CloseHandle (pi.hProcess);
 
   return 0;
 }