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 err = es_read (source, c->buffer, sizeof c->buffer, &c->nread);
239 return 0; /* We will just retry next time. */
241 return my_error_from_syserror ();
244 assert (c->nread <= sizeof c->buffer);
248 return 0; /* Done copying. */
252 err = sink? es_write (sink, c->writep, c->nread, &nwritten) : 0;
254 assert (nwritten <= c->nread);
255 c->writep += nwritten;
256 c->nread -= nwritten;
257 assert (c->writep - c->buffer <= sizeof c->buffer);
262 return 0; /* We will just retry next time. */
264 return my_error_from_syserror ();
267 if (sink && es_fflush (sink) && errno != EAGAIN)
268 err = my_error_from_syserror ();
274 /* Flush the remaining data to SINK. */
276 copy_buffer_flush (struct copy_buffer *c, estream_t sink)
282 err = copy_buffer_do_copy (c, NULL, sink);
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.
302 * On error a diagnostic is printed and an error code returned. */
304 gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
305 estream_t input, estream_t inextra,
307 exec_tool_status_cb_t status_cb,
308 void *status_cb_value)
311 pid_t pid = (pid_t) -1;
312 estream_t infp = NULL;
313 estream_t extrafp = NULL;
314 estream_t outfp = NULL, errfp = NULL;
317 int extrapipe[2] = {-1, -1};
319 const char *argsave = NULL;
322 read_and_log_buffer_t fderrstate;
323 struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL;
325 memset (fds, 0, sizeof fds);
326 memset (&fderrstate, 0, sizeof fderrstate);
328 cpbuf_in = xtrymalloc (sizeof *cpbuf_in);
329 if (cpbuf_in == NULL)
331 err = my_error_from_syserror ();
334 copy_buffer_init (cpbuf_in);
336 cpbuf_out = xtrymalloc (sizeof *cpbuf_out);
337 if (cpbuf_out == NULL)
339 err = my_error_from_syserror ();
342 copy_buffer_init (cpbuf_out);
344 cpbuf_extra = xtrymalloc (sizeof *cpbuf_extra);
345 if (cpbuf_extra == NULL)
347 err = my_error_from_syserror ();
350 copy_buffer_init (cpbuf_extra);
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)
359 err = my_error_from_syserror ();
365 err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1);
368 log_error ("error running outbound pipe for extra fp: %s\n",
372 exceptclose[0] = extrapipe[0]; /* Do not close in child. */
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@"))
381 argsave = argv[argsaveidx];
382 argv[argsaveidx] = extrafdbuf;
389 err = gnupg_spawn_process (pgmname, argv,
390 exceptclose, NULL, GNUPG_SPAWN_NONBLOCK,
392 &outfp, &errfp, &pid);
393 if (extrapipe[0] != -1)
394 close (extrapipe[0]);
396 argv[argsaveidx] = argsave;
399 log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
403 fds[0].stream = infp;
404 fds[0].want_write = 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;
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))
421 count = es_poll (fds, DIM(fds), -1);
424 err = my_error_from_syserror ();
425 log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
430 log_debug ("unexpected timeout while polling '%s'\n", pgmname);
434 if (fds[0].got_write)
436 err = copy_buffer_do_copy (cpbuf_in, input, fds[0].stream);
439 log_error ("error feeding data to '%s': %s\n",
440 pgmname, gpg_strerror (err));
446 err = copy_buffer_flush (cpbuf_in, fds[0].stream);
449 log_error ("error feeding data to '%s': %s\n",
450 pgmname, gpg_strerror (err));
454 fds[0].ignore = 1; /* ready. */
455 es_fclose (infp); infp = NULL;
459 if (fds[3].got_write)
461 log_assert (inextra);
462 err = copy_buffer_do_copy (cpbuf_extra, inextra, fds[3].stream);
465 log_error ("error feeding data to '%s': %s\n",
466 pgmname, gpg_strerror (err));
470 if (es_feof (inextra))
472 err = copy_buffer_flush (cpbuf_extra, fds[3].stream);
475 log_error ("error feeding data to '%s': %s\n",
476 pgmname, gpg_strerror (err));
480 fds[3].ignore = 1; /* ready. */
481 es_fclose (extrafp); extrafp = NULL;
487 err = copy_buffer_do_copy (cpbuf_out, fds[1].stream, output);
490 log_error ("error reading data from '%s': %s\n",
491 pgmname, gpg_strerror (err));
495 if (es_feof (fds[1].stream))
497 err = copy_buffer_flush (cpbuf_out, output);
500 log_error ("error reading data from '%s': %s\n",
501 pgmname, gpg_strerror (err));
505 fds[1].ignore = 1; /* ready. */
510 read_and_log_stderr (&fderrstate, fds + 2);
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;
519 err = gnupg_wait_process (pgmname, pid, 1, NULL);
523 if (err && pid != (pid_t) -1)
524 gnupg_kill_process (pid);
530 if (pid != (pid_t)(-1))
531 gnupg_wait_process (pgmname, pid, 1, NULL);
532 gnupg_release_process (pid);
534 copy_buffer_shred (cpbuf_in);
536 copy_buffer_shred (cpbuf_out);
538 copy_buffer_shred (cpbuf_extra);
540 xfree (fderrstate.buffer);
545 /* A dummy free function to pass to 'es_mopen'. */
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
561 gnupg_exec_tool (const char *pgmname, const char *argv[],
562 const char *input_string,
563 char **result, size_t *resultlen)
566 estream_t input = NULL;
577 len = strlen (input_string);
578 input = es_mopen ((char *) input_string, len, len,
579 0 /* don't grow */, NULL, nop_free, "rb");
581 return my_error_from_syserror ();
584 output = es_fopenmem (0, "wb");
587 err = my_error_from_syserror ();
591 err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output, NULL, NULL);
595 len = es_ftello (output);
596 err = es_fseek (output, 0, SEEK_SET);
600 *result = xtrymalloc (len + 1);
603 err = my_error_from_syserror ();
609 err = es_read (output, *result, len, &nread);
613 log_fatal ("%s: short read from memstream\n", __func__);