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 _ASSUAN_IN_GPGME_BUILD_ASSUAN
185
186 #ifndef HAVE_W32_SYSTEM
187 #define pipe_connect pipe_connect_unix
188 /* Unix version of the pipe connection code.  We use an extra macro to
189    make ChangeLog entries easier. */
190 static assuan_error_t
191 pipe_connect_unix (assuan_context_t *ctx,
192                    const char *name, const char *const argv[],
193                    int *fd_child_list,
194                    void (*atfork) (void *opaque, int reserved),
195                    void *atforkvalue)
196 {
197   assuan_error_t err;
198   int rp[2];
199   int wp[2];
200   char mypidstr[50];
201
202   if (!ctx || !name || !argv || !argv[0])
203     return _assuan_error (ASSUAN_Invalid_Value);
204
205   fix_signals ();
206
207   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
208
209   if (pipe (rp) < 0)
210     return _assuan_error (ASSUAN_General_Error);
211   
212   if (pipe (wp) < 0)
213     {
214       close (rp[0]);
215       close (rp[1]);
216       return _assuan_error (ASSUAN_General_Error);
217     }
218
219   err = _assuan_new_context (ctx);
220   if (err)
221     {
222       close (rp[0]);
223       close (rp[1]);
224       close (wp[0]);
225       close (wp[1]);
226       return err;
227     }
228   (*ctx)->pipe_mode = 1;
229   (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
230   (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
231   (*ctx)->deinit_handler = do_deinit;
232   (*ctx)->finish_handler = do_finish;
233
234   /* FIXME: For GPGME we should better use _gpgme_io_spawn.  The PID
235      stored here is actually soon useless.  */
236   (*ctx)->pid = fork ();
237   if ((*ctx)->pid < 0)
238     {
239       close (rp[0]);
240       close (rp[1]);
241       close (wp[0]);
242       close (wp[1]);
243       _assuan_release_context (*ctx); 
244       return _assuan_error (ASSUAN_General_Error);
245     }
246
247   if ((*ctx)->pid == 0)
248     {
249 #ifdef _ASSUAN_USE_DOUBLE_FORK      
250       pid_t pid;
251
252       if ((pid = fork ()) == 0)
253 #endif
254         {
255           int i, n;
256           char errbuf[512];
257           int *fdp;
258           
259           if (atfork)
260             atfork (atforkvalue, 0);
261
262           /* Dup handles to stdin/stdout. */
263           if (rp[1] != STDOUT_FILENO)
264             {
265               if (dup2 (rp[1], STDOUT_FILENO) == -1)
266                 {
267                   _assuan_log_printf ("dup2 failed in child: %s\n",
268                                       strerror (errno));
269                   _exit (4);
270                 }
271             }
272           if (wp[0] != STDIN_FILENO)
273             {
274               if (dup2 (wp[0], STDIN_FILENO) == -1)
275                 {
276                   _assuan_log_printf ("dup2 failed in child: %s\n",
277                                       strerror (errno));
278                   _exit (4);
279                 }
280             }
281
282           /* Dup stderr to /dev/null unless it is in the list of FDs to be
283              passed to the child. */
284           fdp = fd_child_list;
285           if (fdp)
286             {
287               for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
288                 ;
289             }
290           if (!fdp || *fdp == -1)
291             {
292               int fd = open ("/dev/null", O_WRONLY);
293               if (fd == -1)
294                 {
295                   _assuan_log_printf ("can't open `/dev/null': %s\n",
296                                       strerror (errno));
297                   _exit (4);
298                 }
299               if (dup2 (fd, STDERR_FILENO) == -1)
300                 {
301                   _assuan_log_printf ("dup2(dev/null, 2) failed: %s\n",
302                                       strerror (errno));
303                   _exit (4);
304                 }
305             }
306
307
308           /* Close all files which will not be duped and are not in the
309              fd_child_list. */
310           n = sysconf (_SC_OPEN_MAX);
311           if (n < 0)
312             n = MAX_OPEN_FDS;
313           for (i=0; i < n; i++)
314             {
315               if ( i == STDIN_FILENO || i == STDOUT_FILENO
316                    || i == STDERR_FILENO)
317                 continue;
318               fdp = fd_child_list;
319               if (fdp)
320                 {
321                   while (*fdp != -1 && *fdp != i)
322                     fdp++;
323                 }
324
325               if (!(fdp && *fdp != -1))
326                 close(i);
327             }
328           errno = 0;
329
330           /* We store our parents pid in the environment so that the
331              execed assuan server is able to read the actual pid of the
332              client.  The server can't use getppid because it might have
333              been double forked before the assuan server has been
334              initialized. */
335           setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
336
337           /* Make sure that we never pass a connection fd variable
338              when using a simple pipe.  */
339           unsetenv ("_assuan_connection_fd");
340
341           execv (name, (char *const *) argv); 
342           /* oops - use the pipe to tell the parent about it */
343           snprintf (errbuf, sizeof(errbuf)-1,
344                     "ERR %d can't exec `%s': %.50s\n",
345                     _assuan_error (ASSUAN_Problem_Starting_Server),
346                     name, strerror (errno));
347           errbuf[sizeof(errbuf)-1] = 0;
348           writen (1, errbuf, strlen (errbuf));
349           _exit (4);
350         }
351 #ifdef _ASSUAN_USE_DOUBLE_FORK
352       if (pid == -1)
353         _exit (1);
354       else
355         _exit (0);
356 #endif
357     }
358
359 #ifdef _ASSUAN_USE_DOUBLE_FORK
360   _assuan_waitpid ((*ctx)->pid, NULL, 0);
361   (*ctx)->pid = -1;
362 #endif
363
364   close (rp[1]);
365   close (wp[0]);
366
367   return initial_handshake (ctx);
368 }
369 #endif /*!HAVE_W32_SYSTEM*/
370 #endif /* _ASSUAN_IN_GPGME_BUILD_ASSUAN */
371
372
373 #ifndef HAVE_W32_SYSTEM
374 /* This function is similar to pipe_connect but uses a socketpair and
375    sets the I/O up to use sendmsg/recvmsg. */
376 static assuan_error_t
377 socketpair_connect (assuan_context_t *ctx,
378                     const char *name, const char *const argv[],
379                     int *fd_child_list,
380                     void (*atfork) (void *opaque, int reserved),
381                     void *atforkvalue)
382 {
383   assuan_error_t err;
384   int fds[2];
385   char mypidstr[50];
386
387   if (!ctx
388       || (name && (!argv || !argv[0]))
389       || (!name && argv))
390     return _assuan_error (ASSUAN_Invalid_Value);
391
392   fix_signals ();
393
394   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
395
396   if ( socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) )
397     {
398       _assuan_log_printf ("socketpair failed: %s\n", strerror (errno));
399       return _assuan_error (ASSUAN_General_Error);
400     }
401   
402   err = _assuan_new_context (ctx);
403   if (err)
404     {
405       close (fds[0]);
406       close (fds[1]);
407       return err;
408     }
409   (*ctx)->pipe_mode = 1;
410   (*ctx)->inbound.fd  = fds[0]; 
411   (*ctx)->outbound.fd = fds[0]; 
412   _assuan_init_uds_io (*ctx);
413   (*ctx)->deinit_handler = _assuan_uds_deinit;
414   (*ctx)->finish_handler = do_finish;
415
416   (*ctx)->pid = fork ();
417   if ((*ctx)->pid < 0)
418     {
419       close (fds[0]);
420       close (fds[1]);
421       _assuan_release_context (*ctx); 
422       *ctx = NULL;
423       return _assuan_error (ASSUAN_General_Error);
424     }
425
426   if ((*ctx)->pid == 0)
427     {
428 #ifdef _ASSUAN_USE_DOUBLE_FORK      
429       pid_t pid;
430
431       if ((pid = fork ()) == 0)
432 #endif
433         {
434           int fd, i, n;
435           char errbuf[512];
436           int *fdp;
437           
438           if (atfork)
439             atfork (atforkvalue, 0);
440
441           /* Connect stdin and stdout to /dev/null. */
442           fd = open ("/dev/null", O_RDONLY);
443           if (fd == -1 || dup2 (fd, STDIN_FILENO) == -1)
444             {
445               _assuan_log_printf ("dup2(dev/null) failed: %s\n",
446                                   strerror (errno));
447               _exit (4);
448             }
449           fd = open ("/dev/null", O_WRONLY);
450           if (fd == -1 || dup2 (fd, STDOUT_FILENO) == -1)
451             {
452               _assuan_log_printf ("dup2(dev/null) failed: %s\n",
453                                   strerror (errno));
454               _exit (4);
455             }
456
457           /* Dup stderr to /dev/null unless it is in the list of FDs to be
458              passed to the child. */
459           fdp = fd_child_list;
460           if (fdp)
461             {
462               for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
463                 ;
464             }
465           if (!fdp || *fdp == -1)
466             {
467               fd = open ("/dev/null", O_WRONLY);
468               if (fd == -1 || dup2 (fd, STDERR_FILENO) == -1)
469                 {
470                   _assuan_log_printf ("dup2(dev/null) failed: %s\n",
471                                       strerror (errno));
472                   _exit (4);
473                 }
474             }
475
476
477           /* Close all files which will not be duped, are not in the
478              fd_child_list and are not the connection fd. */
479           n = sysconf (_SC_OPEN_MAX);
480           if (n < 0)
481             n = MAX_OPEN_FDS;
482           for (i=0; i < n; i++)
483             {
484               if ( i == STDIN_FILENO || i == STDOUT_FILENO
485                    || i == STDERR_FILENO || i == fds[1])
486                 continue;
487               fdp = fd_child_list;
488               if (fdp)
489                 {
490                   while (*fdp != -1 && *fdp != i)
491                     fdp++;
492                 }
493
494               if (!(fdp && *fdp != -1))
495                 close(i);
496             }
497           errno = 0;
498
499           /* We store our parents pid in the environment so that the
500              execed assuan server is able to read the actual pid of the
501              client.  The server can't use getppid becuase it might have
502              been double forked before the assuan server has been
503              initialized. */
504           setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
505
506           /* Now set the environment variable used to convey the
507              connection's file descriptor. */
508           sprintf (mypidstr, "%d", fds[1]);
509           if (setenv ("_assuan_connection_fd", mypidstr, 1))
510             {
511               _assuan_log_printf ("setenv failed: %s\n", strerror (errno));
512               _exit (4);
513             }
514
515           if (!name && !argv)
516             {
517               /* No name and no args given, thus we don't do an exec
518                  but continue the forked process.  */
519               _assuan_release_context (*ctx);
520               *ctx = NULL;
521               return 0;
522             }
523
524           execv (name, (char *const *) argv); 
525           /* oops - use the pipe to tell the parent about it */
526           snprintf (errbuf, sizeof(errbuf)-1,
527                     "ERR %d can't exec `%s': %.50s\n",
528                     _assuan_error (ASSUAN_Problem_Starting_Server),
529                     name, strerror (errno));
530           errbuf[sizeof(errbuf)-1] = 0;
531           writen (fds[1], errbuf, strlen (errbuf));
532           _exit (4);
533         }
534 #ifdef _ASSUAN_USE_DOUBLE_FORK
535       if (pid == -1)
536         _exit (1);
537       else
538         _exit (0);
539 #endif
540     }
541
542
543 #ifdef _ASSUAN_USE_DOUBLE_FORK
544   _assuan_waitpid ((*ctx)->pid, NULL, 0);
545   (*ctx)->pid = -1;
546 #endif
547
548   close (fds[1]);
549   
550   return initial_handshake (ctx);
551 }
552 #endif /*!HAVE_W32_SYSTEM*/
553
554
555
556
557 #ifdef _ASSUAN_IN_GPGME_BUILD_ASSUAN
558
559 #define pipe_connect pipe_connect_gpgme
560
561 /* From GPGME priv-io.h  */
562 struct spawn_fd_item_s
563 {
564   int fd;
565   int dup_to;
566 };
567
568 /* W32 version of the pipe connection code. */
569 static assuan_error_t
570 pipe_connect_gpgme (assuan_context_t *ctx,
571                     const char *name, const char *const argv[],
572                     int *fd_child_list,
573                     void (*atfork) (void *opaque, int reserved),
574                     void *atforkvalue)
575 {
576   assuan_error_t err;
577   int res;
578   int rp[2];
579   int wp[2];
580   char mypidstr[50];
581   struct spawn_fd_item_s child_fds[3]; /* stdin, stdout, terminating -1 */
582
583   if (!ctx || !name || !argv || !argv[0])
584     return _assuan_error (ASSUAN_Invalid_Value);
585
586   /* Actually, GPGME does this for us.  But we plan to reuse this code
587      in the generic assuan.  */
588   fix_signals ();
589
590   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
591
592   /* Create the two pipes. */
593   if (_gpgme_io_pipe (rp, 1))
594     return _assuan_error (ASSUAN_General_Error);
595   
596   if (_gpgme_io_pipe (wp, 0))
597     {
598       _gpgme_io_close (rp[0]);
599       _gpgme_io_close (rp[1]);
600       return _assuan_error (ASSUAN_General_Error);
601     }
602
603   err = _assuan_new_context (ctx);
604   if (err)
605     {
606       _gpgme_io_close (rp[0]);
607       _gpgme_io_close (rp[1]);
608       _gpgme_io_close (wp[0]);
609       _gpgme_io_close (wp[1]);
610       return _assuan_error (ASSUAN_General_Error);
611     }
612
613   (*ctx)->pipe_mode = 1;
614   (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
615   (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
616   (*ctx)->deinit_handler = do_deinit;
617   (*ctx)->finish_handler = do_finish;
618
619   /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env
620      variable to mypidstr.  However this requires us to write a full
621      environment handler, because the strings are expected in sorted
622      order.  The suggestion given in the MS Reference Library, to save
623      the old value, changeit, create proces and restore it, is not
624      thread safe.  */
625
626   /* Parent list is same as client list.  Note that GPGME will dup nul
627      to stderr even if the caller wants to inherit the handle for
628      it.  */
629   /* Server stdout is its write end of our read pipe.  */
630   child_fds[0].fd = rp[1];
631   child_fds[0].dup_to = 1;
632   /* Server stdin is its read end of our write pipe.  */
633   child_fds[1].fd = wp[0];
634   child_fds[1].dup_to = 0;
635   child_fds[2].fd = -1;
636
637   /* Start the process.  */
638   res = _gpgme_io_spawn (name, argv, child_fds, child_fds);
639   if (res == -1)
640     {
641       _assuan_log_printf ("CreateProcess failed: %s\n", strerror (errno));
642       _gpgme_io_close (rp[0]);
643       _gpgme_io_close (rp[1]);
644       _gpgme_io_close (wp[0]);
645       _gpgme_io_close (wp[1]);
646       return _assuan_error (ASSUAN_General_Error);
647     }
648
649   (*ctx)->pid = 0;  /* We don't use the PID. */
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