common: Add a stream interface to 'sh-exectool'.
[gnupg.git] / common / sh-exectool.c
1 /* sh-exectool.c - Utility functions to execute a helper tool
2  * Copyright (C) 2015 Werner Koch
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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdarg.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <gpg-error.h>
28
29 #include <assuan.h>
30 #include "i18n.h"
31 #include "logging.h"
32 #include "membuf.h"
33 #include "mischelp.h"
34 #include "exechelp.h"
35 #include "sysutils.h"
36 #include "util.h"
37
38 typedef struct
39 {
40   const char *pgmname;
41   int cont;
42   int used;
43   char buffer[256];
44 } read_and_log_buffer_t;
45
46
47 static void
48 read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
49 {
50   gpg_error_t err;
51   int c;
52
53   if (!fderr)
54     {
55       /* Flush internal buffer.  */
56       if (state->used)
57         {
58           const char *pname;
59           int len;
60
61           state->buffer[state->used] = 0;
62           state->used = 0;
63
64           pname = strrchr (state->pgmname, '/');
65           if (pname && pname != state->pgmname && pname[1])
66             pname++;
67           else
68             pname = state->pgmname;
69           /* If our pgmname plus colon is identical to the start of
70              the output, print only the output.  */
71           len = strlen (pname);
72           if (!state->cont
73               && !strncmp (state->buffer, pname, len)
74               && strlen (state->buffer) > strlen (pname)
75               && state->buffer[len] == ':' )
76             log_info ("%s\n", state->buffer);
77           else
78             log_info ("%s%c %s\n",
79                       pname, state->cont? '+':':', state->buffer);
80         }
81       state->cont = 0;
82       return;
83     }
84   for (;;)
85     {
86       c = es_fgetc (fderr->stream);
87       if (c == EOF)
88         {
89           if (es_feof (fderr->stream))
90             {
91               fderr->ignore = 1; /* Not anymore needed.  */
92             }
93           else if (es_ferror (fderr->stream))
94             {
95               err = gpg_error_from_syserror ();
96               log_error ("error reading stderr of '%s': %s\n",
97                          state->pgmname, gpg_strerror (err));
98               fderr->ignore = 1; /* Disable.  */
99             }
100
101           break;
102         }
103       else if (c == '\n')
104         {
105           read_and_log_stderr (state, NULL);
106         }
107       else
108         {
109           if (state->used >= sizeof state->buffer - 1)
110             {
111               read_and_log_stderr (state, NULL);
112               state->cont = 1;
113             }
114           state->buffer[state->used++] = c;
115         }
116     }
117 }
118
119 \f
120
121 /* A buffer to copy from one stream to another.  */
122 struct copy_buffer
123 {
124   char buffer[4096];
125   char *writep;
126   size_t nread;
127 };
128
129
130 /* Initialize a copy buffer.  */
131 static void
132 copy_buffer_init (struct copy_buffer *c)
133 {
134   c->writep = c->buffer;
135   c->nread = 0;
136 }
137
138
139 /* Securely wipe a copy buffer.  */
140 static void
141 copy_buffer_shred (struct copy_buffer *c)
142 {
143   wipememory (c->buffer, sizeof c->buffer);
144   c->writep = NULL;
145   c->nread = ~0U;
146 }
147
148
149 /* Copy data from SOURCE to SINK using copy buffer C.  */
150 static gpg_error_t
151 copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink)
152 {
153   gpg_error_t err;
154   size_t nwritten;
155
156   if (c->nread == 0)
157     {
158       c->writep = c->buffer;
159       err = es_read (source, c->buffer, sizeof c->buffer, &c->nread);
160       if (err)
161         {
162           if (errno == EAGAIN)
163             return 0;   /* We will just retry next time.  */
164
165           return gpg_error_from_syserror ();
166         }
167
168       assert (c->nread <= sizeof c->buffer);
169     }
170
171   if (c->nread == 0)
172     return 0;   /* Done copying.  */
173
174   err = es_write (sink, c->writep, c->nread, &nwritten);
175   if (err)
176     {
177       if (errno == EAGAIN)
178         return 0;       /* We will just retry next time.  */
179
180       return gpg_error_from_syserror ();
181     }
182
183   assert (nwritten <= c->nread);
184   c->writep += nwritten;
185   c->nread -= nwritten;
186   assert (c->writep - c->buffer <= sizeof c->buffer);
187
188   if (es_fflush (sink) && errno != EAGAIN)
189     err = gpg_error_from_syserror ();
190
191   return err;
192 }
193
194
195 /* Flush the remaining data to SINK.  */
196 static gpg_error_t
197 copy_buffer_flush (struct copy_buffer *c, estream_t sink)
198 {
199   gpg_error_t err;
200
201   while (c->nread > 0)
202     {
203       err = copy_buffer_do_copy (c, NULL, sink);
204       if (err)
205         return err;
206     }
207
208   return 0;
209 }
210
211 \f
212
213 /* Run the program PGMNAME with the command line arguments given in
214    the NULL terminates array ARGV.  If INPUT is not NULL it will be
215    fed to stdin of the process.  stderr is logged using log_info and
216    the process' stdout is written to OUTPUT.  On error a diagnostic is
217    printed, and an error code returned.  */
218 gpg_error_t
219 sh_exec_tool_stream (const char *pgmname, const char *argv[],
220                      estream_t input,
221                      estream_t output)
222 {
223   gpg_error_t err;
224   pid_t pid;
225   estream_t infp = NULL;
226   estream_t outfp, errfp;
227   es_poll_t fds[3];
228   int count;
229   read_and_log_buffer_t fderrstate;
230   struct copy_buffer cpbuf[2];
231
232   memset (fds, 0, sizeof fds);
233   memset (&fderrstate, 0, sizeof fderrstate);
234   copy_buffer_init (&cpbuf[0]);
235   copy_buffer_init (&cpbuf[1]);
236
237   err = gnupg_spawn_process (pgmname, argv, GPG_ERR_SOURCE_DEFAULT,
238                              NULL, GNUPG_SPAWN_NONBLOCK,
239                              input? &infp : NULL,
240                              &outfp, &errfp, &pid);
241   if (err)
242     {
243       log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
244       return err;
245     }
246
247   fderrstate.pgmname = pgmname;
248
249   fds[0].stream = infp;
250   fds[0].want_write = 1;
251   if (!input)
252     fds[0].ignore = 1;
253   fds[1].stream = outfp;
254   fds[1].want_read = 1;
255   fds[2].stream = errfp;
256   fds[2].want_read = 1;
257   /* Now read as long as we have something to poll.  We continue
258      reading even after EOF or error on stdout so that we get the
259      other error messages or remaining outout.  */
260   while (!fds[1].ignore && !fds[2].ignore)
261     {
262       count = es_poll (fds, DIM(fds), -1);
263       if (count == -1)
264         {
265           err = gpg_error_from_syserror ();
266           log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
267           goto leave;
268         }
269       if (!count)
270         {
271           log_debug ("unexpected timeout while polling '%s'\n", pgmname);
272           break;
273         }
274
275       if (fds[0].got_write)
276         {
277           err = copy_buffer_do_copy (&cpbuf[0], input, fds[0].stream);
278           if (err)
279             {
280               log_error ("error feeding data to '%s': %s\n",
281                          pgmname, gpg_strerror (err));
282               goto leave;
283             }
284
285           if (es_feof (input))
286             {
287               err = copy_buffer_flush (&cpbuf[0], fds[0].stream);
288               if (err)
289                 {
290                   log_error ("error feeding data to '%s': %s\n",
291                              pgmname, gpg_strerror (err));
292                   goto leave;
293                 }
294
295               fds[0].ignore = 1; /* ready.  */
296               es_fclose (infp); infp = NULL;
297             }
298         }
299
300       if (fds[1].got_read)
301         {
302           err = copy_buffer_do_copy (&cpbuf[1], fds[1].stream, output);
303           if (err)
304             {
305               log_error ("error reading data from '%s': %s\n",
306                          pgmname, gpg_strerror (err));
307               goto leave;
308             }
309         }
310
311       if (fds[2].got_read)
312         read_and_log_stderr (&fderrstate, fds + 2);
313     }
314
315   err = copy_buffer_flush (&cpbuf[1], output);
316   if (err)
317     {
318       log_error ("error reading data from '%s': %s\n",
319                  pgmname, gpg_strerror (err));
320       goto leave;
321     }
322
323   read_and_log_stderr (&fderrstate, NULL); /* Flush.  */
324   es_fclose (infp); infp = NULL;
325   es_fclose (outfp); outfp = NULL;
326   es_fclose (errfp); errfp = NULL;
327
328   err = gnupg_wait_process (pgmname, pid, 1, NULL);
329   pid = (pid_t)(-1);
330
331  leave:
332   if (err)
333     gnupg_kill_process (pid);
334
335   es_fclose (infp);
336   es_fclose (outfp);
337   es_fclose (errfp);
338   if (pid != (pid_t)(-1))
339     gnupg_wait_process (pgmname, pid, 1, NULL);
340   gnupg_release_process (pid);
341
342   copy_buffer_shred (&cpbuf[0]);
343   copy_buffer_shred (&cpbuf[1]);
344   return err;
345 }
346
347
348 /* A dummy free function to pass to 'es_mopen'.  */
349 static void
350 nop_free (void *ptr)
351 {
352   (void) ptr;
353 }
354
355 /* Run the program PGMNAME with the command line arguments given in
356    the NULL terminates array ARGV.  If INPUT_STRING is not NULL it
357    will be fed to stdin of the process.  stderr is logged using
358    log_info and the process' stdout is returned in a newly malloced
359    buffer RESULT with the length stored at RESULTLEN if not given as
360    NULL.  A hidden Nul is appended to the output.  On error NULL is
361    stored at RESULT, a diagnostic is printed, and an error code
362    returned.  */
363 gpg_error_t
364 sh_exec_tool (const char *pgmname, const char *argv[],
365               const char *input_string,
366               char **result, size_t *resultlen)
367 {
368   gpg_error_t err;
369   estream_t input = NULL;
370   estream_t output;
371   size_t len;
372   size_t nread;
373
374   *result = NULL;
375   if (resultlen)
376     *resultlen = 0;
377
378   if (input_string)
379     {
380       len = strlen (input_string);
381       input = es_mopen ((char *) input_string, len, len,
382                         0 /* don't grow */, NULL, nop_free, "rb");
383       if (! input)
384         return gpg_error_from_syserror ();
385     }
386
387   output = es_fopenmem (0, "wb");
388   if (! output)
389     {
390       err = gpg_error_from_syserror ();
391       goto leave;
392     }
393
394   err = sh_exec_tool_stream (pgmname, argv, input, output);
395   if (err)
396     goto leave;
397
398   len = es_ftello (output);
399   err = es_fseek (output, 0, SEEK_SET);
400   if (err)
401     goto leave;
402
403   *result = xtrymalloc (len);
404   if (*result == NULL)
405     {
406       err = gpg_error_from_syserror ();
407       goto leave;
408     }
409
410   err = es_read (output, *result, len, &nread);
411   if (! err)
412     {
413       assert (nread == len || !"short read on memstream");
414       if (resultlen)
415         *resultlen = len;
416     }
417
418  leave:
419   if (input)
420     es_fclose (input);
421   es_fclose (output);
422   return err;
423 }