assuan/
[gpgme.git] / assuan / assuan-pipe-connect.c
index cbc131f..7fdfe74 100644 (file)
@@ -1,5 +1,6 @@
 /* assuan-pipe-connect.c - Establish a pipe connection (client) 
- *     Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2005, 2006,
+ *               2007 Free Software Foundation, Inc.
  *
  * This file is part of Assuan.
  *
@@ -14,8 +15,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
 
 #include "assuan-defs.h"
 
+#ifdef _ASSUAN_IN_GPGME_BUILD_ASSUAN
+
+/* From GPGME priv-io.h  */
+struct spawn_fd_item_s
+{
+  int fd;
+  int dup_to;
+  int peer_name;
+  int arg_loc;
+};
+
+
+int _gpgme_io_pipe (int filedes[2], int inherit_idx);
+int _gpgme_io_spawn (const char *path, char *const argv[],
+                    struct spawn_fd_item_s *fd_list, pid_t *r_pid);
+#endif
+
+/* Hacks for Slowaris.  */
+#ifndef PF_LOCAL
+# ifdef PF_UNIX
+#  define PF_LOCAL PF_UNIX
+# else
+#  define PF_LOCAL AF_UNIX
+# endif
+#endif
+#ifndef AF_LOCAL
+# define AF_LOCAL AF_UNIX
+#endif
+
+
 #ifdef _POSIX_OPEN_MAX
 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
 #else
 #define MAX_OPEN_FDS 20
 #endif
 
-#ifdef HAVE_W32_SYSTEM
-/* We assume that a HANDLE can be represented by an int which should
-   be true for all i386 systems (HANDLE is defined as void *) and
-   these are the only systems for which Windows is available.  Further
-   we assume that -1 denotes an invalid handle.  */
-#define fd_to_handle(a)  ((HANDLE)(a))
-#define handle_to_fd(a)  ((int)(a))
-#define pid_to_handle(a) ((HANDLE)(a))
-#define handle_to_pid(a) ((int)(a))
-#endif /*HAVE_W32_SYSTEM*/
-
 
 /* This should be called to make sure that SIGPIPE gets ignored.  */
 static void
@@ -84,65 +103,592 @@ fix_signals (void)
 }
 
 
