chiark / gitweb /
Silence two -Wlogical-op warnings.
[gnupg2.git] / common / exectool.c
1 /* exectool.c - Utility functions to execute a helper tool
2  * Copyright (C) 2015 Werner Koch
3  * Copyright (C) 2016 g10 Code GmbH
4  *
5  * This file is part of GnuPG.
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of either
9  *
10  *   - the GNU Lesser General Public License as published by the Free
11  *     Software Foundation; either version 3 of the License, or (at
12  *     your option) any later version.
13  *
14  * or
15  *
16  *   - the GNU General Public License as published by the Free
17  *     Software Foundation; either version 2 of the License, or (at
18  *     your option) any later version.
19  *
20  * or both in parallel, as here.
21  *
22  * This file is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, see <https://www.gnu.org/licenses/>.
29  */
30
31 #include <config.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <gpg-error.h>
39
40 #include <assuan.h>
41 #include "i18n.h"
42 #include "logging.h"
43 #include "membuf.h"
44 #include "mischelp.h"
45 #include "exechelp.h"
46 #include "sysutils.h"
47 #include "util.h"
48 #include "exectool.h"
49
50 typedef struct
51 {
52   const char *pgmname;
53   exec_tool_status_cb_t status_cb;
54   void *status_cb_value;
55   int cont;
56   size_t used;
57   size_t buffer_size;
58   char *buffer;
59 } read_and_log_buffer_t;
60
61
62 static inline gpg_error_t
63 my_error_from_syserror (void)
64 {
65   return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
66 }
67
68
69 static void
70 read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
71 {
72   gpg_error_t err;
73   int c;
74
75   if (!fderr)
76     {
77       /* Flush internal buffer.  */
78       if (state->used)
79         {
80           const char *pname;
81           int len;
82
83           state->buffer[state->used] = 0;
84           state->used = 0;
85
86           pname = strrchr (state->pgmname, '/');
87           if (pname && pname != state->pgmname && pname[1])
88             pname++;
89           else
90             pname = state->pgmname;
91           len = strlen (pname);
92
93           if (state->status_cb
94               && !strncmp (state->buffer, "[GNUPG:] ", 9)
95               && state->buffer[9] >= 'A' && state->buffer[9] <= 'Z')
96             {
97               char *rest;
98
99               rest = strchr (state->buffer + 9, ' ');
100               if (!rest)
101                 {
102                   /* Set REST to an empty string.  */
103                   rest = state->buffer + strlen (state->buffer);
104                 }
105               else
106                 {
107                   *rest++ = 0;
108                   trim_spaces (rest);
109                 }
110               state->status_cb (state->status_cb_value,
111                                 state->buffer + 9, rest);
112             }
113           else if (!state->cont
114               && !strncmp (state->buffer, pname, len)
115               && strlen (state->buffer) > strlen (pname)
116               && state->buffer[len] == ':' )
117             {
118               /* PGMNAME plus colon is identical to the start of
119                  the output: print only the output.  */
120               log_info ("%s\n", state->buffer);
121             }
122           else
123             log_info ("%s%c %s\n",
124                       pname, state->cont? '+':':', state->buffer);
125         }
126       state->cont = 0;
127       return;
128     }
129   for (;;)
130     {
131       c = es_fgetc (fderr->stream);
132       if (c == EOF)
133         {
134           if (es_feof (fderr->stream))
135             {
136               fderr->ignore = 1; /* Not anymore needed.  */
137             }
138           else if (es_ferror (fderr->stream))
139             {
140               err = my_error_from_syserror ();
141               log_error ("error reading stderr of '%s': %s\n",
142                          state->pgmname, gpg_strerror (err));
143               fderr->ignore = 1; /* Disable.  */
144             }
145
146           break;
147         }
148       else if (c == '\n')
149         {
150           read_and_log_stderr (state, NULL);
151         }
152       else
153         {
154           if (state->used >= state->buffer_size - 1)
155             {
156               if (state->status_cb)
157                 {
158                   /* A status callback requires that we have a full
159                    * line.  Thus we need to enlarget the buffer in
160                    * this case.  */
161                   char *newbuffer;
162                   size_t newsize = state->buffer_size + 256;
163
164                   newbuffer = xtrymalloc (newsize);
165                   if (!newbuffer)
166                     {
167                       log_error ("error allocating memory for status cb: %s\n",
168                                  gpg_strerror (my_error_from_syserror ()));
169                       /* We better disable the status CB in this case.  */
170                       state->status_cb = NULL;
171                       read_and_log_stderr (state, NULL);
172                       state->cont = 1;
173                     }
174                   else
175                     {
176                       memcpy (newbuffer, state->buffer, state->used);
177                       xfree (state->buffer);
178                       state->buffer = newbuffer;
179                       state->buffer_size = newsize;
180                     }
181                 }
182               else
183                 {
184                   read_and_log_stderr (state, NULL);
185                   state->cont = 1;
186                 }
187             }
188           state->buffer[state->used++] = c;
189         }
190     }
191 }
192
193 \f
194
195 /* A buffer to copy from one stream to another.  */
196 struct copy_buffer
197 {
198   char buffer[4096];
199   char *writep;
200   size_t nread;
201 };
202
203
204 /* Initialize a copy buffer.  */
205 static void
206 copy_buffer_init (struct copy_buffer *c)
207 {
208   c->writep = c->buffer;
209   c->nread = 0;
210 }
211
212
213 /* Securely wipe a copy buffer.  */
214 static void
215 copy_buffer_shred (struct copy_buffer *c)
216 {
217   if (c == NULL)
218     return;
219   wipememory (c->buffer, sizeof c->buffer);
220   c->writep = NULL;
221   c->nread = ~0U;
222 }
223
224
225 /* Copy data from SOURCE to SINK using copy buffer C.  */
226 static gpg_error_t
227 copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink)
228 {
229   gpg_error_t err;
230   size_t nwritten = 0;
231
232   if (c->nread == 0)
233     {
234       c->writep = c->buffer;
235       err = es_read (source, c->buffer, sizeof c->buffer, &c->nread);
236       if (err)
237         {
238           if (errno == EAGAIN)
239             return 0;   /* We will just retry next time.  */
240
241           return my_error_from_syserror ();
242         }
243
244       assert (c->nread <= sizeof c->buffer);
245     }
246
247   if (c->nread == 0)
248     return 0;   /* Done copying.  */
249
250
251   nwritten = 0;
252   err = sink? es_write (sink, c->writep, c->nread, &nwritten) : 0;
253
254   assert (nwritten <= c->nread);
255   c->writep += nwritten;
256   c->nread -= nwritten;
257   assert (c->writep - c->buffer <= sizeof c->buffer);
258
259   if (err)
260     {
261       if (errno == EAGAIN)
262         return 0;       /* We will just retry next time.  */
263
264       return my_error_from_syserror ();
265     }
266
267   if (sink && es_fflush (sink) && errno != EAGAIN)
268     err = my_error_from_syserror ();
269
270   return err;
271 }
272
273
274 /* Flush the remaining data to SINK.  */
275 static gpg_error_t
276 copy_buffer_flush (struct copy_buffer *c, estream_t sink)
277 {
278   gpg_error_t err;
279
280   while (c->nread > 0)
281     {
282       err = copy_buffer_do_copy (c, NULL, sink);
283       if (err)
284         return err;
285     }
286
287   return 0;
288 }
289
290 \f
291
292 /* Run the program PGMNAME with the command line arguments given in
293  * the NULL terminates array ARGV.  If INPUT is not NULL it will be
294  * fed to stdin of the process.  stderr is logged using log_info and
295  * the process' stdout is written to OUTPUT.  If OUTPUT is NULL the
296  * output is discarded.  If INEXTRA is given, an additional input
297  * stream will be passed to the child; to tell the child about this
298  * ARGV is scanned and the first occurrence of an argument
299  * "-&@INEXTRA@" is replaced by the concatenation of "-&" and the
300  * child's file descriptor of the pipe created for the INEXTRA stream.
301  *
302  * On error a diagnostic is printed and an error code returned.  */
303 gpg_error_t
304 gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
305                         estream_t input, estream_t inextra,
306                         estream_t output,
307                         exec_tool_status_cb_t status_cb,
308                         void *status_cb_value)
309 {
310   gpg_error_t err;
311   pid_t pid = (pid_t) -1;
312   estream_t infp = NULL;
313   estream_t extrafp = NULL;
314   estream_t outfp = NULL, errfp = NULL;
315   es_poll_t fds[4];
316   int exceptclose[2];
317   int extrapipe[2] = {-1, -1};
318   char extrafdbuf[20];
319   const char *argsave = NULL;
320   int argsaveidx;
321   int count;
322   read_and_log_buffer_t fderrstate;
323   struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL;
324
325   memset (fds, 0, sizeof fds);
326   memset (&fderrstate, 0, sizeof fderrstate);
327
328   cpbuf_in = xtrymalloc (sizeof *cpbuf_in);
329   if (cpbuf_in == NULL)
330     {
331       err = my_error_from_syserror ();
332       goto leave;
333     }
334   copy_buffer_init (cpbuf_in);
335
336   cpbuf_out = xtrymalloc (sizeof *cpbuf_out);
337   if (cpbuf_out == NULL)
338     {
339       err = my_error_from_syserror ();
340       goto leave;
341     }
342   copy_buffer_init (cpbuf_out);
343
344   cpbuf_extra = xtrymalloc (sizeof *cpbuf_extra);
345   if (cpbuf_extra == NULL)
346     {
347       err = my_error_from_syserror ();
348       goto leave;
349     }
350   copy_buffer_init (cpbuf_extra);
351
352   fderrstate.pgmname = pgmname;
353   fderrstate.status_cb = status_cb;
354   fderrstate.status_cb_value = status_cb_value;
355   fderrstate.buffer_size = 256;
356   fderrstate.buffer = xtrymalloc (fderrstate.buffer_size);
357   if (!fderrstate.buffer)
358     {
359       err = my_error_from_syserror ();
360       goto leave;
361     }
362
363   if (inextra)
364     {
365       err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1);
366       if (err)
367         {
368           log_error ("error running outbound pipe for extra fp: %s\n",
369                      gpg_strerror (err));
370           goto leave;
371         }
372       exceptclose[0] = extrapipe[0]; /* Do not close in child. */
373       exceptclose[1] = -1;
374       /* Now find the argument marker and replace by the pipe's fd.
375          Yeah, that is an ugly non-thread safe hack but it safes us to
376          create a copy of the array.  */
377       snprintf (extrafdbuf, sizeof extrafdbuf, "-&%d", extrapipe[0]);
378       for (argsaveidx=0; argv[argsaveidx]; argsaveidx++)
379         if (!strcmp (argv[argsaveidx], "-&@INEXTRA@"))
380           {
381             argsave = argv[argsaveidx];
382             argv[argsaveidx] = extrafdbuf;
383             break;
384           }
385     }
386   else
387     exceptclose[0] = -1;
388
389   err = gnupg_spawn_process (pgmname, argv,
390                              exceptclose, NULL, GNUPG_SPAWN_NONBLOCK,
391                              input? &infp : NULL,
392                              &outfp, &errfp, &pid);
393   if (extrapipe[0] != -1)
394     close (extrapipe[0]);
395   if (argsave)
396     argv[argsaveidx] = argsave;
397   if (err)
398     {
399       log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
400       goto leave;
401     }
402
403   fds[0].stream = infp;
404   fds[0].want_write = 1;
405   if (!input)
406     fds[0].ignore = 1;
407   fds[1].stream = outfp;
408   fds[1].want_read = 1;
409   fds[2].stream = errfp;
410   fds[2].want_read = 1;
411   fds[3].stream = extrafp;
412   fds[3].want_write = 1;
413   if (!inextra)
414     fds[3].ignore = 1;
415
416   /* Now read as long as we have something to poll.  We continue
417      reading even after EOF or error on stdout so that we get the
418      other error messages or remaining outut.  */
419   while (! (fds[1].ignore && fds[2].ignore))
420     {
421       count = es_poll (fds, DIM(fds), -1);
422       if (count == -1)
423         {
424           err = my_error_from_syserror ();
425           log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
426           goto leave;
427         }
428       if (!count)
429         {
430           log_debug ("unexpected timeout while polling '%s'\n", pgmname);
431           break;
432         }
433
434       if (fds[0].got_write)
435         {
436           err = copy_buffer_do_copy (cpbuf_in, input, fds[0].stream);
437           if (err)
438             {
439               log_error ("error feeding data to '%s': %s\n",
440                          pgmname, gpg_strerror (err));
441               goto leave;
442             }
443
444           if (es_feof (input))
445             {
446               err = copy_buffer_flush (cpbuf_in, fds[0].stream);
447               if (err)
448                 {
449                   log_error ("error feeding data to '%s': %s\n",
450                              pgmname, gpg_strerror (err));
451                   goto leave;
452                 }
453
454               fds[0].ignore = 1; /* ready.  */
455               es_fclose (infp); infp = NULL;
456             }
457         }
458
459       if (fds[3].got_write)
460         {
461           log_assert (inextra);
462           err = copy_buffer_do_copy (cpbuf_extra, inextra, fds[3].stream);
463           if (err)
464             {
465               log_error ("error feeding data to '%s': %s\n",
466                          pgmname, gpg_strerror (err));
467               goto leave;
468             }
469
470           if (es_feof (inextra))
471             {
472               err = copy_buffer_flush (cpbuf_extra, fds[3].stream);
473               if (err)
474                 {
475                   log_error ("error feeding data to '%s': %s\n",
476                              pgmname, gpg_strerror (err));
477                   goto leave;
478                 }
479
480               fds[3].ignore = 1; /* ready.  */
481               es_fclose (extrafp); extrafp = NULL;
482             }
483         }
484
485       if (fds[1].got_read)
486         {
487           err = copy_buffer_do_copy (cpbuf_out, fds[1].stream, output);
488           if (err)
489             {
490               log_error ("error reading data from '%s': %s\n",
491                          pgmname, gpg_strerror (err));
492               goto leave;
493             }
494
495           if (es_feof (fds[1].stream))
496             {
497               err = copy_buffer_flush (cpbuf_out, output);
498               if (err)
499                 {
500                   log_error ("error reading data from '%s': %s\n",
501                              pgmname, gpg_strerror (err));
502                   goto leave;
503                 }
504
505               fds[1].ignore = 1; /* ready.  */
506             }
507         }
508
509       if (fds[2].got_read)
510         read_and_log_stderr (&fderrstate, fds + 2);
511     }
512
513   read_and_log_stderr (&fderrstate, NULL); /* Flush.  */
514   es_fclose (infp); infp = NULL;
515   es_fclose (extrafp); extrafp = NULL;
516   es_fclose (outfp); outfp = NULL;
517   es_fclose (errfp); errfp = NULL;
518
519   err = gnupg_wait_process (pgmname, pid, 1, NULL);
520   pid = (pid_t)(-1);
521
522  leave:
523   if (err && pid != (pid_t) -1)
524     gnupg_kill_process (pid);
525
526   es_fclose (infp);
527   es_fclose (extrafp);
528   es_fclose (outfp);
529   es_fclose (errfp);
530   if (pid != (pid_t)(-1))
531     gnupg_wait_process (pgmname, pid, 1, NULL);
532   gnupg_release_process (pid);
533
534   copy_buffer_shred (cpbuf_in);
535   xfree (cpbuf_in);
536   copy_buffer_shred (cpbuf_out);
537   xfree (cpbuf_out);
538   copy_buffer_shred (cpbuf_extra);
539   xfree (cpbuf_extra);
540   xfree (fderrstate.buffer);
541   return err;
542 }
543
544
545 /* A dummy free function to pass to 'es_mopen'.  */
546 static void
547 nop_free (void *ptr)
548 {
549   (void) ptr;
550 }
551
552 /* Run the program PGMNAME with the command line arguments given in
553    the NULL terminates array ARGV.  If INPUT_STRING is not NULL it
554    will be fed to stdin of the process.  stderr is logged using
555    log_info and the process' stdout is returned in a newly malloced
556    buffer RESULT with the length stored at RESULTLEN if not given as
557    NULL.  A hidden Nul is appended to the output.  On error NULL is
558    stored at RESULT, a diagnostic is printed, and an error code
559    returned.  */
560 gpg_error_t
561 gnupg_exec_tool (const char *pgmname, const char *argv[],
562                  const char *input_string,
563                  char **result, size_t *resultlen)
564 {
565   gpg_error_t err;
566   estream_t input = NULL;
567   estream_t output;
568   size_t len;
569   size_t nread;
570
571   *result = NULL;
572   if (resultlen)
573     *resultlen = 0;
574
575   if (input_string)
576     {
577       len = strlen (input_string);
578       input = es_mopen ((char *) input_string, len, len,
579                         0 /* don't grow */, NULL, nop_free, "rb");
580       if (! input)
581         return my_error_from_syserror ();
582     }
583
584   output = es_fopenmem (0, "wb");
585   if (! output)
586     {
587       err = my_error_from_syserror ();
588       goto leave;
589     }
590
591   err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output, NULL, NULL);
592   if (err)
593     goto leave;
594
595   len = es_ftello (output);
596   err = es_fseek (output, 0, SEEK_SET);
597   if (err)
598     goto leave;
599
600   *result = xtrymalloc (len + 1);
601   if (!*result)
602     {
603       err = my_error_from_syserror ();
604       goto leave;
605     }
606
607   if (len)
608     {
609       err = es_read (output, *result, len, &nread);
610       if (err)
611         goto leave;
612       if (nread != len)
613         log_fatal ("%s: short read from memstream\n", __func__);
614     }
615   (*result)[len] = 0;
616
617   if (resultlen)
618     *resultlen = len;
619
620  leave:
621   es_fclose (input);
622   es_fclose (output);
623   if (err)
624     {
625       xfree (*result);
626       *result = NULL;
627     }
628   return err;
629 }