Added missing files and fixed stuff from the recent merged. I did only
[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       pid_t pid;
471
472       if ((pid = fork ()) == 0)
473 #endif
474         {
475           int i, n;
476           char errbuf[512];
477           int *fdp;
478           
479           if (atfork)
480             atfork (atforkvalue, 0);
481
482           /* Dup handles to stdin/stdout. */
483           if (rp[1] != STDOUT_FILENO)
484             {
485               if (dup2 (rp[1], STDOUT_FILENO) == -1)
486                 {
487                   _assuan_log_printf ("dup2 failed in child: %s\n",
488                                       strerror (errno));
489                   _exit (4);
490                 }
491             }
492           if (wp[0] != STDIN_FILENO)
493             {
494               if (dup2 (wp[0], STDIN_FILENO) == -1)
495                 {
496                   _assuan_log_printf ("dup2 failed in child: %s\n",
497                                       strerror (errno));
498                   _exit (4);
499                 }
500             }
501
502           /* Dup stderr to /dev/null unless it is in the list of FDs to be
503              passed to the child. */
504           fdp = fd_child_list;
505           if (fdp)
506             {
507               for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
508                 ;
509             }
510           if (!fdp || *fdp == -1)
511             {
512               int fd = open ("/dev/null", O_WRONLY);
513               if (fd == -1)
514                 {
515                   _assuan_log_printf ("can't open `/dev/null': %s\n",
516                                       strerror (errno));
517                   _exit (4);
518                 }
519               if (dup2 (fd, STDERR_FILENO) == -1)
520                 {
521                   _assuan_log_printf ("dup2(dev/null, 2) failed: %s\n",
522                                       strerror (errno));
523                   _exit (4);
524                 }
525             }
526
527
528           /* Close all files which will not be duped and are not in the
529              fd_child_list. */
530           n = sysconf (_SC_OPEN_MAX);
531           if (n < 0)
532             n = MAX_OPEN_FDS;
533           for (i=0; i < n; i++)
534             {
535               if ( i == STDIN_FILENO || i == STDOUT_FILENO
536                    || i == STDERR_FILENO)
537                 continue;
538               fdp = fd_child_list;
539               if (fdp)
540                 {
541                   while (*fdp != -1 && *fdp != i)
542                     fdp++;
543                 }
544
545               if (!(fdp && *fdp != -1))
546                 close(i);
547             }
548           errno = 0;
549
550           /* We store our parents pid in the environment so that the
551              execed assuan server is able to read the actual pid of the
552              client.  The server can't use getppid becuase it might have
553              been double forked before the assuan server has been
554              initialized. */
555           setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
556
557           execv (name, argv); 
558           /* oops - use the pipe to tell the parent about it */
559           snprintf (errbuf, sizeof(errbuf)-1,
560                     "ERR %d can't exec `%s': %.50s\n",
561                     ASSUAN_Problem_Starting_Server, name, strerror (errno));
562           errbuf[sizeof(errbuf)-1] = 0;
563           writen (1, errbuf, strlen (errbuf));
564           _exit (4);
565         }
566 #ifdef _ASSUAN_USE_DOUBLE_FORK
567       if (pid == -1)
568         _exit (1);
569       else
570         _exit (0);
571 #endif
572     }
573
574 #ifdef _ASSUAN_USE_DOUBLE_FORK
575   waitpid ((*ctx)->pid, NULL, 0);
576   (*ctx)->pid = -1;
577 #endif
578
579   close (rp[1]);
580   close (wp[0]);
581
582 #endif /*!HAVE_W32_SYSTEM*/
583
584   /* initial handshake */
585   {
586     int okay, off;
587
588     err = _assuan_read_from_server (*ctx, &okay, &off);
589     if (err)
590       _assuan_log_printf ("can't connect server: %s\n",
591                           assuan_strerror (err));
592     else if (okay != 1)
593       {
594         _assuan_log_printf ("can't connect server: `%s'\n",
595                             (*ctx)->inbound.line);
596         err = ASSUAN_Connect_Failed;
597       }
598   }
599
600   if (err)
601     {
602       assuan_disconnect (*ctx);
603       *ctx = NULL;
604     }
605
606   return err;
607 }
608
609
610 /* Connect to a server over a pipe, creating the assuan context and
611    returning it in CTX.  The server filename is NAME, the argument
612    vector in ARGV.  FD_CHILD_LIST is a -1 terminated list of file
613    descriptors not to close in the child.  */
614 assuan_error_t
615 assuan_pipe_connect (assuan_context_t *ctx, const char *name, char *const argv[],
616                      int *fd_child_list)
617 {
618   return assuan_pipe_connect2 (ctx, name, argv, fd_child_list, NULL, NULL);
619 }