-#ifndef HAVE_W32_SYSTEM
-static int
-writen (int fd, const char *buffer, size_t length)
-{
-  while (length)
+#ifndef HAVE_W32_SYSTEM
+static int
+writen (int fd, const char *buffer, size_t length)
+{
+  while (length)
+    {
+      int nwritten = write (fd, buffer, length);
+      
+      if (nwritten < 0)
+        {
+          if (errno == EINTR)
+            continue;
+          return -1; /* write error */
+        }
+      length -= nwritten;
+      buffer += nwritten;
+    }
+  return 0;  /* okay */
+}
+#endif
+
+static int
+do_finish (assuan_context_t ctx)
+{
+  if (ctx->inbound.fd != ASSUAN_INVALID_FD)
+    {
+      _assuan_close (ctx->inbound.fd);
+      if (ctx->inbound.fd == ctx->outbound.fd)
+        ctx->outbound.fd = ASSUAN_INVALID_FD;
+      ctx->inbound.fd = ASSUAN_INVALID_FD;
+    }
+  if (ctx->outbound.fd != ASSUAN_INVALID_FD)
+    {
+      _assuan_close (ctx->outbound.fd);
+      ctx->outbound.fd = ASSUAN_INVALID_FD;
+    }
+  if (ctx->pid != (pid_t)(-1) && ctx->pid)
+    {
+#ifndef HAVE_W32_SYSTEM
+#ifndef _ASSUAN_USE_DOUBLE_FORK
+      if (!ctx->flags.no_waitpid)
+        _assuan_waitpid (ctx->pid, NULL, 0); 
+      ctx->pid =(pid_t)(-1);
+#endif
+#else /*!HAVE_W32_SYSTEM*/
+      CloseHandle ((HANDLE) ctx->pid);
+      ctx->pid = (pid_t) INVALID_HANDLE_VALUE;
+#endif /*HAVE_W32_SYSTEM*/
+    }
+  return 0;
+}
+
+
+static void
+do_deinit (assuan_context_t ctx)
+{
+  do_finish (ctx);
+}
+
+
+/* Helper for pipe_connect. */
+static assuan_error_t
+initial_handshake (assuan_context_t *ctx)
+{
+  int okay, off;
+  assuan_error_t err;
+  
+  err = _assuan_read_from_server (*ctx, &okay, &off);
+  if (err)
+    _assuan_log_printf ("can't connect server: %s\n",
+                        assuan_strerror (err));
+  else if (okay != 1)
+    {
+      _assuan_log_printf ("can't connect server: `%s'\n",
+                          (*ctx)->inbound.line);
+      err = _assuan_error (ASSUAN_Connect_Failed);
+    }
+
+  if (err)
+    {
+      assuan_disconnect (*ctx);
+      *ctx = NULL;
+    }
+  return err;
+}
+
+
+#ifndef _ASSUAN_IN_GPGME_BUILD_ASSUAN
+
+#ifndef HAVE_W32_SYSTEM
+#define pipe_connect pipe_connect_unix
+/* Unix version of the pipe connection code.  We use an extra macro to
+   make ChangeLog entries easier. */
+static assuan_error_t
+pipe_connect_unix (assuan_context_t *ctx,
+                   const char *name, const char *const argv[],
+                   int *fd_child_list,
+                   void (*atfork) (void *opaque, int reserved),
+                   void *atforkvalue, unsigned int flags)
+{
+  assuan_error_t err;
+  int rp[2];
+  int wp[2];
+  char mypidstr[50];
+
+  (void)flags;
+
+  if (!ctx || !name || !argv || !argv[0])
+    return _assuan_error (ASSUAN_Invalid_Value);
+
+  fix_signals ();
+
+  sprintf (mypidstr, "%lu", (unsigned long)getpid ());
+
+  if (pipe (rp) < 0)
+    return _assuan_error (ASSUAN_General_Error);
+  
+  if (pipe (wp) < 0)
+    {
+      close (rp[0]);
+      close (rp[1]);
+      return _assuan_error (ASSUAN_General_Error);
+    }
+
+  err = _assuan_new_context (ctx);
+  if (err)
+    {
+      close (rp[0]);
+      close (rp[1]);
+      close (wp[0]);
+      close (wp[1]);
+      return err;
+    }
+  (*ctx)->pipe_mode = 1;
+  (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
+  (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
+  (*ctx)->deinit_handler = do_deinit;
+  (*ctx)->finish_handler = do_finish;
+
+  /* FIXME: For GPGME we should better use _gpgme_io_spawn.  The PID
+     stored here is actually soon useless.  */
+  (*ctx)->pid = fork ();
+  if ((*ctx)->pid < 0)
+    {
+      close (rp[0]);
+      close (rp[1]);
+      close (wp[0]);
+      close (wp[1]);
+      _assuan_release_context (*ctx); 
+      return _assuan_error (ASSUAN_General_Error);
+    }
+
+  if ((*ctx)->pid == 0)
+    {
+#ifdef _ASSUAN_USE_DOUBLE_FORK      
+      pid_t pid;
+
+      if ((pid = fork ()) == 0)
+#endif
+       {
+          int i, n;
+          char errbuf[512];
+          int *fdp;
+          
+          if (atfork)
+            atfork (atforkvalue, 0);
+
+          /* Dup handles to stdin/stdout. */
+          if (rp[1] != STDOUT_FILENO)
+            {
+              if (dup2 (rp[1], STDOUT_FILENO) == -1)
+                {
+                  _assuan_log_printf ("dup2 failed in child: %s\n",
+                                      strerror (errno));
+                  _exit (4);
+                }
+            }
+          if (wp[0] != STDIN_FILENO)
+            {
+              if (dup2 (wp[0], STDIN_FILENO) == -1)
+                {
+                  _assuan_log_printf ("dup2 failed in child: %s\n",
+                                      strerror (errno));
+                  _exit (4);
+                }
+            }
+
+          /* Dup stderr to /dev/null unless it is in the list of FDs to be
+             passed to the child. */
+          fdp = fd_child_list;
+          if (fdp)
+            {
+              for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
+                ;
+            }
+          if (!fdp || *fdp == -1)
+            {
+              int fd = open ("/dev/null", O_WRONLY);
+              if (fd == -1)
+                {
+                  _assuan_log_printf ("can't open `/dev/null': %s\n",
+                                      strerror (errno));
+                  _exit (4);
+                }
+              if (dup2 (fd, STDERR_FILENO) == -1)
+                {
+                  _assuan_log_printf ("dup2(dev/null, 2) failed: %s\n",
+                                      strerror (errno));
+                  _exit (4);
+                }
+            }
+
+
+          /* Close all files which will not be duped and are not in the
+             fd_child_list. */
+          n = sysconf (_SC_OPEN_MAX);
+          if (n < 0)
+            n = MAX_OPEN_FDS;
+          for (i=0; i < n; i++)
+            {
+              if ( i == STDIN_FILENO || i == STDOUT_FILENO
+                   || i == STDERR_FILENO)
+                continue;
+              fdp = fd_child_list;
+              if (fdp)
+                {
+                  while (*fdp != -1 && *fdp != i)
+                    fdp++;
+                }
+
+              if (!(fdp && *fdp != -1))
+                close(i);
+            }
+          errno = 0;
+
+          /* We store our parents pid in the environment so that the
+             execed assuan server is able to read the actual pid of the
+             client.  The server can't use getppid because it might have
+             been double forked before the assuan server has been
+             initialized. */
+          setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
+
+          /* Make sure that we never pass a connection fd variable
+             when using a simple pipe.  */
+          unsetenv ("_assuan_connection_fd");
+
+          execv (name, (char *const *) argv); 
+          /* oops - use the pipe to tell the parent about it */
+          snprintf (errbuf, sizeof(errbuf)-1,
+                    "ERR %d can't exec `%s': %.50s\n",
+                    _assuan_error (ASSUAN_Problem_Starting_Server),
+                    name, strerror (errno));
+          errbuf[sizeof(errbuf)-1] = 0;
+          writen (1, errbuf, strlen (errbuf));
+          _exit (4);
+        }
+#ifdef _ASSUAN_USE_DOUBLE_FORK
+      if (pid == -1)
+       _exit (1);
+      else
+       _exit (0);
+#endif
+    }
+
+#ifdef _ASSUAN_USE_DOUBLE_FORK
+  _assuan_waitpid ((*ctx)->pid, NULL, 0);
+  (*ctx)->pid = -1;
+#endif
+
+  close (rp[1]);
+  close (wp[0]);
+
+  return initial_handshake (ctx);
+}
+#endif /*!HAVE_W32_SYSTEM*/
+#endif /* _ASSUAN_IN_GPGME_BUILD_ASSUAN */
+
+
+#ifndef HAVE_W32_SYSTEM
+/* This function is similar to pipe_connect but uses a socketpair and
+   sets the I/O up to use sendmsg/recvmsg. */
+static assuan_error_t
+socketpair_connect (assuan_context_t *ctx,
+                    const char *name, const char *const argv[],
+                    int *fd_child_list,
+                    void (*atfork) (void *opaque, int reserved),
+                    void *atforkvalue)
+{
+  assuan_error_t err;
+  int fds[2];
+  char mypidstr[50];
+
+  if (!ctx
+      || (name && (!argv || !argv[0]))
+      || (!name && argv))
+    return _assuan_error (ASSUAN_Invalid_Value);
+
+  fix_signals ();
+
+  sprintf (mypidstr, "%lu", (unsigned long)getpid ());
+
+  if ( socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) )
+    {
+      _assuan_log_printf ("socketpair failed: %s\n", strerror (errno));
+      return _assuan_error (ASSUAN_General_Error);
+    }
+  
+  err = _assuan_new_context (ctx);
+  if (err)
+    {
+      close (fds[0]);
+      close (fds[1]);
+      return err;
+    }
+  (*ctx)->pipe_mode = 1;
+  (*ctx)->inbound.fd  = fds[0]; 
+  (*ctx)->outbound.fd = fds[0]; 
+  _assuan_init_uds_io (*ctx);
+  (*ctx)->deinit_handler = _assuan_uds_deinit;
+  (*ctx)->finish_handler = do_finish;
+
+  (*ctx)->pid = fork ();
+  if ((*ctx)->pid < 0)
+    {
+      close (fds[0]);
+      close (fds[1]);
+      _assuan_release_context (*ctx); 
+      *ctx = NULL;
+      return _assuan_error (ASSUAN_General_Error);
+    }
+
+  if ((*ctx)->pid == 0)
+    {
+#ifdef _ASSUAN_USE_DOUBLE_FORK      
+      pid_t pid;
+
+      if ((pid = fork ()) == 0)
+#endif
+       {
+          int fd, i, n;
+          char errbuf[512];
+          int *fdp;
+          
+          if (atfork)
+            atfork (atforkvalue, 0);
+
+          /* Connect stdin and stdout to /dev/null. */
+          fd = open ("/dev/null", O_RDONLY);
+          if (fd == -1 || dup2 (fd, STDIN_FILENO) == -1)
+            {
+              _assuan_log_printf ("dup2(dev/null) failed: %s\n",
+                                  strerror (errno));
+              _exit (4);
+            }
+          fd = open ("/dev/null", O_WRONLY);
+          if (fd == -1 || dup2 (fd, STDOUT_FILENO) == -1)
+            {
+              _assuan_log_printf ("dup2(dev/null) failed: %s\n",
+                                  strerror (errno));
+              _exit (4);
+            }
+
+          /* Dup stderr to /dev/null unless it is in the list of FDs to be
+             passed to the child. */
+          fdp = fd_child_list;
+          if (fdp)
+            {
+              for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
+                ;
+            }
+          if (!fdp || *fdp == -1)
+            {
+              fd = open ("/dev/null", O_WRONLY);
+              if (fd == -1 || dup2 (fd, STDERR_FILENO) == -1)
+                {
+                  _assuan_log_printf ("dup2(dev/null) failed: %s\n",
+                                      strerror (errno));
+                  _exit (4);
+                }
+            }
+
+
+          /* Close all files which will not be duped, are not in the
+             fd_child_list and are not the connection fd. */
+          n = sysconf (_SC_OPEN_MAX);
+          if (n < 0)
+            n = MAX_OPEN_FDS;
+          for (i=0; i < n; i++)
+            {
+              if ( i == STDIN_FILENO || i == STDOUT_FILENO
+                   || i == STDERR_FILENO || i == fds[1])
+                continue;
+              fdp = fd_child_list;
+              if (fdp)
+                {
+                  while (*fdp != -1 && *fdp != i)
+                    fdp++;
+                }
+
+              if (!(fdp && *fdp != -1))
+                close(i);
+            }
+          errno = 0;
+
+          /* We store our parents pid in the environment so that the
+             execed assuan server is able to read the actual pid of the
+             client.  The server can't use getppid becuase it might have
+             been double forked before the assuan server has been
+             initialized. */
+          setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
+
+          /* Now set the environment variable used to convey the
+             connection's file descriptor. */
+          sprintf (mypidstr, "%d", fds[1]);
+          if (setenv ("_assuan_connection_fd", mypidstr, 1))
+            {
+              _assuan_log_printf ("setenv failed: %s\n", strerror (errno));
+              _exit (4);
+            }
+
+          if (!name && !argv)
+            {
+              /* No name and no args given, thus we don't do an exec
+                 but continue the forked process.  */
+              _assuan_release_context (*ctx);
+              *ctx = NULL;
+              return 0;
+            }
+
+          execv (name, (char *const *) argv); 
+          /* oops - use the pipe to tell the parent about it */
+          snprintf (errbuf, sizeof(errbuf)-1,
+                    "ERR %d can't exec `%s': %.50s\n",
+                    _assuan_error (ASSUAN_Problem_Starting_Server),
+                    name, strerror (errno));
+          errbuf[sizeof(errbuf)-1] = 0;
+          writen (fds[1], errbuf, strlen (errbuf));
+          _exit (4);
+        }
+#ifdef _ASSUAN_USE_DOUBLE_FORK
+      if (pid == -1)
+       _exit (1);
+      else
+       _exit (0);
+#endif
+    }
+
+
+#ifdef _ASSUAN_USE_DOUBLE_FORK
+  _assuan_waitpid ((*ctx)->pid, NULL, 0);
+  (*ctx)->pid = -1;
+#endif
+
+  close (fds[1]);
+  
+  return initial_handshake (ctx);
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+
+
+#ifdef _ASSUAN_IN_GPGME_BUILD_ASSUAN
+
+#define pipe_connect pipe_connect_gpgme
+
+/* W32 version of the pipe connection code. */
+static assuan_error_t
+pipe_connect_gpgme (assuan_context_t *ctx,
+                   const char *name, const char *const argv[],
+                   int *fd_child_list,
+                   void (*atfork) (void *opaque, int reserved),
+                   void *atforkvalue, unsigned int flags)
+{
+  assuan_error_t err;
+  int res;
+  int idx;
+  int nr;
+  int rp[2];
+  int wp[2];
+  char mypidstr[50];
+  struct spawn_fd_item_s *child_fds;
+
+  if (!ctx || !name || !argv || !argv[0])
+    return _assuan_error (ASSUAN_Invalid_Value);
+
+  /* stdin, stdout, terminating -1 */
+  nr = 3;
+  for (idx = 0; fd_child_list[idx] != -1; idx++)
+    nr++;
+
+  child_fds = calloc (nr, sizeof *child_fds);
+  if (! child_fds)
+    return _assuan_error (ASSUAN_Out_Of_Core);
+
+  /* Actually, GPGME does this for us.  But we plan to reuse this code
+     in the generic assuan.  */
+  fix_signals ();
+
+  sprintf (mypidstr, "%lu", (unsigned long)getpid ());
+
+  /* Create the two pipes. */
+  if (_gpgme_io_pipe (rp, 1))
+    return _assuan_error (ASSUAN_General_Error);
+  
+  if (_gpgme_io_pipe (wp, 0))
     {
-      int nwritten = write (fd, buffer, length);
-      
-      if (nwritten < 0)
-        {
-          if (errno == EINTR)
-            continue;
-          return -1; /* write error */
-        }
-      length -= nwritten;
-      buffer += nwritten;
+      _gpgme_io_close (rp[0]);
+      _gpgme_io_close (rp[1]);
+      return _assuan_error (ASSUAN_General_Error);
     }
-  return 0;  /* okay */
-}
-#endif
 
-static int
-do_finish (assuan_context_t ctx)
-{
-  if (ctx->inbound.fd != -1)
+  err = _assuan_new_context (ctx);
+  if (err)
     {
-      _assuan_close (ctx->inbound.fd);
-      ctx->inbound.fd = -1;
+      _gpgme_io_close (rp[0]);
+      _gpgme_io_close (rp[1]);
+      _gpgme_io_close (wp[0]);
+      _gpgme_io_close (wp[1]);
+      return _assuan_error (ASSUAN_General_Error);
+    }
+
+  (*ctx)->pipe_mode = 1;
+  (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
+  (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
+  (*ctx)->deinit_handler = do_deinit;
+  (*ctx)->finish_handler = do_finish;
+
+  /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env
+     variable to mypidstr.  However this requires us to write a full
+     environment handler, because the strings are expected in sorted
+     order.  The suggestion given in the MS Reference Library, to save
+     the old value, changeit, create proces and restore it, is not
+     thread safe.  */
+
+  nr = 0;
+  /* Server stdout is its write end of our read pipe.  */
+  child_fds[nr].fd = rp[1];
+  child_fds[nr].dup_to = 1;
+  nr++;
+  /* Server stdin is its read end of our write pipe.  */
+  child_fds[nr].fd = wp[0];
+  child_fds[nr].dup_to = 0;
+  nr++;
+
+  for (idx = 0; fd_child_list[idx] != -1; idx++)
+    {
+      child_fds[nr].fd = fd_child_list[idx];
+      child_fds[nr].dup_to = -1;
+      nr++;
     }
-  if (ctx->outbound.fd != -1)
+
+  child_fds[nr].fd = -1;
+  child_fds[nr].dup_to = -1;
+
+  /* Start the process.  */
+  res = _gpgme_io_spawn (name, (char *const *) argv, child_fds, NULL);
+  if (res == -1)
     {
-      _assuan_close (ctx->outbound.fd);
-      ctx->outbound.fd = -1;
+      _assuan_log_printf ("CreateProcess failed: %s\n", strerror (errno));
+      _gpgme_io_close (rp[0]);
+      _gpgme_io_close (rp[1]);
+      _gpgme_io_close (wp[0]);
+      _gpgme_io_close (wp[1]);
+      return _assuan_error (ASSUAN_General_Error);
     }
-  if (ctx->pid != -1 && ctx->pid)
+  else
     {
-#ifndef HAVE_W32_SYSTEM
-#ifndef _ASSUAN_USE_DOUBLE_FORK
-      if (!ctx->flags.no_waitpid)
-        waitpid (ctx->pid, NULL, 0); 
-      ctx->pid = -1;
-#endif
-#endif /*!HAVE_W32_SYSTEM*/
+      /* For W32, the user needs to know the server-local names of the
+        inherited handles.  Return them here.  */
+      for (idx = 0; fd_child_list[idx] != -1; idx++)
+       /* We add 2 to skip over the stdin/stdout pair.  */
+       fd_child_list[idx] = child_fds[idx + 2].peer_name;
     }
-  return 0;
-}
 
-static void
-do_deinit (assuan_context_t ctx)
-{
-  do_finish (ctx);
-}
+  (*ctx)->pid = 0;  /* We don't use the PID. */
 
+  return initial_handshake (ctx);
+}
 
+#else
 #ifdef HAVE_W32_SYSTEM
 /* Build a command line for use with W32's CreateProcess.  On success
    CMDLINE gets the address of a newly allocated string.  */
 static int
-build_w32_commandline (char * const *argv, char **cmdline)
+build_w32_commandline (const char * const *argv, char **cmdline)
 {
   int i, n;
   const char *s;
@@ -194,7 +740,7 @@ build_w32_commandline (char * const *argv, char **cmdline)
 #ifdef HAVE_W32_SYSTEM
 /* Create pipe where one end end is inheritable.  */
 static int
-create_inheritable_pipe (int filedes[2], int for_write)
+create_inheritable_pipe (assuan_fd_t filedes[2], int for_write)
 {
   HANDLE r, w, h;
   SECURITY_ATTRIBUTES sec_attr;
@@ -229,31 +775,26 @@ create_inheritable_pipe (int filedes[2], int for_write)
       w = h;
     }
 
-  filedes[0] = handle_to_fd (r);
-  filedes[1] = handle_to_fd (w);
+  filedes[0] = r;
+  filedes[1] = w;
   return 0;
 }
 #endif /*HAVE_W32_SYSTEM*/
 
 
-/* Connect to a server over a pipe, creating the assuan context and
-   returning it in CTX.  The server filename is NAME, the argument
-   vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file
-   descriptors not to close in the child.  ATFORK is called in the
-   child right after the fork; ATFORKVALUE is passed as the first
-   argument and 0 is passed as the second argument. The ATFORK
-   function should only act if the second value is 0. */
-assuan_error_t
-assuan_pipe_connect2 (assuan_context_t *ctx,
-                      const char *name, char *const argv[],
-                      int *fd_child_list,
-                      void (*atfork) (void *opaque, int reserved),
-                      void *atforkvalue)
-{
 #ifdef HAVE_W32_SYSTEM
+#define pipe_connect pipe_connect_w32
+/* W32 version of the pipe connection code. */
+static assuan_error_t
+pipe_connect_w32 (assuan_context_t *ctx,
+                  const char *name, const char *const argv[],
+                  int *fd_child_list,
+                  void (*atfork) (void *opaque, int reserved),
+                  void *atforkvalue, unsigned int flags)
+{
   assuan_error_t err;
-  int rp[2];
-  int wp[2];
+  assuan_fd_t rp[2];
+  assuan_fd_t wp[2];
   char mypidstr[50];
   char *cmdline;
   SECURITY_ATTRIBUTES sec_attr;
@@ -269,7 +810,7 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
   HANDLE nullfd = INVALID_HANDLE_VALUE;
 
   if (!ctx || !name || !argv || !argv[0])
-    return ASSUAN_Invalid_Value;
+    return _assuan_error (ASSUAN_Invalid_Value);
 
   fix_signals ();
 
@@ -277,33 +818,33 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
 
   /* Build the command line.  */
   if (build_w32_commandline (argv, &cmdline))
-    return ASSUAN_Out_Of_Core;
+    return _assuan_error (ASSUAN_Out_Of_Core);
 
   /* Create thew two pipes. */
   if (create_inheritable_pipe (rp, 0))
     {
       xfree (cmdline);
-      return ASSUAN_General_Error;
+      return _assuan_error (ASSUAN_General_Error);
     }
   
   if (create_inheritable_pipe (wp, 1))
     {
-      CloseHandle (fd_to_handle (rp[0]));
-      CloseHandle (fd_to_handle (rp[1]));
+      CloseHandle (rp[0]);
+      CloseHandle (rp[1]);
       xfree (cmdline);
-      return ASSUAN_General_Error;
+      return _assuan_error (ASSUAN_General_Error);
     }
 
   
   err = _assuan_new_context (ctx);
   if (err)
     {
-      CloseHandle (fd_to_handle (rp[0]));
-      CloseHandle (fd_to_handle (rp[1]));
-      CloseHandle (fd_to_handle (wp[0]));
-      CloseHandle (fd_to_handle (wp[1]));
+      CloseHandle (rp[0]);
+      CloseHandle (rp[1]);
+      CloseHandle (wp[0]);
+      CloseHandle (wp[1]);
       xfree (cmdline);
-      return ASSUAN_General_Error;
+      return _assuan_error (ASSUAN_General_Error);
     }
 
   (*ctx)->pipe_mode = 1;
@@ -328,8 +869,8 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
   memset (&si, 0, sizeof si);
   si.cb = sizeof (si);
   si.dwFlags = STARTF_USESTDHANDLES;
-  si.hStdInput  = fd_to_handle (wp[0]);
-  si.hStdOutput = fd_to_handle (rp[1]);
+  si.hStdInput  = wp[0];
+  si.hStdOutput = rp[1];
 
   /* Dup stderr to /dev/null unless it is in the list of FDs to be
      passed to the child. */
@@ -348,10 +889,10 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
       if (nullfd == INVALID_HANDLE_VALUE)
         {
           _assuan_log_printf ("can't open `nul': %s\n", w32_strerror (-1));
-          CloseHandle (fd_to_handle (rp[0]));
-          CloseHandle (fd_to_handle (rp[1]));
-          CloseHandle (fd_to_handle (wp[0]));
-          CloseHandle (fd_to_handle (wp[1]));
+          CloseHandle (rp[0]);
+          CloseHandle (rp[1]);
+          CloseHandle (wp[0]);
+          CloseHandle (wp[1]);
           xfree (cmdline);
           _assuan_release_context (*ctx); 
           return -1;
@@ -359,7 +900,7 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
       si.hStdError = nullfd;
     }
   else
-    si.hStdError = fd_to_handle (_get_osfhandle (fd));
+    si.hStdError = (void*)_get_osfhandle (fd);
 
 
   /* Note: We inherit all handles flagged as inheritable.  This seems
@@ -373,6 +914,7 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
                       &sec_attr,            /* Thread security attributes.  */
                       TRUE,                 /* Inherit handles.  */
                       (CREATE_DEFAULT_ERROR_MODE
+                       | ((flags & 128)? DETACHED_PROCESS : 0)
                        | GetPriorityClass (GetCurrentProcess ())
                        | CREATE_SUSPENDED), /* Creation flags.  */
                       NULL,                 /* Environment.  */
@@ -382,15 +924,15 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
                       ))
     {
       _assuan_log_printf ("CreateProcess failed: %s\n", w32_strerror (-1));
-      CloseHandle (fd_to_handle (rp[0]));
-      CloseHandle (fd_to_handle (rp[1]));
-      CloseHandle (fd_to_handle (wp[0]));
-      CloseHandle (fd_to_handle (wp[1]));
+      CloseHandle (rp[0]);
+      CloseHandle (rp[1]);
+      CloseHandle (wp[0]);
+      CloseHandle (wp[1]);
       if (nullfd != INVALID_HANDLE_VALUE)
         CloseHandle (nullfd);
       xfree (cmdline);
       _assuan_release_context (*ctx); 
-      return ASSUAN_General_Error;
+      return _assuan_error (ASSUAN_General_Error);
     }
   xfree (cmdline);
   cmdline = NULL;
@@ -400,8 +942,8 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
       nullfd = INVALID_HANDLE_VALUE;
     }
 
-  CloseHandle (fd_to_handle (rp[1]));
-  CloseHandle (fd_to_handle (wp[0]));
+  CloseHandle (rp[1]);
+  CloseHandle (wp[0]);
 
   /*   _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */
   /*                       " dwProcessID=%d dwThreadId=%d\n", */
@@ -410,210 +952,84 @@ assuan_pipe_connect2 (assuan_context_t *ctx,
 
   ResumeThread (pi.hThread);
   CloseHandle (pi.hThread); 
-  (*ctx)->pid = 0;  /* We don't use the PID. */
-  CloseHandle (pi.hProcess); /* We don't need to wait for the process. */
-
-#else /*!HAVE_W32_SYSTEM*/
-  assuan_error_t err;
-  int rp[2];
-  int wp[2];
-  char mypidstr[50];
-
-  if (!ctx || !name || !argv || !argv[0])
-    return ASSUAN_Invalid_Value;
-
-  fix_signals ();
-
-  sprintf (mypidstr, "%lu", (unsigned long)getpid ());
-
-  if (pipe (rp) < 0)
-    return ASSUAN_General_Error;
-
-  if (pipe (wp) < 0)
-    {
-      close (rp[0]);
-      close (rp[1]);
-      return ASSUAN_General_Error;
-    }
-  
-  err = _assuan_new_context (ctx);
-  if (err)
-    {
-      close (rp[0]);
-      close (rp[1]);
-      close (wp[0]);
-      close (wp[1]);
-      return err;
-    }
-  (*ctx)->pipe_mode = 1;
-  (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
-  (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
-  (*ctx)->deinit_handler = do_deinit;
-  (*ctx)->finish_handler = do_finish;
-
-  /* FIXME: For GPGME we should better use _gpgme_io_spawn.  The PID
-     stored here is actually soon useless.  */
-  (*ctx)->pid = fork ();
-  if ((*ctx)->pid < 0)
-    {
-      close (rp[0]);
-      close (rp[1]);
-      close (wp[0]);
-      close (wp[1]);
-      _assuan_release_context (*ctx); 
-      return ASSUAN_General_Error;
-    }
-
-  if ((*ctx)->pid == 0)
-    {
-#ifdef _ASSUAN_USE_DOUBLE_FORK      
-      pid_t pid;
-
-      if ((pid = fork ()) == 0)
-#endif
-       {
-          int i, n;
-          char errbuf[512];
-          int *fdp;
-          
-          if (atfork)
-            atfork (atforkvalue, 0);
+  (*ctx)->pid = (pid_t) pi.hProcess;
 
-          /* Dup handles to stdin/stdout. */
-          if (rp[1] != STDOUT_FILENO)
-            {
-              if (dup2 (rp[1], STDOUT_FILENO) == -1)
-                {
-                  _assuan_log_printf ("dup2 failed in child: %s\n",
-                                      strerror (errno));
-                  _exit (4);
-                }
-            }
-          if (wp[0] != STDIN_FILENO)
-            {
-              if (dup2 (wp[0], STDIN_FILENO) == -1)
-                {
-                  _assuan_log_printf ("dup2 failed in child: %s\n",
-                                      strerror (errno));
-                  _exit (4);
-                }
-            }
+  return initial_handshake (ctx);
+}
+#endif /*HAVE_W32_SYSTEM*/
+#endif /* !_ASSUAN_IN_GPGME_BUILD_ASSUAN */ 
 
-          /* Dup stderr to /dev/null unless it is in the list of FDs to be
-             passed to the child. */
-          fdp = fd_child_list;
-          if (fdp)
-            {
-              for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
-                ;
-            }
-          if (!fdp || *fdp == -1)
-            {
-              int fd = open ("/dev/null", O_WRONLY);
-              if (fd == -1)
-                {
-                  _assuan_log_printf ("can't open `/dev/null': %s\n",
-                                      strerror (errno));
-                  _exit (4);
-                }
-              if (dup2 (fd, STDERR_FILENO) == -1)
-                {
-                  _assuan_log_printf ("dup2(dev/null, 2) failed: %s\n",
-                                      strerror (errno));
-                  _exit (4);
-                }
-            }
+\f
+/* Connect to a server over a pipe, creating the assuan context and
+   returning it in CTX.  The server filename is NAME, the argument
+   vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file
+   descriptors not to close in the child.  */
+assuan_error_t
+assuan_pipe_connect (assuan_context_t *ctx, const char *name,
+                    const char *const argv[], int *fd_child_list)
+{
+  return pipe_connect (ctx, name, argv, fd_child_list, NULL, NULL, 0);
+}
 
 
-          /* Close all files which will not be duped and are not in the
-             fd_child_list. */
-          n = sysconf (_SC_OPEN_MAX);
-          if (n < 0)
-            n = MAX_OPEN_FDS;
-          for (i=0; i < n; i++)
-            {
-              if ( i == STDIN_FILENO || i == STDOUT_FILENO
-                   || i == STDERR_FILENO)
-                continue;
-              fdp = fd_child_list;
-              if (fdp)
-                {
-                  while (*fdp != -1 && *fdp != i)
-                    fdp++;
-                }
 
-              if (!(fdp && *fdp != -1))
-                close(i);
-            }
-          errno = 0;
+assuan_error_t
+assuan_pipe_connect2 (assuan_context_t *ctx,
+                      const char *name, const char *const argv[],
+                      int *fd_child_list,
+                      void (*atfork) (void *opaque, int reserved),
+                      void *atforkvalue)
+{
+  return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue, 0);
+}
 
-          /* We store our parents pid in the environment so that the
-             execed assuan server is able to read the actual pid of the
-             client.  The server can't use getppid becuase it might have
-             been double forked before the assuan server has been
-             initialized. */
-          setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
 
-          execv (name, argv); 
-          /* oops - use the pipe to tell the parent about it */
-          snprintf (errbuf, sizeof(errbuf)-1,
-                    "ERR %d can't exec `%s': %.50s\n",
-                    ASSUAN_Problem_Starting_Server, name, strerror (errno));
-          errbuf[sizeof(errbuf)-1] = 0;
-          writen (1, errbuf, strlen (errbuf));
-          _exit (4);
-        }
-#ifdef _ASSUAN_USE_DOUBLE_FORK
-      if (pid == -1)
-       _exit (1);
-      else
-       _exit (0);
-#endif
-    }
+/* Connect to a server over a full-duplex socket (i.e. created by
+   socketpair), creating the assuan context and returning it in CTX.
+   The server filename is NAME, the argument vector in ARGV.
+   FD_CHILD_LIST is a -1 terminated list of file descriptors not to
+   close in the child.  ATFORK is called in the child right after the
+   fork; ATFORKVALUE is passed as the first argument and 0 is passed
+   as the second argument. The ATFORK function should only act if the
+   second value is 0.
 
-#ifdef _ASSUAN_USE_DOUBLE_FORK
-  waitpid ((*ctx)->pid, NULL, 0);
-  (*ctx)->pid = -1;
-#endif
+   FLAGS is a bit vector and controls how the function acts:
+   Bit 0: If cleared a simple pipe based server is expected and the
+          function behaves similar to `assuan_pipe_connect'.
 
-  close (rp[1]);
-  close (wp[0]);
+          If set a server based on full-duplex pipes is expected. Such
+          pipes are usually created using the `socketpair' function.
+          It also enables features only available with such servers.
 
-#endif /*!HAVE_W32_SYSTEM*/
+   Bit 7: If set and there is a need to start ther server it will be
+          started as a background process.  This flag is useful under
+          W32 systems, so that no new console is created and pops up a
+          console window when starting the server
 
-  /* initial handshake */
-  {
-    int okay, off;
-
-    err = _assuan_read_from_server (*ctx, &okay, &off);
-    if (err)
-      _assuan_log_printf ("can't connect server: %s\n",
-                          assuan_strerror (err));
-    else if (okay != 1)
-      {
-       _assuan_log_printf ("can't connect server: `%s'\n",
-                            (*ctx)->inbound.line);
-       err = ASSUAN_Connect_Failed;
-      }
-  }
 
-  if (err)
+   If NAME as well as ARGV are NULL, no exec is done but the same
+   process is continued.  However all file descriptors are closed and
+   some special environment variables are set. To let the caller
+   detect whether the child or the parent continues, the child returns
+   a CTX of NULL. */
+assuan_error_t
+assuan_pipe_connect_ext (assuan_context_t *ctx,
+                         const char *name, const char *const argv[],
+                         int *fd_child_list,
+                         void (*atfork) (void *opaque, int reserved),
+                         void *atforkvalue, unsigned int flags)
+{
+  if ((flags & 1))
     {
-      assuan_disconnect (*ctx);
-      *ctx = NULL;
+#ifdef HAVE_W32_SYSTEM
+      return _assuan_error (ASSUAN_Not_Implemented);
+#else
+      return socketpair_connect (ctx, name, argv, fd_child_list,
+                                 atfork, atforkvalue);
+#endif
     }
-
-  return err;
+  else
+    return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue,
+                         flags);
 }
 
-
-/* Connect to a server over a pipe, creating the assuan context and
-   returning it in CTX.  The server filename is NAME, the argument
-   vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file
-   descriptors not to close in the child.  */
-assuan_error_t
-assuan_pipe_connect (assuan_context_t *ctx, const char *name, char *const argv[],
-                    int *fd_child_list)
-{
-  return assuan_pipe_connect2 (ctx, name, argv, fd_child_list, NULL, NULL);
-}