Skip to content
Snippets Groups Projects
Commit 943eff90 authored by Simon McVittie's avatar Simon McVittie
Browse files

adverb, utils: Factor out pv_wait_for_child_processes()

parent dc513da7
No related branches found
No related tags found
No related merge requests found
......@@ -534,53 +534,30 @@ main (int argc,
/* If the child writes to stdout and closes it, don't interfere */
g_clear_pointer (&original_stdout, fclose);
while (1)
{
pid_t died = wait (&wait_status);
/* Reap child processes until child_pid exits */
if (!pv_wait_for_child_processes (child_pid, &wait_status, error))
goto out;
if (died < 0)
{
if (errno == EINTR)
{
continue;
}
else if (errno == ECHILD)
{
g_debug ("No more child processes, exiting");
break;
}
else
{
glnx_throw_errno_prefix (error, "wait");
goto out;
}
}
else if (died == child_pid)
{
if (WIFEXITED (wait_status))
{
ret = WEXITSTATUS (wait_status);
g_debug ("Command exited with status %d", ret);
}
else if (WIFSIGNALED (wait_status))
{
ret = 128 + WTERMSIG (wait_status);
g_debug ("Command killed by signal %d", ret - 128);
}
else
{
ret = EX_SOFTWARE;
g_debug ("Command terminated in an unknown way (wait status %d)",
wait_status);
}
}
else
{
g_debug ("Indirect child %lld exited with wait status %d",
(long long) died, wait_status);
g_warn_if_fail (opt_subreaper);
}
if (WIFEXITED (wait_status))
{
ret = WEXITSTATUS (wait_status);
g_debug ("Command exited with status %d", ret);
}
else if (WIFSIGNALED (wait_status))
{
ret = 128 + WTERMSIG (wait_status);
g_debug ("Command killed by signal %d", ret - 128);
}
else
{
ret = EX_SOFTWARE;
g_debug ("Command terminated in an unknown way (wait status %d)",
wait_status);
}
/* Wait for the other child processes, if any */
if (!pv_wait_for_child_processes (0, NULL, error))
goto out;
out:
global_locks = NULL;
......
......@@ -23,6 +23,8 @@
#include "utils.h"
#include <ftw.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <glib.h>
#include <glib/gstdio.h>
......@@ -575,3 +577,88 @@ pv_get_random_uuid (GError **error)
return g_steal_pointer (&contents);
}
/**
* pv_wait_for_child_processes:
* @main_process: process for which we will report wait-status;
* zero or negative if there is no main process
* @wait_status_out: (out): Used to store the wait status of `@main_process`,
* as if from `wait()`, on success
* @error: Used to raise an error on failure
*
* Wait for child processes of this process to exit, until the @main_process
* has exited. If there is no main process, wait until there are no child
* processes at all.
*
* If the process is a subreaper (`PR_SET_CHILD_SUBREAPER`),
* indirect child processes whose parents have exited will be reparented
* to it, so this will have the effect of waiting for all descendants.
*
* If @main_process is positive, return when @main_process has exited.
* Child processes that exited before @main_process will also have been
* "reaped", but child processes that exit after @main_process will not
* (use `pv_wait_for_child_processes (0, &error)` to resume waiting).
*
* If @main_process is zero or negative, wait for all child processes
* to exit.
*
* This function cannot be called in a process that is using
* g_child_watch_source_new() or similar functions, because it waits
* for all child processes regardless of their process IDs, and that is
* incompatible with waiting for individual child processes.
*
* Returns: %TRUE when @main_process has exited, or if @main_process
* is zero or negative and all child processes have exited
*/
gboolean
pv_wait_for_child_processes (pid_t main_process,
int *wait_status_out,
GError **error)
{
if (wait_status_out != NULL)
*wait_status_out = -1;
while (1)
{
int wait_status = -1;
pid_t died = wait (&wait_status);
if (died < 0)
{
if (errno == EINTR)
{
continue;
}
else if (errno == ECHILD)
{
g_debug ("No more child processes");
break;
}
else
{
return glnx_throw_errno_prefix (error, "wait");
}
}
g_debug ("Child %lld exited with wait status %d",
(long long) died, wait_status);
if (died == main_process)
{
if (wait_status_out != NULL)
*wait_status_out = wait_status;
return TRUE;
}
}
if (main_process > 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Process %lld was not seen to exit",
(long long) main_process);
return FALSE;
}
return TRUE;
}
......@@ -23,6 +23,7 @@
#pragma once
#include <stdio.h>
#include <sys/types.h>
#include <glib.h>
......@@ -60,3 +61,7 @@ void pv_async_signal_safe_error (const char *message,
int exit_status) G_GNUC_NORETURN;
gchar *pv_get_random_uuid (GError **error);
gboolean pv_wait_for_child_processes (pid_t main_process,
int *wait_status_out,
GError **error);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment