[Pkg-gnupg-commit] [gnupg2] 04/159: common: Add code to execute a helper.
Daniel Kahn Gillmor
dkg at fifthhorseman.net
Wed Jan 27 13:23:48 UTC 2016
This is an automated email from the git hooks/post-receive script.
dkg pushed a commit to branch master
in repository gnupg2.
commit 2ae07f826aa551db8adf714158fce962790a6b54
Author: Werner Koch <wk at gnupg.org>
Date: Mon Nov 30 12:53:57 2015 +0100
common: Add code to execute a helper.
* common/sh-exectool.c: New file.
Signed-off-by: Justus Winter <justus at g10code.com>
---
common/sh-exectool.c | 303 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 303 insertions(+)
diff --git a/common/sh-exectool.c b/common/sh-exectool.c
new file mode 100644
index 0000000..ab18095
--- /dev/null
+++ b/common/sh-exectool.c
@@ -0,0 +1,303 @@
+/* sh-exectool.c - Utility functions to execute a helper tool
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "g13-syshelp.h"
+#include <assuan.h>
+#include "i18n.h"
+#include "membuf.h"
+#include "exechelp.h"
+#include "sysutils.h"
+
+typedef struct
+{
+ const char *pgmname;
+ int cont;
+ int used;
+ char buffer[256];
+} read_and_log_buffer_t;
+
+
+static void
+read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
+{
+ gpg_error_t err;
+ int c;
+
+ if (!fderr)
+ {
+ /* Flush internal buffer. */
+ if (state->used)
+ {
+ const char *pname;
+ int len;
+
+ state->buffer[state->used] = 0;
+ state->used = 0;
+
+ pname = strrchr (state->pgmname, '/');
+ if (pname && pname != state->pgmname && pname[1])
+ 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
+ && !strncmp (state->buffer, pname, len)
+ && strlen (state->buffer) > strlen (pname)
+ && state->buffer[len] == ':' )
+ log_info ("%s\n", state->buffer);
+ else
+ log_info ("%s%c %s\n",
+ pname, state->cont? '+':':', state->buffer);
+ }
+ state->cont = 0;
+ return;
+ }
+ for (;;)
+ {
+ c = es_fgetc (fderr->stream);
+ if (c == EOF)
+ {
+ if (es_feof (fderr->stream))
+ {
+ fderr->ignore = 1; /* Not anymore needed. */
+ }
+ else if (es_ferror (fderr->stream))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading stderr of '%s': %s\n",
+ state->pgmname, gpg_strerror (err));
+ fderr->ignore = 1; /* Disable. */
+ }
+
+ break;
+ }
+ else if (c == '\n')
+ {
+ read_and_log_stderr (state, NULL);
+ }
+ else
+ {
+ if (state->used >= sizeof state->buffer - 1)
+ {
+ read_and_log_stderr (state, NULL);
+ state->cont = 1;
+ }
+ state->buffer[state->used++] = c;
+ }
+ }
+}
+
+
+static gpg_error_t
+read_stdout (membuf_t *mb, es_poll_t *fdout, const char *pgmname)
+{
+ gpg_error_t err = 0;
+ int c;
+
+ for (;;)
+ {
+ c = es_fgetc (fdout->stream);
+ if (c == EOF)
+ {
+ if (es_feof (fdout->stream))
+ {
+ fdout->ignore = 1; /* Ready. */
+ }
+ else if (es_ferror (fdout->stream))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading stdout of '%s': %s\n",
+ pgmname, gpg_strerror (err));
+ fdout->ignore = 1; /* Disable. */
+ }
+
+ break;
+ }
+ else
+ {
+ char buf[1];
+ *buf = c;
+ put_membuf (mb, buf, 1);
+ }
+ }
+
+ return err;
+}
+
+
+/* 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
+ log_info and the process' stdout is returned in a newly malloced
+ buffer RESULT with the length stored at RESULTLEN if not given as
+ NULL. A hidden Nul is appended to the output. On error NULL is
+ stored at RESULT, a diagnostic is printed, and an error code
+ returned. */
+gpg_error_t
+sh_exec_tool (const char *pgmname, const char *argv[],
+ const char *input_string,
+ char **result, size_t *resultlen)
+{
+ gpg_error_t err;
+ pid_t pid;
+ estream_t infp = NULL;
+ estream_t outfp, errfp;
+ es_poll_t fds[3];
+ int count;
+ read_and_log_buffer_t fderrstate;
+ membuf_t fdout_mb;
+ size_t len, nwritten;
+
+ *result = NULL;
+ if (resultlen)
+ *resultlen = 0;
+ memset (fds, 0, sizeof fds);
+ memset (&fderrstate, 0, sizeof fderrstate);
+ init_membuf (&fdout_mb, 4096);
+
+ err = gnupg_spawn_process (pgmname, argv, GPG_ERR_SOURCE_DEFAULT,
+ NULL, GNUPG_SPAWN_NONBLOCK,
+ input_string? &infp : NULL,
+ &outfp, &errfp, &pid);
+ if (err)
+ {
+ log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
+ return err;
+ }
+
+ fderrstate.pgmname = pgmname;
+
+ fds[0].stream = infp;
+ fds[0].want_write = 1;
+ if (!input_string)
+ fds[0].ignore = 1;
+ fds[1].stream = outfp;
+ fds[1].want_read = 1;
+ fds[2].stream = errfp;
+ fds[2].want_read = 1;
+ /* Now read as long as we have something to poll. We continue
+ reading even after EOF or error on stdout so that we get the
+ other error messages or remaining outout. */
+ while (!fds[1].ignore && !fds[2].ignore)
+ {
+ count = es_poll (fds, DIM(fds), -1);
+ if (count == -1)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
+ goto leave;
+ }
+ if (!count)
+ {
+ log_debug ("unexpected timeout while polling '%s'\n", pgmname);
+ break;
+ }
+
+ if (fds[0].got_write)
+ {
+ len = strlen (input_string);
+ log_debug ("writing '%s'\n", input_string);
+ if (es_write (fds[0].stream, input_string, len, &nwritten))
+ {
+ if (errno != EAGAIN)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error writing '%s': %s\n",
+ pgmname, gpg_strerror (err));
+ goto leave;
+ }
+ else
+ log_debug (" .. EAGAIN\n");
+ }
+ else
+ {
+ assert (nwritten <= len);
+ input_string += nwritten;
+ }
+
+ if (es_fflush (fds[0].stream) && errno != EAGAIN)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error writing '%s' (flush): %s\n",
+ pgmname, gpg_strerror (err));
+ if (gpg_err_code (err) == GPG_ERR_EPIPE && !*input_string)
+ {
+ /* fixme: How can we tell whether estream has
+ pending bytes after a HUP - which is an
+ error? */
+ }
+ else
+ goto leave;
+ }
+ if (!*input_string)
+ {
+ fds[0].ignore = 1; /* ready. */
+ es_fclose (infp); infp = NULL;
+ }
+ }
+
+ if (fds[1].got_read)
+ read_stdout (&fdout_mb, fds + 1, pgmname); /* FIXME: Add error
+ handling. */
+ if (fds[2].got_read)
+ read_and_log_stderr (&fderrstate, fds + 2);
+
+ }
+
+ read_and_log_stderr (&fderrstate, NULL); /* Flush. */
+ es_fclose (infp); infp = NULL;
+ es_fclose (outfp); outfp = NULL;
+ es_fclose (errfp); errfp = NULL;
+
+ err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ pid = (pid_t)(-1);
+
+ leave:
+ if (err)
+ {
+ gnupg_kill_process (pid);
+ xfree (get_membuf (&fdout_mb, NULL));
+ }
+ else
+ {
+ put_membuf (&fdout_mb, "", 1); /* Make sure it is a string. */
+ *result = get_membuf (&fdout_mb, resultlen);
+ if (!*result)
+ err = gpg_error_from_syserror ();
+ }
+
+ es_fclose (infp);
+ es_fclose (outfp);
+ es_fclose (errfp);
+ if (pid != (pid_t)(-1))
+ gnupg_wait_process (pgmname, pid, 1, NULL);
+ gnupg_release_process (pid);
+
+ return err;
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/gnupg2.git
More information about the Pkg-gnupg-commit
mailing list