4ce3862a5e3d8a3a70f7ce7754037beb9ca605ce
[gpgme.git] / assuan / assuan-pipe-connect.c
1 /* assuan-pipe-connect.c - Establish a pipe connection (client) 
2  *      Copyright (C) 2001, 2002, 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #ifndef HAVE_W32_SYSTEM
34 #include <sys/wait.h>
35 #else
36 #include <windows.h>
37 #endif
38
39 #include "assuan-defs.h"
40
41 #ifdef _POSIX_OPEN_MAX
42 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
43 #else
44 #define MAX_OPEN_FDS 20
45 #endif
46
47 #ifdef HAVE_W32_SYSTEM
48 /* We assume that a HANDLE can be represented by an int which should
49    be true for all i386 systems (HANDLE is defined as void *) and
50    these are the only systems for which Windows is available.  Further
51    we assume that -1 denotes an invalid handle.  */
52 #define fd_to_handle(a)  ((HANDLE)(a))
53 #define handle_to_fd(a)  ((int)(a))
54 #define pid_to_handle(a) ((HANDLE)(a))
55 #define handle_to_pid(a) ((int)(a))
56 #endif /*HAVE_W32_SYSTEM*/
57
58
59 /* This should be called to make sure that SIGPIPE gets ignored.  */
60 static void
61 fix_signals (void)
62 {
63 #ifndef _ASSUAN_NO_FIXED_SIGNALS
64 #ifndef HAVE_DOSISH_SYSTEM  /* No SIGPIPE for these systems.  */
65   static int fixed_signals;
66
67   if (!fixed_signals)
68     { 
69       struct sigaction act;
70         
71       sigaction (SIGPIPE, NULL, &act);
72       if (act.sa_handler == SIG_DFL)
73         {
74           act.sa_handler = SIG_IGN;
75           sigemptyset (&act.sa_mask);
76           act.sa_flags = 0;
77           sigaction (SIGPIPE, &act, NULL);
78         }
79       fixed_signals = 1;
80       /* FIXME: This is not MT safe */
81     }
82 #endif /*HAVE_DOSISH_SYSTEM*/
83 #endif /*!_ASSUAN_NO_FIXED_SIGNALS*/
84 }
85
86
87 #ifndef HAVE_W32_SYSTEM
88 static int
89 writen (int fd, const char *buffer, size_t length)
90 {
91   while (length)
92     {
93       int nwritten = write (fd, buffer, length);
94       
95       if (nwritten < 0)
96         {
97           if (errno == EINTR)
98             continue;
99           return -1; /* write error */
100         }
101       length -= nwritten;
102       buffer += nwritten;
103     }
104   return 0;  /* okay */
105 }
106 #endif
107
108 static int
109 do_finish (assuan_context_t ctx)
110 {
111   if (ctx->inbound.fd != -1)
112     {
113       _assuan_close (ctx->inbound.fd);
114       ctx->inbound.fd = -1;
115     }
116   if (ctx->outbound.fd != -1)
117     {
118       _assuan_close (ctx->outbound.fd);
119       ctx->outbound.fd = -1;
120     }
121   if (ctx->pid != -1 && ctx->pid)
122     {
123 #ifndef HAVE_W32_SYSTEM
124 #ifndef _ASSUAN_USE_DOUBLE_FORK
125       if (!ctx->flags.no_waitpid)
126         waitpid (ctx->pid, NULL, 0); 
127       ctx->pid = -1;
128 #endif
129 #endif /*!HAVE_W32_SYSTEM*/
130     }
131   return 0;
132 }
133
134 static void
135 do_deinit (assuan_context_t ctx)
136 {
137   do_finish (ctx);
138 }
139
140
141 #ifdef HAVE_W32_SYSTEM
142 /* Build a command line for use with W32's CreateProcess.  On success
143    CMDLINE gets the address of a newly allocated string.  */
144 static int
145 build_w32_commandline (char * const *argv, char **cmdline)
146 {
147   int i, n;
148   const char *s;
149   char *buf, *p;
150
151   *cmdline = NULL;
152   n = 0;
153   for (i=0; (s=argv[i]); i++)
154     {
155       n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
156       for (; *s; s++)
157         if (*s == '\"')
158           n++;  /* Need to double inner quotes.  */
159     }
160   n++;
161
162   buf = p = xtrymalloc (n);
163   if (!buf)
164     return -1;
165
166   for (i=0; argv[i]; i++) 
167     {
168       if (i)
169         p = stpcpy (p, " ");
170       if (!*argv[i]) /* Empty string. */
171         p = stpcpy (p, "\"\"");
172       else if (strpbrk (argv[i], " \t\n\v\f\""))
173         {
174           p = stpcpy (p, "\"");
175           for (s=argv[i]; *s; s++)
176             {
177               *p++ = *s;
178               if (*s == '\"')
179                 *p++ = *s;
180             }
181           *p++ = '\"';
182           *p = 0;
183         }
184       else
185         p = stpcpy (p, argv[i]);
186     }
187
188   *cmdline= buf;
189   return 0;
190 }
191 #endif /*HAVE_W32_SYSTEM*/
192
193
194 #ifdef HAVE_W32_SYSTEM
195 /* Create pipe where one end end is inheritable.  */
196 static int
197 create_inheritable_pipe (int filedes[2], int for_write)
198 {
199   HANDLE r, w, h;
200   SECURITY_ATTRIBUTES sec_attr;
201
202   memset (&sec_attr, 0, sizeof sec_attr );
203   sec_attr.nLength = sizeof sec_attr;
204   sec_attr.bInheritHandle = FALSE;
205     
206   if (!CreatePipe (&r, &w, &sec_attr, 0))
207     {
208       _assuan_log_printf ("CreatePipe failed: %s\n", w32_strerror (-1));
209       return -1;
210     }
211
212   if (!DuplicateHandle (GetCurrentProcess(), for_write? r : w,
213                         GetCurrentProcess(), &h, 0,
214                         TRUE, DUPLICATE_SAME_ACCESS ))
215     {
216       _assuan_log_printf ("DuplicateHandle failed: %s\n", w32_strerror (-1));
217       CloseHandle (r);
218       CloseHandle (w);
219       return -1;
220     }
221   if (for_write)
222     {
223       CloseHandle (r);
224       r = h;
225     }
226   else
227     {
228       CloseHandle (w);
229       w = h;
230     }
231
232   filedes[0] = handle_to_fd (r);
233   filedes[1] = handle_to_fd (w);
234   return 0;
235 }
236 #endif /*HAVE_W32_SYSTEM*/
237
238
239 /* Connect to a server over a pipe, creating the assuan context and
240    returning it in CTX.  The server filename is NAME, the argument
241    vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file
242    descriptors not to close in the child.  ATFORK is called in the
243    child right after the fork; ATFORKVALUE is passed as the first
244    argument and 0 is passed as the second argument. The ATFORK
245    function should only act if the second value is 0. */
246 assuan_error_t
247 assuan_pipe_connect2 (assuan_context_t *ctx,
248                       const char *name, char *const argv[],
249                       int *fd_child_list,
250                       void (*atfork) (void *opaque, int reserved),
251                       void *atforkvalue)
252 {
253 #ifdef HAVE_W32_SYSTEM
254   assuan_error_t err;
255   int rp[2];
256   int wp[2];
257   char mypidstr[50];
258   char *cmdline;
259   SECURITY_ATTRIBUTES sec_attr;
260   PROCESS_INFORMATION pi = 
261     {
262       NULL,      /* Returns process handle.  */
263       0,         /* Returns primary thread handle.  */
264       0,         /* Returns pid.  */
265       0          /* Returns tid.  */
266     };
267   STARTUPINFO si;
268   int fd, *fdp;
269   HANDLE nullfd = INVALID_HANDLE_VALUE;
270
271   if (!ctx || !name || !argv || !argv[0])
272     return ASSUAN_Invalid_Value;
273
274   fix_signals ();
275
276   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
277
278   /* Build the command line.  */
279   if (build_w32_commandline (argv, &cmdline))
280     return ASSUAN_Out_Of_Core;
281
282   /* Create thew two pipes. */
283   if (create_inheritable_pipe (rp, 0))
284     {
285       xfree (cmdline);
286       return ASSUAN_General_Error;
287     }
288   
289   if (create_inheritable_pipe (wp, 1))
290     {
291       CloseHandle (fd_to_handle (rp[0]));
292       CloseHandle (fd_to_handle (rp[1]));
293       xfree (cmdline);
294       return ASSUAN_General_Error;
295     }
296
297   
298   err = _assuan_new_context (ctx);
299   if (err)
300     {
301       CloseHandle (fd_to_handle (rp[0]));
302       CloseHandle (fd_to_handle (rp[1]));
303       CloseHandle (fd_to_handle (wp[0]));
304       CloseHandle (fd_to_handle (wp[1]));
305       xfree (cmdline);
306       return ASSUAN_General_Error;
307     }
308
309   (*ctx)->pipe_mode = 1;
310   (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
311   (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
312   (*ctx)->deinit_handler = do_deinit;
313   (*ctx)->finish_handler = do_finish;
314
315
316   /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env
317      variable.  However this requires us to write a full environment
318      handler, because the strings are expected in sorted order.  The
319      suggestion given in the MS Reference Library, to save the old
320      value, changeit, create proces and restore it, is not thread
321      safe.  */
322
323   /* Start the process.  */
324   memset (&sec_attr, 0, sizeof sec_attr );
325   sec_attr.nLength = sizeof sec_attr;
326   sec_attr.bInheritHandle = FALSE;
327   
328   memset (&si, 0, sizeof si);
329   si.cb = sizeof (si);
330   si.dwFlags = STARTF_USESTDHANDLES;
331   si.hStdInput  = fd_to_handle (wp[0]);
332   si.hStdOutput = fd_to_handle (rp[1]);
333
334   /* Dup stderr to /dev/null unless it is in the list of FDs to be
335      passed to the child. */
336   fd = fileno (stderr);
337   fdp = fd_child_list;
338   if (fdp)
339     {
340       for (; *fdp != -1 && *fdp != fd; fdp++)
341         ;
342     }
343   if (!fdp || *fdp == -1)
344     {
345       nullfd = CreateFile ("nul", GENERIC_WRITE,
346                            FILE_SHARE_READ | FILE_SHARE_WRITE,
347                            NULL, OPEN_EXISTING, 0, NULL);
348       if (nullfd == INVALID_HANDLE_VALUE)
349         {
350           _assuan_log_printf ("can't open `nul': %s\n", w32_strerror (-1));
351           CloseHandle (fd_to_handle (rp[0]));
352           CloseHandle (fd_to_handle (rp[1]));
353           CloseHandle (fd_to_handle (wp[0]));
354           CloseHandle (fd_to_handle (wp[1]));
355           xfree (cmdline);
356           _assuan_release_context (*ctx); 
357           return -1;
358         }
359       si.hStdError = nullfd;
360     }
361   else
362     si.hStdError = fd_to_handle (_get_osfhandle (fd));
363
364
365   /* Note: We inherit all handles flagged as inheritable.  This seems
366      to be a security flaw but there seems to be no way of selecting
367      handles to inherit. */
368   /*   _assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n", */
369   /*                       name, cmdline); */
370   if (!CreateProcess (name,                 /* Program to start.  */
371                       cmdline,              /* Command line arguments.  */
372                       &sec_attr,            /* Process security attributes.  */
373                       &sec_attr,            /* Thread security attributes.  */
374                       TRUE,                 /* Inherit handles.  */
375                       (CREATE_DEFAULT_ERROR_MODE
376                        | GetPriorityClass (GetCurrentProcess ())
377                        | CREATE_SUSPENDED), /* Creation flags.  */
378                       NULL,                 /* Environment.  */
379                       NULL,                 /* Use current drive/directory.  */
380                       &si,                  /* Startup information. */
381                       &pi                   /* Returns process information.  */
382                       ))
383     {
384       _assuan_log_printf ("CreateProcess failed: %s\n", w32_strerror (-1));
385       CloseHandle (fd_to_handle (rp[0]));
386       CloseHandle (fd_to_handle (rp[1]));
387       CloseHandle (fd_to_handle (wp[0]));
388       CloseHandle (fd_to_handle (wp[1]));
389       if (nullfd != INVALID_HANDLE_VALUE)
390         CloseHandle (nullfd);
391       xfree (cmdline);
392       _assuan_release_context (*ctx); 
393       return ASSUAN_General_Error;
394     }
395   xfree (cmdline);
396   cmdline = NULL;
397   if (nullfd != INVALID_HANDLE_VALUE)
398     {
399       CloseHandle (nullfd);
400       nullfd = INVALID_HANDLE_VALUE;
401     }
402
403   CloseHandle (fd_to_handle (rp[1]));
404   CloseHandle (fd_to_handle (wp[0]));
405
406   /*   _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */
407   /*                       " dwProcessID=%d dwThreadId=%d\n", */
408   /*                       pi.hProcess, pi.hThread, */
409   /*                       (int) pi.dwProcessId, (int) pi.dwThreadId); */
410
411   ResumeThread (pi.hThread);
412   CloseHandle (pi.hThread); 
413   (*ctx)->pid = 0;  /* We don't use the PID. */
414   CloseHandle (pi.hProcess); /* We don't need to wait for the process. */
415
416 #else /*!HAVE_W32_SYSTEM*/
417   assuan_error_t err;
418   int rp[2];
419   int wp[2];
420   char mypidstr[50];
421
422   if (!ctx || !name || !argv || !argv[0])
423     return ASSUAN_Invalid_Value;
424
425   fix_signals ();
426
427   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
428
429   if (pipe (rp) < 0)
430     return ASSUAN_General_Error;
431
432   if (pipe (wp) < 0)
433     {
434       close (rp[0]);
435       close (rp[1]);
436       return ASSUAN_General_Error;
437     }
438   
439   err = _assuan_new_context (ctx);
440   if (err)
441     {
442       close (rp[0]);
443       close (rp[1]);
444       close (wp[0]);
445       close (wp[1]);
446       return err;
447     }
448   (*ctx)->pipe_mode = 1;
449   (*ctx)->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
450   (*ctx)->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
451   (*ctx)->deinit_handler = do_deinit;
452   (*ctx)->finish_handler = do_finish;
453
454   /* FIXME: For GPGME we should better use _gpgme_io_spawn.  The PID
455      stored here is actually soon useless.  */
456   (*ctx)->pid = fork ();
457   if ((*ctx)->pid < 0)
458     {
459       close (rp[0]);
460       close (rp[1]);
461       close (wp[0]);
462       close (wp[1]);
463       _assuan_release_context (*ctx); 
464       return ASSUAN_General_Error;
465     }
466
467   if ((*ctx)->pid == 0)
468     {
469 #ifdef _ASSUAN_USE_DOUBLE_FORK      
470       if ((pid = fork ()) == 0)
471 #endif
472         {
473           int i, n;
474           char errbuf[512];
475           int *fdp;
476           
477           if (atfork)
478             atfork (atforkvalue, 0);
479
480           /* Dup handles to stdin/stdout. */
481           if (rp[1] != STDOUT_FILENO)
482             {
483               if (dup2 (rp[1], STDOUT_FILENO) == -1)
484                 {
485                   _assuan_log_printf ("dup2 failed in child: %s\n",
486                                       strerror (errno));
487                   _exit (4);
488                 }
489             }
490           if (wp[0] != STDIN_FILENO)
491             {
492               if (dup2 (wp[0], STDIN_FILENO) == -1)
493                 {
494                   _assuan_log_printf ("dup2 failed in child: %s\n",
495                                       strerror (errno));
496                   _exit (4);
497                 }
498             }
499
500           /* Dup stderr to /dev/null unless it is in the list of FDs to be
501              passed to the child. */
502           fdp = fd_child_list;
503           if (fdp)
504             {
505               for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
506                 ;
507             }
508           if (!fdp || *fdp == -1)
509             {
510               int fd = open ("/dev/null", O_WRONLY);
511               if (fd == -1)
512                 {
513                   _assuan_log_printf ("can't open `/dev/null': %s\n",
514                                       strerror (errno));
515                   _exit (4);
516                 }
517               if (dup2 (fd, STDERR_FILENO) == -1)
518                 {
519                   _assuan_log_printf ("dup2(dev/null, 2) failed: %s\n",
520                                       strerror (errno));
521                   _exit (4);
522                 }
523             }
524
525
526           /* Close all files which will not be duped and are not in the
527              fd_child_list. */
528           n = sysconf (_SC_OPEN_MAX);
529           if (n < 0)
530             n = MAX_OPEN_FDS;
531           for (i=0; i < n; i++)
532             {
533               if ( i == STDIN_FILENO || i == STDOUT_FILENO
534                    || i == STDERR_FILENO)
535                 continue;
536               fdp = fd_child_list;
537               if (fdp)
538                 {
539                   while (*fdp != -1 && *fdp != i)
540                     fdp++;
541                 }
542
543               if (!(fdp && *fdp != -1))
544                 close(i);
545             }
546           errno = 0;
547
548           /* We store our parents pid in the environment so that the
549              execed assuan server is able to read the actual pid of the
550              client.  The server can't use getppid becuase it might have
551              been double forked before the assuan server has been
552              initialized. */
553           setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
554
555           execv (name, argv); 
556           /* oops - use the pipe to tell the parent about it */
557           snprintf (errbuf, sizeof(errbuf)-1,
558                     "ERR %d can't exec `%s': %.50s\n",
559                     ASSUAN_Problem_Starting_Server, name, strerror (errno));
560           errbuf[sizeof(errbuf)-1] = 0;
561           writen (1, errbuf, strlen (errbuf));
562           _exit (4);
563         }
564 #ifdef _ASSUAN_USE_DOUBLE_FORK
565       if (pid == -1)
566         _exit (1);
567       else
568         _exit (0);
569 #endif
570     }
571
572 #ifdef _ASSUAN_USE_DOUBLE_FORK
573   waitpid ((*ctx)->pid, NULL, 0);
574   (*ctx)->pid = -1;
575 #endif
576
577   close (rp[1]);
578   close (wp[0]);
579
580 #endif /*!HAVE_W32_SYSTEM*/
581
582   /* initial handshake */
583   {
584     int okay, off;
585
586     err = _assuan_read_from_server (*ctx, &okay, &off);
587     if (err)
588       _assuan_log_printf ("can't connect server: %s\n",
589                           assuan_strerror (err));
590     else if (okay != 1)
591       {
592         _assuan_log_printf ("can't connect server: `%s'\n",
593                             (*ctx)->inbound.line);
594         err = ASSUAN_Connect_Failed;
595       }
596   }
597
598   if (err)
599     {
600       assuan_disconnect (*ctx);
601       *ctx = NULL;
602     }
603
604   return err;
605 }
606
607
608 /* Connect to a server over a pipe, creating the assuan context and
609    returning it in CTX.  The server filename is NAME, the argument
610    vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file
611    descriptors not to close in the child.  */
612 assuan_error_t
613 assuan_pipe_connect (assuan_context_t *ctx, const char *name, char *const argv[],
614                      int *fd_child_list)
615 {
616   return assuan_pipe_connect2 (ctx, name, argv, fd_child_list, NULL, NULL);
617 }