Updated Assuan to the current version
[gpgme.git] / assuan / assuan-connect.c
1 /* assuan-connect.c - Establish a connection (client) 
2  *      Copyright (C) 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * 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 <sys/types.h>
32 #include <sys/wait.h>
33
34 #include "assuan-defs.h"
35
36 #ifdef _POSIX_OPEN_MAX
37 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
38 #else
39 #define MAX_OPEN_FDS 20
40 #endif
41
42 #ifdef HAVE_JNLIB_LOGGING
43 #include "../jnlib/logging.h"
44 #define LOGERROR1(a,b)   log_error ((a), (b))
45 #else
46 #define LOGERROR1(a,b)   fprintf (stderr, (a), (b))
47 #endif
48
49
50
51 static int
52 writen ( int fd, const char *buffer, size_t length )
53 {
54   while (length)
55     {
56       int nwritten = write (fd, buffer, length);
57       
58       if (nwritten < 0)
59         {
60           if (errno == EINTR)
61             continue;
62           return -1; /* write error */
63         }
64       length -= nwritten;
65       buffer += nwritten;
66     }
67   return 0;  /* okay */
68 }
69
70
71
72 /* Connect to a server over a pipe, creating the assuan context and
73    returning it in CTX.  The server filename is NAME, the argument
74    vector in ARGV.  */
75 AssuanError
76 assuan_pipe_connect (ASSUAN_CONTEXT *ctx, const char *name, char *const argv[])
77 {
78   static int fixed_signals = 0;
79   AssuanError err;
80   int rp[2];
81   int wp[2];
82   int fd[2];
83
84   if (!ctx || !name || !argv || !argv[0])
85     return ASSUAN_Invalid_Value;
86
87   if (!fixed_signals)
88     { 
89       struct sigaction act;
90         
91       sigaction (SIGPIPE, NULL, &act);
92       if (act.sa_handler == SIG_DFL)
93         {
94           act.sa_handler = SIG_IGN;
95           sigemptyset (&act.sa_mask);
96           act.sa_flags = 0;
97           sigaction (SIGPIPE, &act, NULL);
98         }
99       fixed_signals = 1;
100       /* FIXME: This is not MT safe */
101     }
102
103   if (pipe (rp) < 0)
104     return ASSUAN_General_Error;
105
106   if (pipe (wp) < 0)
107     {
108       close (rp[0]);
109       close (rp[1]);
110       return ASSUAN_General_Error;
111     }
112   
113   fd[0] = rp[0];  /* Our inbound is read end of read pipe.  */
114   fd[1] = wp[1];  /* Our outbound is write end of write pipe.  */
115
116   err = assuan_init_pipe_server (ctx, fd);  /* FIXME: Common code should be factored out.  */
117   if (err)
118     {
119       close (rp[0]);
120       close (rp[1]);
121       close (wp[0]);
122       close (wp[1]);
123       return err;
124     }
125   (*ctx)->is_server = 0;
126
127   (*ctx)->pid = fork ();
128   if ((*ctx)->pid < 0)
129     {
130       close (rp[0]);
131       close (rp[1]);
132       close (wp[0]);
133       close (wp[1]);
134       assuan_deinit_pipe_server (*ctx);  /* FIXME: Common code should be factored out.  */
135       return ASSUAN_General_Error;
136     }
137
138   if ((*ctx)->pid == 0)
139     {
140       int i, n;
141       char errbuf[512];
142 #ifdef HAVE_JNLIB_LOGGING
143       int log_fd = log_get_fd (); 
144 #endif
145       /* close all files which will not be duped but keep stderr
146          and log_stream for now */
147       n = sysconf (_SC_OPEN_MAX);
148       if (n < 0)
149         n = MAX_OPEN_FDS;
150       for (i=0; i < n; i++)
151         {
152           if (i != fileno (stderr) 
153 #ifdef HAVE_JNLIB_LOGGING
154               && i != log_fd
155 #endif
156               && i != rp[1] && i != wp[0])
157             close(i);
158         }
159       errno = 0;
160
161       /* Dup handles and to stdin/stdout and exec */
162       if (rp[1] != STDOUT_FILENO)
163         {
164           if (dup2 (rp[1], STDOUT_FILENO) == -1)
165             {
166               LOGERROR1 ("dup2 failed in child: %s\n", strerror (errno));
167               _exit (4);
168             }
169           close (rp[1]);
170         }
171       if (wp[0] != STDIN_FILENO)
172         {
173           if (dup2 (wp[0], STDIN_FILENO) == -1)
174             {
175               LOGERROR1 ("dup2 failed in child: %s\n", strerror (errno));
176               _exit (4);
177             }
178           close (wp[0]);
179         }
180
181       execv (name, argv); 
182       /* oops - use the pipe to tell the parent about it */
183       snprintf (errbuf, sizeof(errbuf)-1, "ERR %d can't exec `%s': %.50s\n",
184                 ASSUAN_Problem_Starting_Server, name, strerror (errno));
185       errbuf[sizeof(errbuf)-1] = 0;
186       writen (1, errbuf, strlen (errbuf));
187       _exit (4);
188     }
189
190   close (rp[1]);
191   close (wp[0]);
192
193   /* initial handshake */
194   {
195     int okay, off;
196
197     err = _assuan_read_from_server (*ctx, &okay, &off);
198     if (err)
199       {
200         LOGERROR1 ("can't connect server: %s\n", assuan_strerror (err));
201       }
202     else if (okay != 1)
203       {
204         LOGERROR1 ("can't connect server: `%s'\n", (*ctx)->inbound.line);
205         err = ASSUAN_Connect_Failed;
206       }
207   }
208
209   if (err)
210     {
211       if ((*ctx)->pid != -1)
212         waitpid ((*ctx)->pid, NULL, 0);  /* FIXME Check return value.  */
213       assuan_deinit_pipe_server (*ctx);  /* FIXME: Common code should be factored out.  */
214     }
215
216   return err;
217 }
218
219 void
220 assuan_pipe_disconnect (ASSUAN_CONTEXT ctx)
221 {
222   assuan_write_line (ctx, "BYE");
223   close (ctx->inbound.fd);
224   close (ctx->outbound.fd);
225   waitpid (ctx->pid, NULL, 0);  /* FIXME Check return value.  */
226   assuan_deinit_pipe_server (ctx);
227 }
228
229 pid_t
230 assuan_get_pid (ASSUAN_CONTEXT ctx)
231 {
232   return ctx ? ctx->pid : -1;
233 }
234
235