From 239a4d53916b47b5b0f0167a9b2c7a8915bb9c52 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sat, 28 May 2016 00:07:09 +0200 Subject: [PATCH] common: Add a status callback to gnupg_exec_tool_stream. * common/exectool.h (exec_tool_status_cb_t): New. * common/exectool.c: Include missing exectool.h. (read_and_log_buffer_t): Replace array by pointer. (gnupg_exec_tool_stream): Add args 'status_cb' and 'status_cb_value'. Change all callers to pass NULL for them. Malloc buffer for FDERRSTATE. (read_and_log_stderr): Implement status_fd feature. Signed-off-by: Werner Koch --- common/exectool.c | 93 ++++++++++++++++++++++++++++++++++++------ common/exectool.h | 15 ++++++- tools/gpgtar-create.c | 2 +- tools/gpgtar-extract.c | 2 +- tools/gpgtar-list.c | 2 +- 5 files changed, 97 insertions(+), 17 deletions(-) diff --git a/common/exectool.c b/common/exectool.c index 953c34af0..897450e1e 100644 --- a/common/exectool.c +++ b/common/exectool.c @@ -44,13 +44,17 @@ #include "exechelp.h" #include "sysutils.h" #include "util.h" +#include "exectool.h" typedef struct { const char *pgmname; + exec_tool_status_cb_t status_cb; + void *status_cb_value; int cont; - int used; - char buffer[256]; + size_t used; + size_t buffer_size; + char *buffer; } read_and_log_buffer_t; @@ -83,14 +87,37 @@ read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr) pname++; else pname = state->pgmname; - /* If our pgmname plus colon is identical to the start of - the output, print only the output. */ len = strlen (pname); - if (!state->cont + + if (state->status_cb + && !strncmp (state->buffer, "[GNUPG:] ", 9) + && state->buffer[9] >= 'A' && state->buffer[9] <= 'Z') + { + char *rest; + + rest = strchr (state->buffer + 9, ' '); + if (!rest) + { + /* Set REST to an empty string. */ + rest = state->buffer + strlen (state->buffer); + } + else + { + *rest++ = 0; + trim_spaces (rest); + } + state->status_cb (state->status_cb_value, + state->buffer + 9, rest); + } + else if (!state->cont && !strncmp (state->buffer, pname, len) && strlen (state->buffer) > strlen (pname) && state->buffer[len] == ':' ) - log_info ("%s\n", state->buffer); + { + /* PGMNAME plus colon is identical to the start of + the output: print only the output. */ + log_info ("%s\n", state->buffer); + } else log_info ("%s%c %s\n", pname, state->cont? '+':':', state->buffer); @@ -123,10 +150,39 @@ read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr) } else { - if (state->used >= sizeof state->buffer - 1) + if (state->used >= state->buffer_size - 1) { - read_and_log_stderr (state, NULL); - state->cont = 1; + if (state->status_cb) + { + /* A status callback requires that we have a full + * line. Thus we need to enlarget the buffer in + * this case. */ + char *newbuffer; + size_t newsize = state->buffer_size + 256; + + newbuffer = xtrymalloc (newsize); + if (!newbuffer) + { + log_error ("error allocating memory for status cb: %s\n", + gpg_strerror (my_error_from_syserror ())); + /* We better disable the status CB in this case. */ + state->status_cb = NULL; + read_and_log_stderr (state, NULL); + state->cont = 1; + } + else + { + memcpy (newbuffer, state->buffer, state->used); + xfree (state->buffer); + state->buffer = newbuffer; + state->buffer_size = newsize; + } + } + else + { + read_and_log_stderr (state, NULL); + state->cont = 1; + } } state->buffer[state->used++] = c; } @@ -242,7 +298,9 @@ copy_buffer_flush (struct copy_buffer *c, estream_t sink) gpg_error_t gnupg_exec_tool_stream (const char *pgmname, const char *argv[], estream_t input, estream_t inextra, - estream_t output) + estream_t output, + exec_tool_status_cb_t status_cb, + void *status_cb_value) { gpg_error_t err; pid_t pid; @@ -265,6 +323,14 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], copy_buffer_init (&cpbuf_out); copy_buffer_init (&cpbuf_extra); + fderrstate.pgmname = pgmname; + fderrstate.status_cb = status_cb; + fderrstate.status_cb_value = status_cb_value; + fderrstate.buffer_size = 256; + fderrstate.buffer = xtrymalloc (fderrstate.buffer_size); + if (!fderrstate.buffer) + return my_error_from_syserror (); + if (inextra) { err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1); @@ -272,6 +338,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], { log_error ("error running outbound pipe for extra fp: %s\n", gpg_strerror (err)); + xfree (fderrstate.buffer); return err; } exceptclose[0] = extrapipe[0]; /* Do not close in child. */ @@ -303,11 +370,10 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], { log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); es_fclose (extrafp); + xfree (fderrstate.buffer); return err; } - fderrstate.pgmname = pgmname; - fds[0].stream = infp; fds[0].want_write = 1; if (!input) @@ -438,6 +504,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], copy_buffer_shred (&cpbuf_out); if (inextra) copy_buffer_shred (&cpbuf_extra); + xfree (fderrstate.buffer); return err; } @@ -488,7 +555,7 @@ gnupg_exec_tool (const char *pgmname, const char *argv[], goto leave; } - err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output); + err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output, NULL, NULL); if (err) goto leave; diff --git a/common/exectool.h b/common/exectool.h index 21bc177a1..94091fdd7 100644 --- a/common/exectool.h +++ b/common/exectool.h @@ -32,6 +32,17 @@ #include +/* This callback can be used to process --status-fd outputs of GnuPG + * tools. OPAQUE can be used to communicate between the caller of the + * function and the callback. KEYWORD is the status keyword (see + * doc/DETAILS); it is never NULL. ARGS are the arguments of the + * status line and will also never be NULL; the caller may modify this + * string. */ +typedef void (*exec_tool_status_cb_t) (void *opaque, + const char *keyword, + char *args); + + /* Run the program PGMNAME with the command line arguments given in the NULL terminates array ARGV. If INPUT_STRING is not NULL it will be fed to stdin of the process. stderr is logged using @@ -51,6 +62,8 @@ gpg_error_t gnupg_exec_tool (const char *pgmname, const char *argv[], printed, and an error code returned. INEXTRA is reserved. */ gpg_error_t gnupg_exec_tool_stream (const char *pgmname, const char *argv[], estream_t input, estream_t inextra, - estream_t output); + estream_t output, + exec_tool_status_cb_t status_cb, + void *status_cb_value); #endif /* GNUPG_COMMON_EXECTOOL_H */ diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c index d615fd38d..6adc1f513 100644 --- a/tools/gpgtar-create.c +++ b/tools/gpgtar-create.c @@ -932,7 +932,7 @@ gpgtar_create (char **inpattern, int encrypt, int sign) } err = gnupg_exec_tool_stream (opt.gpg_program, argv, - outstream, NULL, cipher_stream); + outstream, NULL, cipher_stream, NULL, NULL); xfree (argv); if (err) goto leave; diff --git a/tools/gpgtar-extract.c b/tools/gpgtar-extract.c index c4bf44064..866215b2c 100644 --- a/tools/gpgtar-extract.c +++ b/tools/gpgtar-extract.c @@ -327,7 +327,7 @@ gpgtar_extract (const char *filename, int decrypt) } err = gnupg_exec_tool_stream (opt.gpg_program, argv, - cipher_stream, NULL, stream); + cipher_stream, NULL, stream, NULL, NULL); xfree (argv); if (err) goto leave; diff --git a/tools/gpgtar-list.c b/tools/gpgtar-list.c index a3f85aceb..1d59d9c65 100644 --- a/tools/gpgtar-list.c +++ b/tools/gpgtar-list.c @@ -327,7 +327,7 @@ gpgtar_list (const char *filename, int decrypt) } err = gnupg_exec_tool_stream (opt.gpg_program, argv, - cipher_stream, NULL, stream); + cipher_stream, NULL, stream, NULL, NULL); xfree (argv); if (err) goto leave;