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