ac6ba1140cdc863bd01f4dd25f181784e21c2680
[gpgme.git] / gpgme / rungpg.c
1 /* rungpg.c 
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME 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  * GPGME 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 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <errno.h>
27 #include <sys/time.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <sys/wait.h>
31 #include <signal.h>
32 #include <fcntl.h>
33
34 #include "gpgme.h"
35 #include "util.h"
36 #include "ops.h"
37 #include "wait.h"
38 #include "rungpg.h"
39 #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
40
41 #include "status-table.h"
42
43 /* This type is used to build a list of gpg arguments and
44  * data sources/sinks */
45 struct arg_and_data_s {
46     struct arg_and_data_s *next;
47     GpgmeData data;  /* If this is not NULL .. */
48     int dup_to;
49     char arg[1];     /* .. this is used */
50 };
51
52 struct fd_data_map_s {
53     GpgmeData data;
54     int inbound;  /* true if this is used for reading from gpg */
55     int dup_to;
56     int fd;       /* the fd to use */
57     int peer_fd;  /* the outher side of the pipe */
58 };
59
60
61 struct gpg_object_s {
62     struct arg_and_data_s *arglist;
63     struct arg_and_data_s **argtail;
64     int arg_error;  
65
66     struct {
67         int fd[2];  
68         size_t bufsize;
69         char *buffer;
70         size_t readpos;
71         int eof;
72         GpgStatusHandler fnc;
73         void *fnc_value;
74     } status;
75
76     char **argv;  
77     struct fd_data_map_s *fd_data_map;
78
79     pid_t pid; 
80
81     int running;
82     int exit_status;
83     int exit_signal;
84 };
85
86 static void kill_gpg ( GpgObject gpg );
87 static void free_argv ( char **argv );
88 static void free_fd_data_map ( struct fd_data_map_s *fd_data_map );
89 static int gpg_status_handler ( void *opaque, pid_t pid, int fd );
90 static int gpg_inbound_handler ( void *opaque, pid_t pid, int fd );
91 static int gpg_outbound_handler ( void *opaque, pid_t pid, int fd );
92
93 static GpgmeError read_status ( GpgObject gpg );
94
95
96
97 GpgmeError
98 _gpgme_gpg_new_object ( GpgObject *r_gpg )
99 {
100     GpgObject gpg;
101     int rc = 0;
102
103     gpg = xtrycalloc ( 1, sizeof *gpg );
104     if ( !gpg ) {
105         rc = mk_error (Out_Of_Core);
106         goto leave;
107     }
108     gpg->argtail = &gpg->arglist;
109
110     gpg->status.fd[0] = -1;
111     gpg->status.fd[1] = -1;
112
113     /* allocate the read buffer for the status pipe */
114     gpg->status.bufsize = 1024;
115     gpg->status.readpos = 0;
116     gpg->status.buffer = xtrymalloc (gpg->status.bufsize);
117     if (!gpg->status.buffer) {
118         rc = mk_error (Out_Of_Core);
119         goto leave;
120     }
121     /* In any case we need a status pipe - create it right here  and
122      * don't handle it with our generic GpgmeData mechanism */
123     if (pipe (gpg->status.fd) == -1) {
124         rc = mk_error (Pipe_Error);
125         goto leave;
126     }
127     gpg->status.eof = 0;
128     _gpgme_gpg_add_arg ( gpg, "--status-fd" );
129     {
130         char buf[25];
131         sprintf ( buf, "%d", gpg->status.fd[1]);
132         _gpgme_gpg_add_arg ( gpg, buf );
133     }
134     _gpgme_gpg_add_arg ( gpg, "--batch" );
135     _gpgme_gpg_add_arg ( gpg, "--no-tty" );
136
137  leave:
138     if (rc) {
139         _gpgme_gpg_release_object (gpg);
140         *r_gpg = NULL;
141     }
142     else
143         *r_gpg = gpg;
144     return rc;
145 }
146
147 void
148 _gpgme_gpg_release_object ( GpgObject gpg )
149 {
150     if ( !gpg )
151         return;
152     xfree (gpg->status.buffer);
153     if ( gpg->argv )
154         free_argv (gpg->argv);
155     if (gpg->status.fd[0] != -1 )
156         close (gpg->status.fd[0]);
157     if (gpg->status.fd[1] != -1 )
158         close (gpg->status.fd[1]);
159     free_fd_data_map (gpg->fd_data_map);
160     kill_gpg (gpg); /* fixme: should be done asyncronously */
161     xfree (gpg);
162 }
163
164 static void
165 kill_gpg ( GpgObject gpg )
166 {
167   #if 0
168     if ( gpg->running ) {
169         /* still running? Must send a killer */
170         kill ( gpg->pid, SIGTERM);
171         sleep (2);
172         if ( !waitpid (gpg->pid, NULL, WNOHANG) ) {
173             /* pay the murderer better and then forget about it */
174             kill (gpg->pid, SIGKILL);
175         }
176         gpg->running = 0;
177     }
178   #endif
179 }
180
181
182 GpgmeError
183 _gpgme_gpg_add_arg ( GpgObject gpg, const char *arg )
184 {
185     struct arg_and_data_s *a;
186
187     assert (gpg);
188     assert (arg);
189     a = xtrymalloc ( sizeof *a + strlen (arg) );
190     if ( !a ) {
191         gpg->arg_error = 1;
192         return mk_error(Out_Of_Core);
193     }
194     a->next = NULL;
195     a->data = NULL;
196     a->dup_to = -1;
197     strcpy ( a->arg, arg );
198     *gpg->argtail = a;
199     gpg->argtail = &a->next;
200     return 0;
201 }
202
203 GpgmeError
204 _gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to )
205 {
206     struct arg_and_data_s *a;
207
208     assert (gpg);
209     assert (data);
210     a = xtrymalloc ( sizeof *a - 1 );
211     if ( !a ) {
212         gpg->arg_error = 1;
213         return mk_error(Out_Of_Core);
214     }
215     a->next = NULL;
216     a->data = data;
217     a->dup_to = dup_to;
218     *gpg->argtail = a;
219     gpg->argtail = &a->next;
220     return 0;
221 }
222
223 /*
224  * Note, that the status_handler is allowed to modifiy the args value
225  */
226 void
227 _gpgme_gpg_set_status_handler ( GpgObject gpg,
228                                 GpgStatusHandler fnc, void *fnc_value ) 
229 {
230     assert (gpg);
231     gpg->status.fnc = fnc;
232     gpg->status.fnc_value = fnc_value;
233 }
234
235 static void
236 free_argv ( char **argv )
237 {
238     int i;
239
240     for (i=0; argv[i]; i++ )
241         xfree (argv[i]);
242     xfree (argv);
243 }
244
245 static void
246 free_fd_data_map ( struct fd_data_map_s *fd_data_map )
247 {
248     int i;
249
250     for (i=0; fd_data_map[i].data; i++ ) {
251         if ( fd_data_map[i].fd != -1 )
252             close (fd_data_map[i].fd);
253         if ( fd_data_map[i].peer_fd != -1 )
254             close (fd_data_map[i].peer_fd);
255         /* don't realease data because this is only a reference */
256     }
257     xfree (fd_data_map);
258 }
259
260
261 static GpgmeError
262 build_argv ( GpgObject gpg )
263 {
264     struct arg_and_data_s *a;
265     struct fd_data_map_s *fd_data_map;
266     size_t datac=0, argc=0;  
267     char **argv;
268     int need_special = 0;
269        
270     if ( gpg->argv ) {
271         free_argv ( gpg->argv );
272         gpg->argv = NULL;
273     }
274     if (gpg->fd_data_map) {
275         free_fd_data_map (gpg->fd_data_map);
276         gpg->fd_data_map = NULL;
277     }
278
279     argc++; /* for argv[0] */
280     for ( a=gpg->arglist; a; a = a->next ) {
281         argc++;
282         if (a->data) {
283             /*fprintf (stderr, "build_argv: data\n" );*/
284             datac++;
285             if ( a->dup_to == -1 )
286                 need_special = 1;
287         }
288         else {
289             /*   fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
290         }
291     }
292     if ( need_special )
293         argc++;
294
295     argv = xtrycalloc ( argc+1, sizeof *argv );
296     if (!argv)
297         return mk_error (Out_Of_Core);
298     fd_data_map = xtrycalloc ( datac+1, sizeof *fd_data_map );
299     if (!fd_data_map) {
300         free_argv (argv);
301         return mk_error (Out_Of_Core);
302     }
303
304     argc = datac = 0;
305     argv[argc] = xtrystrdup ( "gpg" ); /* argv[0] */
306     if (!argv[argc]) {
307         xfree (fd_data_map);
308         free_argv (argv);
309         return mk_error (Out_Of_Core);
310     }
311     argc++;
312     if ( need_special ) {
313         argv[argc] = xtrystrdup ( "--enable-special-filenames" );
314         if (!argv[argc]) {
315             xfree (fd_data_map);
316             free_argv (argv);
317             return mk_error (Out_Of_Core);
318         }
319         argc++;
320     }
321     for ( a=gpg->arglist; a; a = a->next ) {
322         if ( a->data ) {
323             switch ( _gpgme_query_data_mode (a->data) ) {
324               case GPGME_DATA_MODE_NONE:
325               case GPGME_DATA_MODE_INOUT:
326                 xfree (fd_data_map);
327                 free_argv (argv);
328                 return mk_error (Invalid_Mode);
329               case GPGME_DATA_MODE_IN:
330                 /* create a pipe to read from gpg */
331                 fd_data_map[datac].inbound = 1;
332                 break;
333               case GPGME_DATA_MODE_OUT:
334                 /* create a pipe to pass it down to gpg */
335                 fd_data_map[datac].inbound = 0;
336                 break;
337             }
338
339             switch ( gpgme_query_data_type (a->data) ) {
340               case GPGME_DATA_TYPE_NONE:
341                 if ( fd_data_map[datac].inbound )
342                     break;  /* allowed */
343                 xfree (fd_data_map);
344                 free_argv (argv);
345                 return mk_error (Invalid_Type);
346               case GPGME_DATA_TYPE_MEM:
347                 break;
348               case GPGME_DATA_TYPE_FD:
349               case GPGME_DATA_TYPE_FILE:
350                 xfree (fd_data_map);
351                 free_argv (argv);
352                 return mk_error (Not_Implemented);
353             }
354   
355             /* create a pipe */
356             {   
357                 int fds[2];
358                 
359                 if (pipe (fds) == -1) {
360                     xfree (fd_data_map);
361                     free_argv (argv);
362                     return mk_error (Pipe_Error);
363                 }
364                 /* if the data_type is FD, we have to do a dup2 here */
365                 if (fd_data_map[datac].inbound) {
366                     fd_data_map[datac].fd       = fds[0];
367                     fd_data_map[datac].peer_fd  = fds[1];
368                 }
369                 else {
370                     fd_data_map[datac].fd       = fds[1];
371                     fd_data_map[datac].peer_fd  = fds[0];
372                 }
373             }
374             fd_data_map[datac].data = a->data;
375             fd_data_map[datac].dup_to = a->dup_to;
376             if ( a->dup_to == -1 ) {
377                 argv[argc] = xtrymalloc ( 25 );
378                 if (!argv[argc]) {
379                     xfree (fd_data_map);
380                     free_argv (argv);
381                     return mk_error (Out_Of_Core);
382                 }
383                 sprintf ( argv[argc], "-&%d", fd_data_map[datac].peer_fd );
384                 argc++;
385             }
386             datac++;
387         }
388         else {
389             argv[argc] = xtrystrdup ( a->arg );
390             if (!argv[argc]) {
391                 xfree (fd_data_map);
392                 free_argv (argv);
393                 return mk_error (Out_Of_Core);
394             }
395             argc++;
396         }
397     }
398
399     gpg->argv = argv;
400     gpg->fd_data_map = fd_data_map;
401     return 0;
402 }
403
404 GpgmeError
405 _gpgme_gpg_spawn( GpgObject gpg, void *opaque )
406 {
407     int rc;
408     int i;
409     pid_t pid;
410
411     if ( !gpg )
412         return mk_error (Invalid_Value);
413
414     /* Kludge, so that we don't need to check the return code of
415      * all the gpgme_gpg_add_arg().  we bail out here instead */
416     if ( gpg->arg_error )
417         return mk_error (Out_Of_Core);
418
419     rc = build_argv ( gpg );
420     if ( rc )
421         return rc;
422
423     fflush (stderr);
424     pid = fork ();
425     if (pid == -1) {
426         return mk_error (Exec_Error);
427     }
428         
429     if ( !pid ) { /* child */
430         int duped_stdin = 0;
431         int duped_stderr = 0;
432
433         close (gpg->status.fd[0]);
434             
435         for (i=0; gpg->fd_data_map[i].data; i++ ) {
436             close (gpg->fd_data_map[i].fd);
437             gpg->fd_data_map[i].fd = -1;
438             if ( gpg->fd_data_map[i].dup_to != -1 ) {
439                 if ( dup2 (gpg->fd_data_map[i].peer_fd,
440                            gpg->fd_data_map[i].dup_to ) == -1 ) {
441                     fprintf (stderr, "dup2 failed in child: %s\n",
442                              strerror (errno));
443                     _exit (8);
444                 }
445                 if ( gpg->fd_data_map[i].dup_to == 0 )
446                     duped_stdin=1;
447                 if ( gpg->fd_data_map[i].dup_to == 2 )
448                     duped_stderr=1;
449                 close ( gpg->fd_data_map[i].peer_fd );
450             }
451         }
452
453         if( !duped_stdin || !duped_stderr ) {
454             int fd = open ( "/dev/null", O_RDONLY );
455             if ( fd == -1 ) {
456                 fprintf (stderr,"can't open `/dev/null': %s\n",
457                          strerror (errno) );
458                 _exit (8);
459             }
460             /* Make sure that gpg has a connected stdin */
461             if ( !duped_stdin ) {
462                 if ( dup2 ( fd, 0 ) == -1 ) {
463                     fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n",
464                              strerror (errno) );
465                     _exit (8);
466                 }
467             }
468             /* We normally don't want all the normal output */
469             if ( !duped_stderr ) {
470                 if ( dup2 ( fd, 2 ) == -1 ) {
471                     fprintf (stderr,"dup2(dev/null, 2) failed: %s\n",
472                              strerror (errno) );
473                     _exit (8);
474                 }
475             }
476             close (fd);
477         }
478
479         execv ("./gpg", gpg->argv );
480         fprintf (stderr,"exec of gpg failed\n");
481         _exit (8);
482     }
483     /* parent */
484     gpg->pid = pid;
485
486     /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
487
488     if ( gpg->status.fd[1] != -1 )
489         close (gpg->status.fd[1]);
490     if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler,
491                                         gpg, pid, gpg->status.fd[0], 1 ) ) {
492         /* FIXME: kill the child */
493         return mk_error (General_Error);
494
495     }
496     for (i=0; gpg->fd_data_map[i].data; i++ ) {
497         close (gpg->fd_data_map[i].peer_fd);
498         gpg->fd_data_map[i].peer_fd = -1;
499         if ( _gpgme_register_pipe_handler (
500                  opaque, 
501                  gpg->fd_data_map[i].inbound?
502                        gpg_inbound_handler:gpg_outbound_handler,
503                  gpg->fd_data_map[i].data,
504                  pid, gpg->fd_data_map[i].fd,
505                  gpg->fd_data_map[i].inbound )
506            ) {
507             /* FIXME: kill the child */
508             return mk_error (General_Error);
509         }
510     }
511
512     /* fixme: check what data we can release here */
513
514     gpg->running = 1;
515     return 0;
516 }
517
518
519 static int
520 gpg_inbound_handler ( void *opaque, pid_t pid, int fd )
521 {
522     GpgmeData dh = opaque;
523     GpgmeError err;
524     int nread;
525     char buf[200];
526
527     assert ( _gpgme_query_data_mode (dh) == GPGME_DATA_MODE_IN );
528
529     do {
530         nread = read (fd, buf, 200 );
531     } while ( nread == -1 && errno == EINTR);
532     fprintf(stderr, "inbound on fd %d: nread=%d\n", fd, nread );
533     if ( nread < 0 ) {
534         fprintf (stderr, "read_mem_data: read failed on fd %d (n=%d): %s\n",
535                  fd, nread, strerror (errno) );
536         return 1;
537     }
538     else if (!nread)
539         return 1; /* eof */
540
541     /* We could improve this with a GpgmeData function which takes
542      * the read function or provides a memory area for writing to it.
543      */
544     
545     err = _gpgme_append_data ( dh, buf, nread );
546     if ( err ) {
547         fprintf (stderr, "_gpgme_append_data failed: %s\n",
548                  gpgme_strerror(err));
549         /* Fixme: we should close the pipe or read it to /dev/null in
550          * this case. Returnin EOF is not sufficient */
551         return 1;
552     }
553
554     return 0;
555 }
556
557
558 static int
559 write_mem_data ( GpgmeData dh, int fd )
560 {
561     size_t nbytes;
562     int  nwritten; 
563
564     nbytes = dh->len - dh->readpos;
565     if ( !nbytes ) {
566         close (fd);
567         return 1;
568     }
569     
570     do {
571         nwritten = write ( fd, dh->data+dh->readpos, nbytes );
572     } while ( nwritten == -1 && errno == EINTR );
573     if ( nwritten < 1 ) {
574         fprintf (stderr, "write_mem_data: write failed on fd %d (n=%d): %s\n",
575                  fd, nwritten, strerror (errno) );
576         close (fd);
577         return 1;
578     }
579
580     dh->readpos += nwritten;
581     return 0;
582 }
583
584
585 static int
586 gpg_outbound_handler ( void *opaque, pid_t pid, int fd )
587 {
588     GpgmeData dh = opaque;
589
590     assert ( _gpgme_query_data_mode (dh) == GPGME_DATA_MODE_OUT );
591     switch ( gpgme_query_data_type (dh) ) {
592       case GPGME_DATA_TYPE_MEM:
593         if ( write_mem_data ( dh, fd ) )
594             return 1; /* ready */
595         break;
596       default:
597         assert (0);
598     }
599
600
601     return 0;
602 }
603
604
605
606 static int
607 gpg_status_handler ( void *opaque, pid_t pid, int fd )
608 {
609     GpgObject gpg = opaque;
610     int rc = 0;
611
612     assert ( fd == gpg->status.fd[0] );
613     rc = read_status ( gpg );
614     if ( rc ) {
615         fprintf (stderr, "gpg_handler: read_status problem %d\n - stop", rc);
616         return 1;
617     }
618
619     return gpg->status.eof;
620 }
621
622
623 static int
624 status_cmp (const void *ap, const void *bp)
625 {
626     const struct status_table_s *a = ap;
627     const struct status_table_s *b = bp;
628
629     return strcmp (a->name, b->name);
630 }
631
632
633
634 /*
635  * Handle the status output of GnuPG.  This function does read entire
636  * lines and passes them as C strings to the callback function (we can
637  * use C Strings because the status output is always UTF-8 encoded).
638  * Of course we have to buffer the lines to cope with long lines
639  * e.g. with a large user ID.  Note: We can optimize this to only cope
640  * with status line code we know about and skip all other stuff
641  * without buffering (i.e. without extending the buffer).  */
642 static GpgmeError
643 read_status ( GpgObject gpg )
644 {
645     char *p;
646     int nread;
647     size_t bufsize = gpg->status.bufsize; 
648     char *buffer = gpg->status.buffer;
649     size_t readpos = gpg->status.readpos; 
650
651     assert (buffer);
652     if (bufsize - readpos < 256) { 
653         /* need more room for the read */
654         bufsize += 1024;
655         buffer = xtryrealloc (buffer, bufsize);
656         if ( !buffer ) 
657             return mk_error (Out_Of_Core);
658     }
659     
660
661     do { 
662         nread = read ( gpg->status.fd[0], buffer+readpos, bufsize-readpos );
663     } while (nread == -1 && errno == EINTR);
664
665     if (nread == -1)
666         return mk_error(Read_Error);
667
668     if (!nread) {
669         gpg->status.eof = 1;
670         if (gpg->status.fnc)
671             gpg->status.fnc ( gpg->status.fnc_value, STATUS_EOF, "" );
672         return 0;
673     }
674
675     while (nread > 0) {
676         for (p = buffer + readpos; nread; nread--, p++) {
677             if ( *p == '\n' ) {
678                 /* (we require that the last line is terminated by a LF) */
679                 *p = 0;
680                 fprintf (stderr, "read_status: `%s'\n", buffer);
681                 if (!strncmp (buffer, "[GNUPG:] ", 9 )
682                     && buffer[9] >= 'A' && buffer[9] <= 'Z'
683                     && gpg->status.fnc ) {
684                     struct status_table_s t, *r;
685                     char *rest;
686
687                     rest = strchr (buffer+9, ' ');
688                     if ( !rest )
689                         rest = p; /* set to an empty string */
690                     else
691                         *rest++ = 0;
692                     
693                     t.name = buffer+9;
694                     /* (the status table as one extra element) */
695                     r = bsearch ( &t, status_table, DIM(status_table)-1,
696                                   sizeof t, status_cmp );
697                     if ( r ) {
698                         gpg->status.fnc ( gpg->status.fnc_value, 
699                                           r->code, rest);
700                     }
701                 }
702                 /* To reuse the buffer for the next line we have to
703                  * shift the remaining data to the buffer start and
704                  * restart the loop Hmmm: We can optimize this
705                  * function by looking forward in the buffer to see
706                  * whether a second complete line is available and in
707                  * this case avoid the memmove for this line.  */
708                 nread--; p++;
709                 if (nread)
710                     memmove (buffer, p, nread);
711                 readpos = 0;
712                 break; /* the for loop */
713             }
714             else
715                 readpos++;
716         }
717     } 
718
719     /* Update the gpg object.  */
720     gpg->status.bufsize = bufsize;
721     gpg->status.buffer = buffer;
722     gpg->status.readpos = readpos;
723     return 0;
724 }
725
726