2001-12-16 Marcus Brinkmann <marcus@g10code.de>
[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       char errbuf[512];
141
142       close (rp[0]);
143       close (wp[1]);
144
145       /* Dup handles and to stdin/stdout and exec */
146       if (rp[1] != STDOUT_FILENO)
147         {
148           if (dup2 (rp[1], STDOUT_FILENO) == -1)
149             {
150               LOGERROR1 ("dup2 failed in child: %s\n", strerror (errno));
151               _exit (4);
152             }
153           close (rp[1]);
154         }
155       if (wp[0] != STDIN_FILENO)
156         {
157           if (dup2 (wp[0], STDIN_FILENO) == -1)
158             {
159               LOGERROR1 ("dup2 failed in child: %s\n", strerror (errno));
160               _exit (4);
161             }
162           close (wp[0]);
163         }
164
165       execv (name, argv); 
166       /* oops - use the pipe to tell the parent about it */
167       snprintf (errbuf, sizeof(errbuf)-1, "ERR %d can't exec `%s': %.50s\n",
168                 ASSUAN_Problem_Starting_Server, name, strerror (errno));
169       errbuf[sizeof(errbuf)-1] = 0;
170       writen (1, errbuf, strlen (errbuf));
171       _exit (4);
172     }
173
174   close (rp[1]);
175   close (wp[0]);
176
177   /* initial handshake */
178   {
179     int okay, off;
180
181     err = _assuan_read_from_server (*ctx, &okay, &off);
182     if (err)
183       {
184         LOGERROR1 ("can't connect server: %s\n", assuan_strerror (err));
185       }
186     else if (okay != 1)
187       {
188         LOGERROR1 ("can't connect server: `%s'\n", (*ctx)->inbound.line);
189         err = ASSUAN_Connect_Failed;
190       }
191   }
192
193   if (err)
194     {
195       if ((*ctx)->pid != -1)
196         waitpid ((*ctx)->pid, NULL, 0);  /* FIXME Check return value.  */
197       assuan_deinit_pipe_server (*ctx);  /* FIXME: Common code should be factored out.  */
198     }
199
200   return err;
201 }
202
203 void
204 assuan_pipe_disconnect (ASSUAN_CONTEXT ctx)
205 {
206   assuan_write_line (ctx, "BYE");
207   close (ctx->inbound.fd);
208   close (ctx->outbound.fd);
209   waitpid (ctx->pid, NULL, 0);  /* FIXME Check return value.  */
210   assuan_deinit_pipe_server (ctx);
211 }
212
213 pid_t
214 assuan_get_pid (ASSUAN_CONTEXT ctx)
215 {
216   return ctx ? ctx->pid : -1;
217 }
218
219