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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-05-28 00:07:09 +02:00
parent 44a32455c8
commit 239a4d5391
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 97 additions and 17 deletions

View File

@ -44,13 +44,17 @@
#include "exechelp.h" #include "exechelp.h"
#include "sysutils.h" #include "sysutils.h"
#include "util.h" #include "util.h"
#include "exectool.h"
typedef struct typedef struct
{ {
const char *pgmname; const char *pgmname;
exec_tool_status_cb_t status_cb;
void *status_cb_value;
int cont; int cont;
int used; size_t used;
char buffer[256]; size_t buffer_size;
char *buffer;
} read_and_log_buffer_t; } read_and_log_buffer_t;
@ -83,14 +87,37 @@ read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
pname++; pname++;
else else
pname = state->pgmname; pname = state->pgmname;
/* If our pgmname plus colon is identical to the start of
the output, print only the output. */
len = strlen (pname); 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) && !strncmp (state->buffer, pname, len)
&& strlen (state->buffer) > strlen (pname) && strlen (state->buffer) > strlen (pname)
&& state->buffer[len] == ':' ) && 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 else
log_info ("%s%c %s\n", log_info ("%s%c %s\n",
pname, state->cont? '+':':', state->buffer); pname, state->cont? '+':':', state->buffer);
@ -123,10 +150,39 @@ read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
} }
else else
{ {
if (state->used >= sizeof state->buffer - 1) if (state->used >= state->buffer_size - 1)
{ {
read_and_log_stderr (state, NULL); if (state->status_cb)
state->cont = 1; {
/* 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; state->buffer[state->used++] = c;
} }
@ -242,7 +298,9 @@ copy_buffer_flush (struct copy_buffer *c, estream_t sink)
gpg_error_t gpg_error_t
gnupg_exec_tool_stream (const char *pgmname, const char *argv[], gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
estream_t input, estream_t inextra, 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; gpg_error_t err;
pid_t pid; 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_out);
copy_buffer_init (&cpbuf_extra); 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) if (inextra)
{ {
err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1); 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", log_error ("error running outbound pipe for extra fp: %s\n",
gpg_strerror (err)); gpg_strerror (err));
xfree (fderrstate.buffer);
return err; return err;
} }
exceptclose[0] = extrapipe[0]; /* Do not close in child. */ 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)); log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
es_fclose (extrafp); es_fclose (extrafp);
xfree (fderrstate.buffer);
return err; return err;
} }
fderrstate.pgmname = pgmname;
fds[0].stream = infp; fds[0].stream = infp;
fds[0].want_write = 1; fds[0].want_write = 1;
if (!input) if (!input)
@ -438,6 +504,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
copy_buffer_shred (&cpbuf_out); copy_buffer_shred (&cpbuf_out);
if (inextra) if (inextra)
copy_buffer_shred (&cpbuf_extra); copy_buffer_shred (&cpbuf_extra);
xfree (fderrstate.buffer);
return err; return err;
} }
@ -488,7 +555,7 @@ gnupg_exec_tool (const char *pgmname, const char *argv[],
goto leave; 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) if (err)
goto leave; goto leave;

View File

@ -32,6 +32,17 @@
#include <gpg-error.h> #include <gpg-error.h>
/* 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 /* Run the program PGMNAME with the command line arguments given in
the NULL terminates array ARGV. If INPUT_STRING is not NULL it the NULL terminates array ARGV. If INPUT_STRING is not NULL it
will be fed to stdin of the process. stderr is logged using 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. */ printed, and an error code returned. INEXTRA is reserved. */
gpg_error_t gnupg_exec_tool_stream (const char *pgmname, const char *argv[], gpg_error_t gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
estream_t input, estream_t inextra, 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 */ #endif /* GNUPG_COMMON_EXECTOOL_H */

View File

@ -932,7 +932,7 @@ gpgtar_create (char **inpattern, int encrypt, int sign)
} }
err = gnupg_exec_tool_stream (opt.gpg_program, argv, err = gnupg_exec_tool_stream (opt.gpg_program, argv,
outstream, NULL, cipher_stream); outstream, NULL, cipher_stream, NULL, NULL);
xfree (argv); xfree (argv);
if (err) if (err)
goto leave; goto leave;

View File

@ -327,7 +327,7 @@ gpgtar_extract (const char *filename, int decrypt)
} }
err = gnupg_exec_tool_stream (opt.gpg_program, argv, err = gnupg_exec_tool_stream (opt.gpg_program, argv,
cipher_stream, NULL, stream); cipher_stream, NULL, stream, NULL, NULL);
xfree (argv); xfree (argv);
if (err) if (err)
goto leave; goto leave;

View File

@ -327,7 +327,7 @@ gpgtar_list (const char *filename, int decrypt)
} }
err = gnupg_exec_tool_stream (opt.gpg_program, argv, err = gnupg_exec_tool_stream (opt.gpg_program, argv,
cipher_stream, NULL, stream); cipher_stream, NULL, stream, NULL, NULL);
xfree (argv); xfree (argv);
if (err) if (err)
goto leave; goto leave;