core: Make the status-fd monitor work for all gpgsm commands.
[gpgme.git] / src / w32-io.c
index 42961e3..05e11ee 100644 (file)
@@ -50,6 +50,7 @@
 #include "sema.h"
 #include "priv-io.h"
 #include "debug.h"
+#include "sys-util.h"
 
 
 /* FIXME: Optimize.  */
@@ -74,7 +75,7 @@ static struct
      that dup'ed file descriptors are closed before the file
      descriptors from which they are dup'ed are closed, ensures that
      the handle or socket is always valid, and shared among all file
-     descriptors refering to the same underlying object.
+     descriptors referring to the same underlying object.
 
      The logic behind this is that there is only one reason for us to
      dup file descriptors anyway: to allow simpler book-keeping of
@@ -83,6 +84,7 @@ static struct
      duplicates works just fine.  */
   int dup_from;
 } fd_table[MAX_SLAFD];
+DEFINE_STATIC_LOCK (fd_table_lock);
 
 
 /* Returns the FD or -1 on resource limit.  */
@@ -91,6 +93,8 @@ new_fd (void)
 {
   int idx;
 
+  LOCK (fd_table_lock);
+
   for (idx = 0; idx < MAX_SLAFD; idx++)
     if (! fd_table[idx].used)
       break;
@@ -98,14 +102,18 @@ new_fd (void)
   if (idx == MAX_SLAFD)
     {
       gpg_err_set_errno (EIO);
-      return -1;
+      idx = -1;
+    }
+  else
+    {
+      fd_table[idx].used = 1;
+      fd_table[idx].handle = INVALID_HANDLE_VALUE;
+      fd_table[idx].socket = INVALID_SOCKET;
+      fd_table[idx].rvid = 0;
+      fd_table[idx].dup_from = -1;
     }
 
-  fd_table[idx].used = 1;
-  fd_table[idx].handle = INVALID_HANDLE_VALUE;
-  fd_table[idx].socket = INVALID_SOCKET;
-  fd_table[idx].rvid = 0;
-  fd_table[idx].dup_from = -1;
+  UNLOCK (fd_table_lock);
 
   return idx;
 }
@@ -114,14 +122,21 @@ new_fd (void)
 void
 release_fd (int fd)
 {
-  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+  if (fd < 0 || fd >= MAX_SLAFD)
     return;
 
-  fd_table[fd].used = 0;
-  fd_table[fd].handle = INVALID_HANDLE_VALUE;
-  fd_table[fd].socket = INVALID_SOCKET;
-  fd_table[fd].rvid = 0;
-  fd_table[fd].dup_from = -1;
+  LOCK (fd_table_lock);
+
+  if (fd_table[fd].used)
+    {
+      fd_table[fd].used = 0;
+      fd_table[fd].handle = INVALID_HANDLE_VALUE;
+      fd_table[fd].socket = INVALID_SOCKET;
+      fd_table[fd].rvid = 0;
+      fd_table[fd].dup_from = -1;
+    }
+
+  UNLOCK (fd_table_lock);
 }
 
 
@@ -685,7 +700,7 @@ writer (void *arg)
   for (;;)
     {
       LOCK (ctx->mutex);
-      if (ctx->stop_me)
+      if (ctx->stop_me && !ctx->nbytes)
        {
          UNLOCK (ctx->mutex);
          break;
@@ -702,7 +717,7 @@ writer (void *arg)
          TRACE_LOG ("got data to send");
          LOCK (ctx->mutex);
                }
-      if (ctx->stop_me)
+      if (ctx->stop_me && !ctx->nbytes)
        {
          UNLOCK (ctx->mutex);
          break;
@@ -761,6 +776,9 @@ writer (void *arg)
   TRACE_LOG ("waiting for close");
   WaitForSingleObject (ctx->close_ev, INFINITE);
 
+  if (ctx->nbytes)
+    TRACE_LOG1 ("still %d bytes in buffer at close time", ctx->nbytes);
+
   CloseHandle (ctx->close_ev);
   CloseHandle (ctx->have_data);
   CloseHandle (ctx->is_empty);
@@ -877,6 +895,9 @@ destroy_writer (struct writer_context_s *ctx)
     SetEvent (ctx->have_data);
   UNLOCK (ctx->mutex);
 
+  /* Give the writer a chance to flush the buffer.  */
+  WaitForSingleObject (ctx->is_empty, INFINITE);
+
 #ifdef HAVE_W32CE_SYSTEM
   /* Scenario: We never create a full pipe, but already started
      writing more than the pipe buffer.  Then we need to unblock the
@@ -978,7 +999,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
       return TRACE_SYSRES (-1);
     }
 
-  /* If no error occured, the number of bytes in the buffer must be
+  /* If no error occurred, the number of bytes in the buffer must be
      zero.  */
   assert (!ctx->nbytes);
 
@@ -1550,6 +1571,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
   int debug_me = 0;
   int tmp_fd;
   char *tmp_name;
+  const char *spawnhelper;
 
   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
              "path=%s", path);
@@ -1603,7 +1625,33 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
   if ((flags & IOSPAWN_FLAG_DETACHED))
     cr_flags |= DETACHED_PROCESS;
   cr_flags |= GetPriorityClass (GetCurrentProcess ());
-  if (!CreateProcessA (_gpgme_get_w32spawn_path (),
+  spawnhelper = _gpgme_get_w32spawn_path ();
+  if (!spawnhelper)
+    {
+      /* This is a common mistake for new users of gpgme not to include
+         gpgme-w32spawn.exe with their binary. So we want to make
+         this transparent to developers. If users have somehow messed
+         up their installation this should also be properly communicated
+         as otherwise calls to gnupg will result in unsupported protocol
+         errors that do not explain a lot. */
+      char *msg;
+      gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the "
+                            "detected installation directory of GpgME"
+                            "\n\t\"%s\"\n\n"
+                            "Crypto operations will not work.\n\n"
+                            "If you see this it indicates a problem "
+                            "with your installation.\n"
+                            "Please report the problem to your "
+                            "distributor of GpgME.\n\n"
+                            "Developer's Note: The install dir can be "
+                            "manually set with: gpgme_set_global_flag",
+                            _gpgme_get_inst_dir ());
+      MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK);
+      gpgrt_free (msg);
+      gpg_err_set_errno (EIO);
+      return TRACE_SYSRES (-1);
+    }
+  if (!CreateProcessA (spawnhelper,
                       arg_string,
                       &sec_attr,     /* process security attributes */
                       &sec_attr,     /* thread security attributes */
@@ -1614,7 +1662,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
                       &si,           /* startup information */
                       &pi))          /* returns process information */
     {
-      TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+      int lasterr = (int)GetLastError ();
+      TRACE_LOG1 ("CreateProcess failed: ec=%d", lasterr);
       free (arg_string);
       close (tmp_fd);
       DeleteFileA (tmp_name);
@@ -1675,8 +1724,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
     int written;
     size_t len;
 
-    if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
-      strcpy (line, "~1 \n");
+    if (flags)
+      snprintf (line, BUFFER_MAX, "~%i \n", flags);
     else
       strcpy (line, "\n");
     for (i = 0; fd_list[i].fd != -1; i++)