e35553259d39433730bac35dc8a1ea37da5d8f08
[gpgme.git] / assuan / assuan-pipe-connect.c
1 /* assuan-pipe-connect.c - Establish a pipe connection (client) 
2  * Copyright (C) 2001, 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
3  *
4  * This file is part of Assuan.
5  *
6  * Assuan is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Assuan is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA. 
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <signal.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <sys/types.h>
34 #ifndef HAVE_W32_SYSTEM
35 #include <sys/wait.h>
36 #else
37 #include <windows.h>
38 #endif
39
40 #include "assuan-defs.h"
41
42 #ifdef _ASSUAN_IN_GPGME_BUILD_ASSUAN
43
44 /* From GPGME priv-io.h  */
45 struct spawn_fd_item_s
46 {
47   int fd;
48   int dup_to;
49   int peer_name;
50   int arg_loc;
51 };
52
53
54 int _gpgme_io_pipe (int filedes[2], int inherit_idx);
55 int _gpgme_io_spawn (const char *path, char *const argv[],
56                      struct spawn_fd_item_s *fd_list, pid_t *r_pid);
57 #endif
58
59 /* Hacks for Slowaris.  */
60 #ifndef PF_LOCAL
61 # ifdef PF_UNIX
62 #  define PF_LOCAL PF_UNIX
63 # else
64 #  define PF_LOCAL AF_UNIX
65 # endif
66 #endif
67 #ifndef AF_LOCAL
68 # define AF_LOCAL AF_UNIX
69 #endif
70
71
72 #ifdef _POSIX_OPEN_MAX
73 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
74 #else
75 #define MAX_OPEN_FDS 20
76 #endif
77
78 #ifdef HAVE_W32_SYSTEM
79 /* We assume that a HANDLE can be represented by an int which should
80    be true for all i386 systems (HANDLE is defined as void *) and
81    these are the only systems for which Windows is available.  Further
82    we assume that -1 denotes an invalid handle.  */
83 #define fd_to_handle(a)  ((HANDLE)(a))
84 #define handle_to_fd(a)  ((int)(a))
85 #define pid_to_handle(a) ((HANDLE)(a))
86 #define handle_to_pid(a) ((int)(a))
87 #endif /*HAVE_W32_SYSTEM*/
88
89
90 /* This should be called to make sure that SIGPIPE gets ignored.  */
91 static void
92 fix_signals (void)
93 {
94 #ifndef _ASSUAN_NO_FIXED_SIGNALS
95 #ifndef HAVE_DOSISH_SYSTEM  /* No SIGPIPE for these systems.  */
96   static int fixed_signals;
97
98   if (!fixed_signals)
99     { 
100       struct sigaction act;
101         
102       sigaction (SIGPIPE, NULL, &act);
103       if (act.sa_handler == SIG_DFL)
104         {
105           act.sa_handler = SIG_IGN;
106           sigemptyset (&act.sa_mask);
107           act.sa_flags = 0;
108           sigaction (SIGPIPE, &act, NULL);
109         }
110       fixed_signals = 1;
111       /* FIXME: This is not MT safe */
112     }
113 #endif /*HAVE_DOSISH_SYSTEM*/
114 #endif /*!_ASSUAN_NO_FIXED_SIGNALS*/
115 }
116
117
118 #ifndef HAVE_W32_SYSTEM
119 static int
120 writen (int fd, const char *buffer, size_t length)
121 {
122   while (length)
123     {
124       int nwritten = write (fd, buffer, length);
125       
126       if (nwritten < 0)
127         {
128           if (errno == EINTR)
129             continue;
130           return -1; /* write error */
131         }
132       length -= nwritten;
133       buffer += nwritten;
134     }
135   return 0;  /* okay */
136 }
137 #endif
138
139 static int
140 do_finish (assuan_context_t ctx)
141 {
142   if (ctx->inbound.fd != -1)
143     {
144       _assuan_close (ctx->inbound.fd);
145       if (ctx->inbound.fd == ctx->outbound.fd)
146         ctx->outbound.fd = -1;
147       ctx->inbound.fd = -1;
148     }
149   if (ctx->outbound.fd != -1)
150     {
151       _assuan_close (ctx->outbound.fd);
152       ctx->outbound.fd = -1;
153     }
154   if (ctx->pid != -1 && ctx->pid)
155     {
156 #ifndef HAVE_W32_SYSTEM
157 #ifndef _ASSUAN_USE_DOUBLE_FORK
158       if (!ctx->flags.no_waitpid)
159         _assuan_waitpid (ctx->pid, NULL, 0); 
160       ctx->pid = -1;
161 #endif
162 #endif /*!HAVE_W32_SYSTEM*/
163     }
164   return 0;
165 }
166
167 static void
168 do_deinit (assuan_context_t ctx)
169 {
170   do_finish (ctx);
171 }
172
173
174 /* Helper for pipe_connect. */
175 static assuan_error_t
176 initial_handshake (assuan_context_t *ctx)
177 {
178   int okay, off;
179   assuan_error_t err;
180   
181   err = _assuan_read_from_server (*ctx, &okay, &off);
182   if (err)
183     _assuan_log_printf ("can't connect server: %s\n",
184                         assuan_strerror (err));
185   else if (okay != 1)
186     {
187       _assuan_log_printf ("can't connect server: `%s'\n",
188                           (*ctx)->inbound.line);
189       err = _assuan_error (ASSUAN_Connect_Failed);
190     }
191
192   if (err)
193     {
194       assuan_disconnect (*ctx);
195       *ctx = NULL;
196     }
197   return err;
198 }
199
200
201 #ifndef _ASSUAN_IN_GPGME_BUILD_ASSUAN
202
203 #ifndef HAVE_W32_SYSTEM
204 #define pipe_connect pipe_connect_unix
205 /* Unix version of the pipe connection code.  We use an extra macro to
206    make ChangeLog entries easier. */
207 static assuan_error_t
208 pipe_connect_unix (assuan_context_t *ctx,
209                    const char *name, const char *const argv[],
210                    int *fd_child_list,
211                    void (*atfork) (void *opaque, int reserved),
212                    void *atforkvalue)
213 {
214   assuan_error_t err;
215   int rp[2];
216   int wp[2];
217   char mypidstr[50];
218
219   if (!ctx || !name || !argv || !argv[0])
220     return _assuan_error (ASSUAN_Invalid_Value);
221
222   fix_signals ();
223
224   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
225
226   if (pipe (rp) < 0)
227     return _assuan_error (ASSUAN_General_Error);
228   
229   if (pipe (wp) < 0)
230     {
231       close (rp[0]);
232       close (rp[1]);
233       return _assuan_error (ASSUAN_General_Error);
234     }
235
236   err = _assuan_new_context (ctx);
237   if (err)
238     {
239       close (rp[0]);
240       close (rp[1]);
241       close (wp[0]);
242       close (wp[1]);
243       return err;
244     }
245   (*ctx)->pipe_mode = 1;
246   (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
247   (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
248   (*ctx)->deinit_handler = do_deinit;
249   (*ctx)->finish_handler = do_finish;
250
251   /* FIXME: For GPGME we should better use _gpgme_io_spawn.  The PID
252      stored here is actually soon useless.  */
253   (*ctx)->pid = fork ();
254   if ((*ctx)->pid < 0)
255     {
256       close (rp[0]);
257       close (rp[1]);
258       close (wp[0]);
259       close (wp[1]);
260       _assuan_release_context (*ctx); 
261       return _assuan_error (ASSUAN_General_Error);
262     }
263
264   if ((*ctx)->pid == 0)
265     {
266 #ifdef _ASSUAN_USE_DOUBLE_FORK      
267       pid_t pid;
268
269       if ((pid = fork ()) == 0)
270 #endif
271         {
272           int i, n;
273           char errbuf[512];
274           int *fdp;
275           
276           if (atfork)
277             atfork (atforkvalue, 0);
278
279           /* Dup handles to stdin/stdout. */
280           if (rp[1] != STDOUT_FILENO)
281             {
282               if (dup2 (rp[1], STDOUT_FILENO) == -1)
283                 {
284                   _assuan_log_printf ("dup2 failed in child: %s\n",
285                                       strerror (errno));
286                   _exit (4);
287                 }
288             }
289           if (wp[0] != STDIN_FILENO)
290             {
291               if (dup2 (wp[0], STDIN_FILENO) == -1)
292                 {
293                   _assuan_log_printf ("dup2 failed in child: %s\n",
294                                       strerror (errno));
295                   _exit (4);
296                 }
297             }
298
299           /* Dup stderr to /dev/null unless it is in the list of FDs to be
300              passed to the child. */
301           fdp = fd_child_list;
302           if (fdp)
303             {
304               for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
305                 ;
306             }
307           if (!fdp || *fdp == -1)
308             {
309               int fd = open ("/dev/null", O_WRONLY);
310               if (fd == -1)
311                 {
312                   _assuan_log_printf ("can't open `/dev/null': %s\n",
313                                       strerror (errno));
314                   _exit (4);
315                 }
316               if (dup2 (fd, STDERR_FILENO) == -1)
317                 {
318                   _assuan_log_printf ("dup2(dev/null, 2) failed: %s\n",
319                                       strerror (errno));
320                   _exit (4);
321                 }
322             }
323
324
325           /* Close all files which will not be duped and are not in the
326              fd_child_list. */
327           n = sysconf (_SC_OPEN_MAX);
328           if (n < 0)
329             n = MAX_OPEN_FDS;
330           for (i=0; i < n; i++)
331             {
332               if ( i == STDIN_FILENO || i == STDOUT_FILENO
333                    || i == STDERR_FILENO)
334                 continue;
335               fdp = fd_child_list;
336               if (fdp)
337                 {
338                   while (*fdp != -1 && *fdp != i)
339                     fdp++;
340                 }
341
342               if (!(fdp && *fdp != -1))
343                 close(i);
344             }
345           errno = 0;
346
347           /* We store our parents pid in the environment so that the
348              execed assuan server is able to read the actual pid of the
349              client.  The server can't use getppid because it might have
350              been double forked before the assuan server has been
351              initialized. */
352           setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
353
354           /* Make sure that we never pass a connection fd variable
355              when using a simple pipe.  */
356           unsetenv ("_assuan_connection_fd");
357
358           execv (name, (char *const *) argv); 
359           /* oops - use the pipe to tell the parent about it */
360           snprintf (errbuf, sizeof(errbuf)-1,
361                     "ERR %d can't exec `%s': %.50s\n",
362                     _assuan_error (ASSUAN_Problem_Starting_Server),
363                     name, strerror (errno));
364           errbuf[sizeof(errbuf)-1] = 0;
365           writen (1, errbuf, strlen (errbuf));
366           _exit (4);
367         }
368 #ifdef _ASSUAN_USE_DOUBLE_FORK
369       if (pid == -1)
370         _exit (1);
371       else
372         _exit (0);
373 #endif
374     }
375
376 #ifdef _ASSUAN_USE_DOUBLE_FORK
377   _assuan_waitpid ((*ctx)->pid, NULL, 0);
378   (*ctx)->pid = -1;
379 #endif
380
381   close (rp[1]);
382   close (wp[0]);
383
384   return initial_handshake (ctx);
385 }
386 #endif /*!HAVE_W32_SYSTEM*/
387 #endif /* _ASSUAN_IN_GPGME_BUILD_ASSUAN */
388
389
390 #ifndef HAVE_W32_SYSTEM
391 /* This function is similar to pipe_connect but uses a socketpair and
392    sets the I/O up to use sendmsg/recvmsg. */
393 static assuan_error_t
394 socketpair_connect (assuan_context_t *ctx,
395                     const char *name, const char *const argv[],
396                     int *fd_child_list,
397                     void (*atfork) (void *opaque, int reserved),
398                     void *atforkvalue)
399 {
400   assuan_error_t err;
401   int fds[2];
402   char mypidstr[50];
403
404   if (!ctx
405       || (name && (!argv || !argv[0]))
406       || (!name && argv))
407     return _assuan_error (ASSUAN_Invalid_Value);
408
409   fix_signals ();
410
411   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
412
413   if ( socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) )
414     {
415       _assuan_log_printf ("socketpair failed: %s\n", strerror (errno));
416       return _assuan_error (ASSUAN_General_Error);
417     }
418   
419   err = _assuan_new_context (ctx);
420   if (err)
421     {
422       close (fds[0]);
423       close (fds[1]);
424       return err;
425     }
426   (*ctx)->pipe_mode = 1;
427   (*ctx)->inbound.fd  = fds[0]; 
428   (*ctx)->outbound.fd = fds[0]; 
429   _assuan_init_uds_io (*ctx);
430   (*ctx)->deinit_handler = _assuan_uds_deinit;
431   (*ctx)->finish_handler = do_finish;
432
433   (*ctx)->pid = fork ();
434   if ((*ctx)->pid < 0)
435     {
436       close (fds[0]);
437       close (fds[1]);
438       _assuan_release_context (*ctx); 
439       *ctx = NULL;
440       return _assuan_error (ASSUAN_General_Error);
441     }
442
443   if ((*ctx)->pid == 0)
444     {
445 #ifdef _ASSUAN_USE_DOUBLE_FORK      
446       pid_t pid;
447
448       if ((pid = fork ()) == 0)
449 #endif
450         {
451           int fd, i, n;
452           char errbuf[512];
453           int *fdp;
454           
455           if (atfork)
456             atfork (atforkvalue, 0);
457
458           /* Connect stdin and stdout to /dev/null. */
459           fd = open ("/dev/null", O_RDONLY);
460           if (fd == -1 || dup2 (fd, STDIN_FILENO) == -1)
461             {
462               _assuan_log_printf ("dup2(dev/null) failed: %s\n",
463                                   strerror (errno));
464               _exit (4);
465             }
466           fd = open ("/dev/null", O_WRONLY);
467           if (fd == -1 || dup2 (fd, STDOUT_FILENO) == -1)
468             {
469               _assuan_log_printf ("dup2(dev/null) failed: %s\n",
470                                   strerror (errno));
471               _exit (4);
472             }
473
474           /* Dup stderr to /dev/null unless it is in the list of FDs to be
475              passed to the child. */
476           fdp = fd_child_list;
477           if (fdp)
478             {
479               for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
480                 ;
481             }
482           if (!fdp || *fdp == -1)
483             {
484               fd = open ("/dev/null", O_WRONLY);
485               if (fd == -1 || dup2 (fd, STDERR_FILENO) == -1)
486                 {
487                   _assuan_log_printf ("dup2(dev/null) failed: %s\n",
488                                       strerror (errno));
489                   _exit (4);
490                 }
491             }
492
493
494           /* Close all files which will not be duped, are not in the
495              fd_child_list and are not the connection fd. */
496           n = sysconf (_SC_OPEN_MAX);
497           if (n < 0)
498             n = MAX_OPEN_FDS;
499           for (i=0; i < n; i++)
500             {
501               if ( i == STDIN_FILENO || i == STDOUT_FILENO
502                    || i == STDERR_FILENO || i == fds[1])
503                 continue;
504               fdp = fd_child_list;
505               if (fdp)
506                 {
507                   while (*fdp != -1 && *fdp != i)
508                     fdp++;
509                 }
510
511               if (!(fdp && *fdp != -1))
512                 close(i);
513             }
514           errno = 0;
515
516           /* We store our parents pid in the environment so that the
517              execed assuan server is able to read the actual pid of the
518              client.  The server can't use getppid becuase it might have
519              been double forked before the assuan server has been
520              initialized. */
521           setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
522
523           /* Now set the environment variable used to convey the
524              connection's file descriptor. */
525           sprintf (mypidstr, "%d", fds[1]);
526           if (setenv ("_assuan_connection_fd", mypidstr, 1))
527             {
528               _assuan_log_printf ("setenv failed: %s\n", strerror (errno));
529               _exit (4);
530             }
531
532           if (!name && !argv)
533             {
534               /* No name and no args given, thus we don't do an exec
535                  but continue the forked process.  */
536               _assuan_release_context (*ctx);
537               *ctx = NULL;
538               return 0;
539             }
540
541           execv (name, (char *const *) argv); 
542           /* oops - use the pipe to tell the parent about it */
543           snprintf (errbuf, sizeof(errbuf)-1,
544                     "ERR %d can't exec `%s': %.50s\n",
545                     _assuan_error (ASSUAN_Problem_Starting_Server),
546                     name, strerror (errno));
547           errbuf[sizeof(errbuf)-1] = 0;
548           writen (fds[1], errbuf, strlen (errbuf));
549           _exit (4);
550         }
551 #ifdef _ASSUAN_USE_DOUBLE_FORK
552       if (pid == -1)
553         _exit (1);
554       else
555         _exit (0);
556 #endif
557     }
558
559
560 #ifdef _ASSUAN_USE_DOUBLE_FORK
561   _assuan_waitpid ((*ctx)->pid, NULL, 0);
562   (*ctx)->pid = -1;
563 #endif
564
565   close (fds[1]);
566   
567   return initial_handshake (ctx);
568 }
569 #endif /*!HAVE_W32_SYSTEM*/
570
571
572
573
574 #ifdef _ASSUAN_IN_GPGME_BUILD_ASSUAN
575
576 #define pipe_connect pipe_connect_gpgme
577
578 /* W32 version of the pipe connection code. */
579 static assuan_error_t
580 pipe_connect_gpgme (assuan_context_t *ctx,
581                     const char *name, const char *const argv[],
582                     int *fd_child_list,
583                     void (*atfork) (void *opaque, int reserved),
584                     void *atforkvalue)
585 {
586   assuan_error_t err;
587   int res;
588   int idx;
589   int nr;
590   int rp[2];
591   int wp[2];
592   char mypidstr[50];
593   struct spawn_fd_item_s *child_fds;
594
595   if (!ctx || !name || !argv || !argv[0])
596     return _assuan_error (ASSUAN_Invalid_Value);
597
598   /* stdin, stdout, terminating -1 */
599   nr = 3;
600   for (idx = 0; fd_child_list[idx] != -1; idx++)
601     nr++;
602
603   child_fds = calloc (nr, sizeof *child_fds);
604   if (! child_fds)
605     return _assuan_error (ASSUAN_Out_Of_Core);
606
607   /* Actually, GPGME does this for us.  But we plan to reuse this code
608      in the generic assuan.  */
609   fix_signals ();
610
611   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
612
613   /* Create the two pipes. */
614   if (_gpgme_io_pipe (rp, 1))
615     return _assuan_error (ASSUAN_General_Error);
616   
617   if (_gpgme_io_pipe (wp, 0))
618     {
619       _gpgme_io_close (rp[0]);
620       _gpgme_io_close (rp[1]);
621       return _assuan_error (ASSUAN_General_Error);
622     }
623
624   err = _assuan_new_context (ctx);
625   if (err)
626     {
627       _gpgme_io_close (rp[0]);
628       _gpgme_io_close (rp[1]);
629       _gpgme_io_close (wp[0]);
630       _gpgme_io_close (wp[1]);
631       return _assuan_error (ASSUAN_General_Error);
632     }
633
634   (*ctx)->pipe_mode = 1;
635   (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
636   (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
637   (*ctx)->deinit_handler = do_deinit;
638   (*ctx)->finish_handler = do_finish;
639
640   /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env
641      variable to mypidstr.  However this requires us to write a full
642      environment handler, because the strings are expected in sorted
643      order.  The suggestion given in the MS Reference Library, to save
644      the old value, changeit, create proces and restore it, is not
645      thread safe.  */
646
647   nr = 0;
648   /* Server stdout is its write end of our read pipe.  */
649   child_fds[nr].fd = rp[1];
650   child_fds[nr].dup_to = 1;
651   nr++;
652   /* Server stdin is its read end of our write pipe.  */
653   child_fds[nr].fd = wp[0];
654   child_fds[nr].dup_to = 0;
655   nr++;
656
657   for (idx = 0; fd_child_list[idx] != -1; idx++)
658     {
659       child_fds[nr].fd = fd_child_list[idx];
660       child_fds[nr].dup_to = -1;
661       nr++;
662     }
663
664   child_fds[nr].fd = -1;
665   child_fds[nr].dup_to = -1;
666
667   /* Start the process.  */
668   res = _gpgme_io_spawn (name, (char *const *) argv, child_fds, NULL);
669   if (res == -1)
670     {
671       _assuan_log_printf ("CreateProcess failed: %s\n", strerror (errno));
672       _gpgme_io_close (rp[0]);
673       _gpgme_io_close (rp[1]);
674       _gpgme_io_close (wp[0]);
675       _gpgme_io_close (wp[1]);
676       return _assuan_error (ASSUAN_General_Error);
677     }
678   else
679     {
680       /* For W32, the user needs to know the server-local names of the
681          inherited handles.  Return them here.  */
682       for (idx = 0; fd_child_list[idx] != -1; idx++)
683         /* We add 2 to skip over the stdin/stdout pair.  */
684         fd_child_list[idx] = child_fds[idx + 2].peer_name;
685     }
686
687   (*ctx)->pid = 0;  /* We don't use the PID. */
688
689   return initial_handshake (ctx);
690 }
691
692 #else
693 #ifdef HAVE_W32_SYSTEM
694 /* Build a command line for use with W32's CreateProcess.  On success
695    CMDLINE gets the address of a newly allocated string.  */
696 static int
697 build_w32_commandline (const char * const *argv, char **cmdline)
698 {
699   int i, n;
700   const char *s;
701   char *buf, *p;
702
703   *cmdline = NULL;
704   n = 0;
705   for (i=0; (s=argv[i]); i++)
706     {
707       n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
708       for (; *s; s++)
709         if (*s == '\"')
710           n++;  /* Need to double inner quotes.  */
711     }
712   n++;
713
714   buf = p = xtrymalloc (n);
715   if (!buf)
716     return -1;
717
718   for (i=0; argv[i]; i++) 
719     {
720       if (i)
721         p = stpcpy (p, " ");
722       if (!*argv[i]) /* Empty string. */
723         p = stpcpy (p, "\"\"");
724       else if (strpbrk (argv[i], " \t\n\v\f\""))
725         {
726           p = stpcpy (p, "\"");
727           for (s=argv[i]; *s; s++)
728             {
729               *p++ = *s;
730               if (*s == '\"')
731                 *p++ = *s;
732             }
733           *p++ = '\"';
734           *p = 0;
735         }
736       else
737         p = stpcpy (p, argv[i]);
738     }
739
740   *cmdline= buf;
741   return 0;
742 }
743 #endif /*HAVE_W32_SYSTEM*/
744
745
746 #ifdef HAVE_W32_SYSTEM
747 /* Create pipe where one end is inheritable.  */
748 static int
749 create_inheritable_pipe (int filedes[2], int for_write)
750 {
751   HANDLE r, w, h;
752   SECURITY_ATTRIBUTES sec_attr;
753
754   memset (&sec_attr, 0, sizeof sec_attr );
755   sec_attr.nLength = sizeof sec_attr;
756   sec_attr.bInheritHandle = FALSE;
757     
758   if (!CreatePipe (&r, &w, &sec_attr, 0))
759     {
760       _assuan_log_printf ("CreatePipe failed: %s\n", w32_strerror (-1));
761       return -1;
762     }
763
764   if (!DuplicateHandle (GetCurrentProcess(), for_write? r : w,
765                         GetCurrentProcess(), &h, 0,
766                         TRUE, DUPLICATE_SAME_ACCESS ))
767     {
768       _assuan_log_printf ("DuplicateHandle failed: %s\n", w32_strerror (-1));
769       CloseHandle (r);
770       CloseHandle (w);
771       return -1;
772     }
773   if (for_write)
774     {
775       CloseHandle (r);
776       r = h;
777     }
778   else
779     {
780       CloseHandle (w);
781       w = h;
782     }
783
784   _assuan_log_printf ("created pipe: read=%p%s, write=%p%s\n",
785                       r, for_write? " (inherit)":"",
786                       w, for_write? "":" (inherit)");
787   filedes[0] = handle_to_fd (r);
788   filedes[1] = handle_to_fd (w);
789   return 0;
790 }
791 #endif /* HAVE_W32_SYSTEM */
792
793 #ifdef HAVE_W32_SYSTEM
794 #define pipe_connect pipe_connect_w32
795 /* W32 version of the pipe connection code. */
796 static assuan_error_t
797 pipe_connect_w32 (assuan_context_t *ctx,
798                   const char *name, const char *const argv[],
799                   int *fd_child_list,
800                   void (*atfork) (void *opaque, int reserved),
801                   void *atforkvalue)
802 {
803   assuan_error_t err;
804   int rp[2];
805   int wp[2];
806   char mypidstr[50];
807   char *cmdline;
808   SECURITY_ATTRIBUTES sec_attr;
809   PROCESS_INFORMATION pi = 
810     {
811       NULL,      /* Returns process handle.  */
812       0,         /* Returns primary thread handle.  */
813       0,         /* Returns pid.  */
814       0          /* Returns tid.  */
815     };
816   STARTUPINFO si;
817   int fd, *fdp;
818   HANDLE nullfd = INVALID_HANDLE_VALUE;
819
820   if (!ctx || !name || !argv || !argv[0])
821     return _assuan_error (ASSUAN_Invalid_Value);
822
823   fix_signals ();
824
825   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
826
827   /* Build the command line.  */
828   if (build_w32_commandline (argv, &cmdline))
829     return _assuan_error (ASSUAN_Out_Of_Core);
830
831   /* Create thew two pipes. */
832   if (create_inheritable_pipe (rp, 0))
833     {
834       xfree (cmdline);
835       return _assuan_error (ASSUAN_General_Error);
836     }
837   
838   if (create_inheritable_pipe (wp, 1))
839     {
840       CloseHandle (fd_to_handle (rp[0]));
841       CloseHandle (fd_to_handle (rp[1]));
842       xfree (cmdline);
843       return _assuan_error (ASSUAN_General_Error);
844     }
845
846   
847   err = _assuan_new_context (ctx);
848   if (err)
849     {
850       CloseHandle (fd_to_handle (rp[0]));
851       CloseHandle (fd_to_handle (rp[1]));
852       CloseHandle (fd_to_handle (wp[0]));
853       CloseHandle (fd_to_handle (wp[1]));
854       xfree (cmdline);
855       return _assuan_error (ASSUAN_General_Error);
856     }
857
858   (*ctx)->pipe_mode = 1;
859   (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
860   (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
861   (*ctx)->deinit_handler = do_deinit;
862   (*ctx)->finish_handler = do_finish;
863
864
865   /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env
866      variable.  However this requires us to write a full environment
867      handler, because the strings are expected in sorted order.  The
868      suggestion given in the MS Reference Library, to save the old
869      value, changeit, create proces and restore it, is not thread
870      safe.  */
871
872   /* Start the process.  */
873   memset (&sec_attr, 0, sizeof sec_attr );
874   sec_attr.nLength = sizeof sec_attr;
875   sec_attr.bInheritHandle = FALSE;
876   
877   memset (&si, 0, sizeof si);
878   si.cb = sizeof (si);
879   si.dwFlags = STARTF_USESTDHANDLES;
880   si.hStdInput  = fd_to_handle (wp[0]);
881   si.hStdOutput = fd_to_handle (rp[1]);
882
883   /* Dup stderr to /dev/null unless it is in the list of FDs to be
884      passed to the child. */
885   fd = fileno (stderr);
886   fdp = fd_child_list;
887   if (fdp)
888     {
889       for (; *fdp != -1 && *fdp != fd; fdp++)
890         ;
891     }
892   if (!fdp || *fdp == -1)
893     {
894       nullfd = CreateFile ("nul", GENERIC_WRITE,
895                            FILE_SHARE_READ | FILE_SHARE_WRITE,
896                            NULL, OPEN_EXISTING, 0, NULL);
897       _assuan_log_printf ("created nul device, hd=%p\n", nullfd);
898       if (nullfd == INVALID_HANDLE_VALUE)
899         {
900           _assuan_log_printf ("can't open `nul': %s\n", w32_strerror (-1));
901           CloseHandle (fd_to_handle (rp[0]));
902           CloseHandle (fd_to_handle (rp[1]));
903           CloseHandle (fd_to_handle (wp[0]));
904           CloseHandle (fd_to_handle (wp[1]));
905           xfree (cmdline);
906           _assuan_release_context (*ctx); 
907           return -1;
908         }
909       si.hStdError = nullfd;
910     }
911   else
912     si.hStdError = fd_to_handle (_get_osfhandle (fd));
913
914
915   /* Note: We inherit all handles flagged as inheritable.  This seems
916      to be a security flaw but there seems to be no way of selecting
917      handles to inherit. */
918   _assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n",
919                       name, cmdline);
920   _assuan_log_printf ("        stdin=%p stdout=%p stderr=%p\n",
921                       si.hStdInput, si.hStdOutput, si.hStdError);
922   if (!CreateProcess (name,                 /* Program to start.  */
923                       cmdline,              /* Command line arguments.  */
924                       &sec_attr,            /* Process security attributes.  */
925                       &sec_attr,            /* Thread security attributes.  */
926                       TRUE,                 /* Inherit handles.  */
927                       (CREATE_DEFAULT_ERROR_MODE
928                        | DETACHED_PROCESS
929                        | GetPriorityClass (GetCurrentProcess ())
930                        | CREATE_SUSPENDED), /* Creation flags.  */
931                       NULL,                 /* Environment.  */
932                       NULL,                 /* Use current drive/directory.  */
933                       &si,                  /* Startup information. */
934                       &pi                   /* Returns process information.  */
935                       ))
936     {
937       _assuan_log_printf ("CreateProcess failed: %s\n", w32_strerror (-1));
938       CloseHandle (fd_to_handle (rp[0]));
939       CloseHandle (fd_to_handle (rp[1]));
940       CloseHandle (fd_to_handle (wp[0]));
941       CloseHandle (fd_to_handle (wp[1]));
942       if (nullfd != INVALID_HANDLE_VALUE)
943         CloseHandle (nullfd);
944       xfree (cmdline);
945       _assuan_release_context (*ctx); 
946       return _assuan_error (ASSUAN_General_Error);
947     }
948   xfree (cmdline);
949   cmdline = NULL;
950   if (nullfd != INVALID_HANDLE_VALUE)
951     {
952       CloseHandle (nullfd);
953       nullfd = INVALID_HANDLE_VALUE;
954     }
955
956   _assuan_log_printf ("closing handles %p and %p\n", 
957                       fd_to_handle (rp[1]), fd_to_handle (wp[0]) );
958   CloseHandle (fd_to_handle (rp[1]));
959   CloseHandle (fd_to_handle (wp[0]));
960
961   _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p"
962                       " dwProcessID=%d dwThreadId=%d\n",
963                       pi.hProcess, pi.hThread,
964                       (int) pi.dwProcessId, (int) pi.dwThreadId);
965
966   ResumeThread (pi.hThread);
967   CloseHandle (pi.hThread); 
968   (*ctx)->pid = 0;  /* We don't use the PID. */
969   CloseHandle (pi.hProcess); /* We don't need to wait for the process. */
970
971   return initial_handshake (ctx);
972 }
973 #endif /*HAVE_W32_SYSTEM*/
974 #endif /* !_ASSUAN_IN_GPGME_BUILD_ASSUAN */ 
975
976 \f
977 /* Connect to a server over a pipe, creating the assuan context and
978    returning it in CTX.  The server filename is NAME, the argument
979    vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file
980    descriptors not to close in the child.  */
981 assuan_error_t
982 assuan_pipe_connect (assuan_context_t *ctx, const char *name,
983                      const char *const argv[], int *fd_child_list)
984 {
985   return pipe_connect (ctx, name, argv, fd_child_list, NULL, NULL);
986 }
987
988
989
990 assuan_error_t
991 assuan_pipe_connect2 (assuan_context_t *ctx,
992                       const char *name, const char *const argv[],
993                       int *fd_child_list,
994                       void (*atfork) (void *opaque, int reserved),
995                       void *atforkvalue)
996 {
997   return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue);
998 }
999
1000
1001 /* Connect to a server over a full-duplex socket (i.e. created by
1002    socketpair), creating the assuan context and returning it in CTX.
1003    The server filename is NAME, the argument vector in ARGV.
1004    FD_CHILD_LIST is a -1 terminated list of file descriptors not to
1005    close in the child.  ATFORK is called in the child right after the
1006    fork; ATFORKVALUE is passed as the first argument and 0 is passed
1007    as the second argument. The ATFORK function should only act if the
1008    second value is 0.
1009
1010    For now FLAGS may either take the value 0 to behave like
1011    assuan_pipe_connect2 or 1 to enable the described full-duplex
1012    socket behaviour.
1013
1014    If NAME as well as ARGV are NULL, no exec is done but the same
1015    process is continued.  However all file descriptors are closed and
1016    some special environment variables are set. To let the caller
1017    detect whether the child or the parent continues, the child returns
1018    a CTX of NULL. */
1019 assuan_error_t
1020 assuan_pipe_connect_ext (assuan_context_t *ctx,
1021                          const char *name, const char *const argv[],
1022                          int *fd_child_list,
1023                          void (*atfork) (void *opaque, int reserved),
1024                          void *atforkvalue, unsigned int flags)
1025 {
1026   if ((flags & 1))
1027     {
1028 #ifdef HAVE_W32_SYSTEM
1029       return _assuan_error (ASSUAN_Not_Implemented);
1030 #else
1031       return socketpair_connect (ctx, name, argv, fd_child_list,
1032                                  atfork, atforkvalue);
1033 #endif
1034     }
1035   else
1036     return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue);
1037 }
1038