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