1 /* exectool.c - Utility functions to execute a helper tool
2 * Copyright (C) 2015 Werner Koch
3 * Copyright (C) 2016 g10 Code GmbH
5 * This file is part of GnuPG.
7 * This file is free software; you can redistribute it and/or modify
8 * it under the terms of either
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.
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.
20 * or both in parallel, as here.
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.
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/>.
38 #include <gpg-error.h>
53 exec_tool_status_cb_t status_cb;
54 void *status_cb_value;
59 } read_and_log_buffer_t;
62 static inline gpg_error_t
63 my_error_from_syserror (void)
65 return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
70 read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
77 /* Flush internal buffer. */
83 state->buffer[state->used] = 0;
86 pname = strrchr (state->pgmname, '/');
87 if (pname && pname != state->pgmname && pname[1])
90 pname = state->pgmname;
94 && !strncmp (state->buffer, "[GNUPG:] ", 9)
95 && state->buffer[9] >= 'A' && state->buffer[9] <= 'Z')
99 rest = strchr (state->buffer + 9, ' ');
102 /* Set REST to an empty string. */
103 rest = state->buffer + strlen (state->buffer);
110 state->status_cb (state->status_cb_value,
111 state->buffer + 9, rest);
113 else if (!state->cont
114 && !strncmp (state->buffer, pname, len)
115 && strlen (state->buffer) > strlen (pname)
116 && state->buffer[len] == ':' )
118 /* PGMNAME plus colon is identical to the start of
119 the output: print only the output. */
120 log_info ("%s\n", state->buffer);
123 log_info ("%s%c %s\n",
124 pname, state->cont? '+':':', state->buffer);
131 c = es_fgetc (fderr->stream);
134 if (es_feof (fderr->stream))
136 fderr->ignore = 1; /* Not anymore needed. */
138 else if (es_ferror (fderr->stream))
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. */
150 read_and_log_stderr (state, NULL);
154 if (state->used >= state->buffer_size - 1)
156 if (state->status_cb)
158 /* A status callback requires that we have a full
159 * line. Thus we need to enlarget the buffer in
162 size_t newsize = state->buffer_size + 256;
164 newbuffer = xtrymalloc (newsize);
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);
176 memcpy (newbuffer, state->buffer, state->used);
177 xfree (state->buffer);
178 state->buffer = newbuffer;
179 state->buffer_size = newsize;
184 read_and_log_stderr (state, NULL);
188 state->buffer[state->used++] = c;
195 /* A buffer to copy from one stream to another. */
204 /* Initialize a copy buffer. */
206 copy_buffer_init (struct copy_buffer *c)
208 c->writep = c->buffer;
213 /* Securely wipe a copy buffer. */
215 copy_buffer_shred (struct copy_buffer *c)
219 wipememory (c->buffer, sizeof c->buffer);
225 /* Copy data from SOURCE to SINK using copy buffer C. */
227 copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink)
234 c->writep = c->buffer;
235 if (es_read (source, c->buffer, sizeof c->buffer, &c->nread))
237 err = my_error_from_syserror ();
238 if (gpg_err_code (err) == GPG_ERR_EAGAIN)
239 return 0; /* We will just retry next time. */
244 log_assert (c->nread <= sizeof c->buffer);
248 return 0; /* Done copying. */
251 if (sink && es_write (sink, c->writep, c->nread, &nwritten))
252 err = my_error_from_syserror ();
256 log_assert (nwritten <= c->nread);
257 c->writep += nwritten;
258 c->nread -= nwritten;
259 log_assert (c->writep - c->buffer <= sizeof c->buffer);
263 if (gpg_err_code (err) == GPG_ERR_EAGAIN)
264 return 0; /* We will just retry next time. */
269 if (sink && es_fflush (sink) && errno != EAGAIN)
270 err = my_error_from_syserror ();
276 /* Flush the remaining data to SINK. */
278 copy_buffer_flush (struct copy_buffer *c, estream_t sink)
283 if (es_write (sink, c->writep, c->nread, &nwritten))
284 err = my_error_from_syserror ();
286 log_assert (nwritten <= c->nread);
287 c->writep += nwritten;
288 c->nread -= nwritten;
289 log_assert (c->writep - c->buffer <= sizeof c->buffer);
294 if (es_fflush (sink))
295 err = my_error_from_syserror ();
302 /* Run the program PGMNAME with the command line arguments given in
303 * the NULL terminates array ARGV. If INPUT is not NULL it will be
304 * fed to stdin of the process. stderr is logged using log_info and
305 * the process' stdout is written to OUTPUT. If OUTPUT is NULL the
306 * output is discarded. If INEXTRA is given, an additional input
307 * stream will be passed to the child; to tell the child about this
308 * ARGV is scanned and the first occurrence of an argument
309 * "-&@INEXTRA@" is replaced by the concatenation of "-&" and the
310 * child's file descriptor of the pipe created for the INEXTRA stream.
312 * On error a diagnostic is printed and an error code returned. */
314 gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
315 estream_t input, estream_t inextra,
317 exec_tool_status_cb_t status_cb,
318 void *status_cb_value)
321 pid_t pid = (pid_t) -1;
322 estream_t infp = NULL;
323 estream_t extrafp = NULL;
324 estream_t outfp = NULL, errfp = NULL;
327 int extrapipe[2] = {-1, -1};
329 const char *argsave = NULL;
332 read_and_log_buffer_t fderrstate;
333 struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL;
335 memset (fds, 0, sizeof fds);
336 memset (&fderrstate, 0, sizeof fderrstate);
338 cpbuf_in = xtrymalloc (sizeof *cpbuf_in);
339 if (cpbuf_in == NULL)
341 err = my_error_from_syserror ();
344 copy_buffer_init (cpbuf_in);
346 cpbuf_out = xtrymalloc (sizeof *cpbuf_out);
347 if (cpbuf_out == NULL)
349 err = my_error_from_syserror ();
352 copy_buffer_init (cpbuf_out);
354 cpbuf_extra = xtrymalloc (sizeof *cpbuf_extra);
355 if (cpbuf_extra == NULL)
357 err = my_error_from_syserror ();
360 copy_buffer_init (cpbuf_extra);
362 fderrstate.pgmname = pgmname;
363 fderrstate.status_cb = status_cb;
364 fderrstate.status_cb_value = status_cb_value;
365 fderrstate.buffer_size = 256;
366 fderrstate.buffer = xtrymalloc (fderrstate.buffer_size);
367 if (!fderrstate.buffer)
369 err = my_error_from_syserror ();
375 err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1);
378 log_error ("error running outbound pipe for extra fp: %s\n",
382 exceptclose[0] = extrapipe[0]; /* Do not close in child. */
384 /* Now find the argument marker and replace by the pipe's fd.
385 Yeah, that is an ugly non-thread safe hack but it safes us to
386 create a copy of the array. */
387 snprintf (extrafdbuf, sizeof extrafdbuf, "-&%d", extrapipe[0]);
388 for (argsaveidx=0; argv[argsaveidx]; argsaveidx++)
389 if (!strcmp (argv[argsaveidx], "-&@INEXTRA@"))
391 argsave = argv[argsaveidx];
392 argv[argsaveidx] = extrafdbuf;
399 err = gnupg_spawn_process (pgmname, argv,
400 exceptclose, NULL, GNUPG_SPAWN_NONBLOCK,
402 &outfp, &errfp, &pid);
403 if (extrapipe[0] != -1)
404 close (extrapipe[0]);
406 argv[argsaveidx] = argsave;
409 log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
413 fds[0].stream = infp;
414 fds[0].want_write = 1;
417 fds[1].stream = outfp;
418 fds[1].want_read = 1;
419 fds[2].stream = errfp;
420 fds[2].want_read = 1;
421 fds[3].stream = extrafp;
422 fds[3].want_write = 1;
426 /* Now read as long as we have something to poll. We continue
427 reading even after EOF or error on stdout so that we get the
428 other error messages or remaining outut. */
429 while (! (fds[1].ignore && fds[2].ignore))
431 count = es_poll (fds, DIM(fds), -1);
434 err = my_error_from_syserror ();
435 log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
440 log_debug ("unexpected timeout while polling '%s'\n", pgmname);
444 if (fds[0].got_write)
446 err = copy_buffer_do_copy (cpbuf_in, input, fds[0].stream);
449 log_error ("error feeding data to '%s': %s\n",
450 pgmname, gpg_strerror (err));
456 err = copy_buffer_flush (cpbuf_in, fds[0].stream);
457 if (gpg_err_code (err) == GPG_ERR_EAGAIN)
458 continue; /* Retry next time. */
461 log_error ("error feeding data to '%s': %s\n",
462 pgmname, gpg_strerror (err));
466 fds[0].ignore = 1; /* ready. */
467 es_fclose (infp); infp = NULL;
471 if (fds[3].got_write)
473 log_assert (inextra);
474 err = copy_buffer_do_copy (cpbuf_extra, inextra, fds[3].stream);
477 log_error ("error feeding data to '%s': %s\n",
478 pgmname, gpg_strerror (err));
482 if (es_feof (inextra))
484 err = copy_buffer_flush (cpbuf_extra, fds[3].stream);
485 if (gpg_err_code (err) == GPG_ERR_EAGAIN)
486 continue; /* Retry next time. */
489 log_error ("error feeding data to '%s': %s\n",
490 pgmname, gpg_strerror (err));
494 fds[3].ignore = 1; /* ready. */
495 es_fclose (extrafp); extrafp = NULL;
501 err = copy_buffer_do_copy (cpbuf_out, fds[1].stream, output);
504 log_error ("error reading data from '%s': %s\n",
505 pgmname, gpg_strerror (err));
509 if (es_feof (fds[1].stream))
511 err = copy_buffer_flush (cpbuf_out, output);
514 log_error ("error reading data from '%s': %s\n",
515 pgmname, gpg_strerror (err));
519 fds[1].ignore = 1; /* ready. */
524 read_and_log_stderr (&fderrstate, fds + 2);
527 read_and_log_stderr (&fderrstate, NULL); /* Flush. */
528 es_fclose (infp); infp = NULL;
529 es_fclose (extrafp); extrafp = NULL;
530 es_fclose (outfp); outfp = NULL;
531 es_fclose (errfp); errfp = NULL;
533 err = gnupg_wait_process (pgmname, pid, 1, NULL);
537 if (err && pid != (pid_t) -1)
538 gnupg_kill_process (pid);
544 if (pid != (pid_t)(-1))
545 gnupg_wait_process (pgmname, pid, 1, NULL);
546 gnupg_release_process (pid);
548 copy_buffer_shred (cpbuf_in);
550 copy_buffer_shred (cpbuf_out);
552 copy_buffer_shred (cpbuf_extra);
554 xfree (fderrstate.buffer);
559 /* A dummy free function to pass to 'es_mopen'. */
566 /* Run the program PGMNAME with the command line arguments given in
567 the NULL terminates array ARGV. If INPUT_STRING is not NULL it
568 will be fed to stdin of the process. stderr is logged using
569 log_info and the process' stdout is returned in a newly malloced
570 buffer RESULT with the length stored at RESULTLEN if not given as
571 NULL. A hidden Nul is appended to the output. On error NULL is
572 stored at RESULT, a diagnostic is printed, and an error code
575 gnupg_exec_tool (const char *pgmname, const char *argv[],
576 const char *input_string,
577 char **result, size_t *resultlen)
580 estream_t input = NULL;
591 len = strlen (input_string);
592 input = es_mopen ((char *) input_string, len, len,
593 0 /* don't grow */, NULL, nop_free, "rb");
595 return my_error_from_syserror ();
598 output = es_fopenmem (0, "wb");
601 err = my_error_from_syserror ();
605 err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output, NULL, NULL);
609 len = es_ftello (output);
610 err = es_fseek (output, 0, SEEK_SET);
614 *result = xtrymalloc (len + 1);
617 err = my_error_from_syserror ();
623 if (es_read (output, *result, len, &nread))
625 err = my_error_from_syserror ();
629 log_fatal ("%s: short read from memstream\n", __func__);