summaryrefslogtreecommitdiffstats
path: root/common/exechelp-posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/exechelp-posix.c')
-rw-r--r--common/exechelp-posix.c142
1 files changed, 112 insertions, 30 deletions
diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c
index 943f20aa5..32c420318 100644
--- a/common/exechelp-posix.c
+++ b/common/exechelp-posix.c
@@ -583,6 +583,66 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
}
+
+
+/* Waiting for child processes.
+
+ waitpid(2) may return information about terminated children that we
+ did not yet request, and there is no portable way to wait for a
+ specific set of children.
+
+ As a workaround, we store the results of children for later use.
+
+ XXX: This assumes that PIDs are not reused too quickly. */
+
+struct terminated_child
+{
+ pid_t pid;
+ int exitcode;
+ struct terminated_child *next;
+};
+
+struct terminated_child *terminated_children;
+
+
+static gpg_error_t
+store_result (pid_t pid, int exitcode)
+{
+ struct terminated_child *c;
+
+ c = xmalloc (sizeof *c);
+ if (c == NULL)
+ return gpg_err_code_from_syserror ();
+
+ c->pid = pid;
+ c->exitcode = exitcode;
+ c->next = terminated_children;
+ terminated_children = c;
+
+ return 0;
+}
+
+
+static int
+get_result (pid_t pid, int *r_exitcode)
+{
+ struct terminated_child *c, **prevp;
+
+ for (prevp = &terminated_children, c = terminated_children;
+ c;
+ prevp = &c->next, c = c->next)
+ if (c->pid == pid)
+ {
+ *prevp = c->next;
+ *r_exitcode = c->exitcode;
+ xfree (c);
+ return 1;
+ }
+
+ return 0;
+}
+
+
/* See exechelp.h for a description. */
gpg_error_t
gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
@@ -597,17 +657,25 @@ gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
{
gpg_err_code_t ec = 0;
size_t i, left;
+ int *dummy = NULL;
- for (i = 0; i < count; i++)
+ if (r_exitcodes == NULL)
+ dummy = r_exitcodes = xmalloc (sizeof *r_exitcodes * count);
+
+ for (i = 0, left = count; i < count; i++)
{
- if (r_exitcodes)
- r_exitcodes[i] = -1;
+ int status = -1;
if (pids[i] == (pid_t)(-1))
return my_error (GPG_ERR_INV_VALUE);
+
+ /* See if there was a previously stored result for this pid. */
+ if (get_result (pids[i], &status))
+ left -= 1;
+
+ r_exitcodes[i] = status;
}
- left = count;
while (left > 0)
{
pid_t pid;
@@ -639,43 +707,57 @@ gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
break;
if (i == count)
- /* No match, ignore this pid. */
- continue;
-
- /* Process PIDS[i] died. */
- left -= 1;
-
- if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
{
- log_error (_("error running '%s': probably not installed\n"),
- pgmnames[i]);
- ec = GPG_ERR_CONFIGURATION;
- }
- else if (WIFEXITED (status) && WEXITSTATUS (status))
- {
- if (!r_exitcodes)
- log_error (_("error running '%s': exit status %d\n"),
- pgmnames[i], WEXITSTATUS (status));
- else
- r_exitcodes[i] = WEXITSTATUS (status);
- ec = GPG_ERR_GENERAL;
+ /* No match, store this result. */
+ ec = store_result (pid, status);
+ if (ec)
+ break;
+ continue;
}
- else if (!WIFEXITED (status))
+
+ /* Process PIDS[i] died. */
+ if (r_exitcodes[i] != (pid_t) -1)
{
- log_error (_("error running '%s': terminated\n"), pgmnames[i]);
+ log_error ("PID %d was reused", pid);
ec = GPG_ERR_GENERAL;
+ break;
}
- else
- {
- if (r_exitcodes)
- r_exitcodes[i] = 0;
- }
+
+ left -= 1;
+ r_exitcodes[i] = status;
}
}
+ if (ec == 0)
+ for (i = 0; i < count; i++)
+ {
+ if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127)
+ {
+ log_error (_("error running '%s': probably not installed\n"),
+ pgmnames[i]);
+ ec = GPG_ERR_CONFIGURATION;
+ }
+ else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]))
+ {
+ if (dummy)
+ log_error (_("error running '%s': exit status %d\n"),
+ pgmnames[i], WEXITSTATUS (r_exitcodes[i]));
+ else
+ r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]);
+ ec = GPG_ERR_GENERAL;
+ }
+ else if (!WIFEXITED (r_exitcodes[i]))
+ {
+ log_error (_("error running '%s': terminated\n"), pgmnames[i]);
+ ec = GPG_ERR_GENERAL;
+ }
+ }
+
+ xfree (dummy);
return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
}
+
void
gnupg_release_process (pid_t pid)