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

launcher: implement --exit-on-readable=FD


This allows a controlling process to terminate the launcher, even if it
is wrapped in an "adverb" command like pressure-vessel-wrap that makes
it non-straightforward to send signals to the launcher.

Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent 504bae20
No related branches found
No related tags found
No related merge requests found
...@@ -47,6 +47,11 @@ D-Bus session bus, and executes arbitrary commands as subprocesses. ...@@ -47,6 +47,11 @@ D-Bus session bus, and executes arbitrary commands as subprocesses.
: Connect to the well-known D-Bus session bus, request the given name : Connect to the well-known D-Bus session bus, request the given name
and listen for commands there. and listen for commands there.
**--exit-on-readable** *FD*
: Exit when file descriptor *FD* (typically 0, for **stdin**) becomes
readable, meaning either data is available or end-of-file has been
reached.
**--replace** **--replace**
: When used with **--bus-name**, allow other : When used with **--bus-name**, allow other
**pressure-vessel-launcher** processes to take over the bus name, **pressure-vessel-launcher** processes to take over the bus name,
...@@ -196,19 +201,29 @@ Listen on the session bus, and run two commands, and exit: ...@@ -196,19 +201,29 @@ Listen on the session bus, and run two commands, and exit:
kill -TERM "$launcher_pid" kill -TERM "$launcher_pid"
wait "$launcher_pid" wait "$launcher_pid"
Listen on a socket in a temporary directory: Listen on a socket in a temporary directory, and use `--exit-with-fd`.
Production code would be more likely to invoke **pressure-vessel-launch**
from C, C++ or Python code rather than a shell script, and use pipes
instead of fifos.
tmpdir="$(mktemp -d)" tmpdir="$(mktemp -d)"
mkfifo "${tmpdir}/fifo" mkfifo "${tmpdir}/exit-fifo"
mkfifo "${tmpdir}/ready-fifo"
pressure-vessel-wrap \ pressure-vessel-wrap \
--filesystem="${tmpdir}" \ --filesystem="${tmpdir}" \
... \ ... \
-- \ -- \
pressure-vessel-launcher \ pressure-vessel-launcher \
--socket="${tmpdir}/launcher" > "${tmpdir}/fifo" & --exit-on-readable=0 \
--socket="${tmpdir}/launcher" \
< "${tmpdir}/exit-fifo" \
> "${tmpdir}/ready-fifo" \
&
launcher_pid="$!" launcher_pid="$!"
sleep infinity > "${tmpdir}/exit-fifo" &
sleep_pid="$!"
# Wait for EOF so we know the socket is available # Wait for EOF so we know the socket is available
cat "${tmpdir}/fifo" > /dev/null cat "${tmpdir}/ready-fifo" > /dev/null
pressure-vessel-launch \ pressure-vessel-launch \
--socket="${tmpdir}/launcher" \ --socket="${tmpdir}/launcher" \
...@@ -218,9 +233,17 @@ Listen on a socket in a temporary directory: ...@@ -218,9 +233,17 @@ Listen on a socket in a temporary directory:
--socket="${tmpdir}/launcher" \ --socket="${tmpdir}/launcher" \
-- \ -- \
id id
pressure-vessel-launch \
kill -TERM "$launcher_pid" --socket="${tmpdir}/launcher" \
wait "$launcher_pid" -- \
sleep infinity &
launch_sleep_pid="$!"
# Make pressure-vessel-launcher's stdin become readable, which
# will make it exit due to --exit-on-readable
kill "$sleep_pid"
wait "$launcher_pid" || echo "launcher exit status: $?"
wait "$launch_sleep_pid" || echo "launched process exit status: $?"
rm -fr "$tmpdir" rm -fr "$tmpdir"
<!-- vim:set sw=4 sts=4 et: --> <!-- vim:set sw=4 sts=4 et: -->
...@@ -853,7 +853,103 @@ connect_to_signals (void) ...@@ -853,7 +853,103 @@ connect_to_signals (void)
return g_unix_fd_add (sfd, G_IO_IN, signal_handler, NULL); return g_unix_fd_add (sfd, G_IO_IN, signal_handler, NULL);
} }
/*
* If @fd is `stdin`, make `stdin` point to /dev/null and return a
* new fd that is a duplicate of the original `stdin`, so that the
* `stdin` inherited by child processes will not collide with the fd
* we are using for some other purpose.
*/
static int
avoid_stdin (int fd,
GError **error)
{
g_return_val_if_fail (fd >= 0, FALSE);
if (fd == STDIN_FILENO)
{
glnx_autofd int old_stdin = -1;
glnx_autofd int new_stdin = -1;
int fd_flags;
old_stdin = dup (STDIN_FILENO);
if (old_stdin < 0)
{
glnx_throw_errno_prefix (error,
"Unable to duplicate standard input");
return -1;
}
fd_flags = fcntl (old_stdin, F_GETFD);
if (fd_flags < 0 ||
fcntl (old_stdin, F_SETFD, fd_flags | FD_CLOEXEC) != 0)
{
glnx_throw_errno_prefix (error, "Unable to set flags on fd %d",
old_stdin);
return -1;
}
new_stdin = open ("/dev/null", O_RDONLY | O_CLOEXEC);
if (new_stdin < 0)
{
glnx_throw_errno_prefix (error, "Unable to open /dev/null");
return -1;
}
if (dup2 (new_stdin, STDIN_FILENO) != STDIN_FILENO)
{
glnx_throw_errno_prefix (error,
"Unable to make stdin point to /dev/null");
return -1;
}
fd = glnx_steal_fd (&old_stdin);
}
return fd;
}
static gboolean
exit_on_readable_cb (int fd,
GIOCondition condition,
gpointer user_data)
{
guint *id_p = user_data;
terminate_children (SIGTERM);
g_main_loop_quit (main_loop);
*id_p = 0;
return G_SOURCE_REMOVE;
}
static gboolean
set_up_exit_on_readable (int fd,
guint *id_p,
GError **error)
{
g_return_val_if_fail (fd >= 0, FALSE);
g_return_val_if_fail (id_p != NULL, FALSE);
g_return_val_if_fail (*id_p == 0, FALSE);
if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
{
return glnx_throw (error,
"--exit-on-readable fd cannot be stdout or stderr");
}
fd = avoid_stdin (fd, error);
if (fd < 0)
return FALSE;
*id_p = g_unix_fd_add (fd, G_IO_IN|G_IO_ERR|G_IO_HUP, exit_on_readable_cb, id_p);
return TRUE;
}
static gchar *opt_bus_name = NULL; static gchar *opt_bus_name = NULL;
static gint opt_exit_on_readable_fd = -1;
static gboolean opt_replace = FALSE; static gboolean opt_replace = FALSE;
static gchar *opt_socket = NULL; static gchar *opt_socket = NULL;
static gchar *opt_socket_directory = NULL; static gchar *opt_socket_directory = NULL;
...@@ -866,6 +962,11 @@ static GOptionEntry options[] = ...@@ -866,6 +962,11 @@ static GOptionEntry options[] =
G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_bus_name, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_bus_name,
"Use this well-known name on the D-Bus session bus.", "Use this well-known name on the D-Bus session bus.",
"NAME" }, "NAME" },
{ "exit-on-readable", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &opt_exit_on_readable_fd,
"Exit when data is available for reading or when end-of-file is "
"reached on this fd, usually 0 for stdin.",
"FD" },
{ "replace", '\0', { "replace", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_replace, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_replace,
"Replace a previous instance with the same bus name. " "Replace a previous instance with the same bus name. "
...@@ -907,6 +1008,7 @@ main (int argc, ...@@ -907,6 +1008,7 @@ main (int argc,
g_autoptr(AutoDBusServer) server = NULL; g_autoptr(AutoDBusServer) server = NULL;
g_autoptr(GOptionContext) context = NULL; g_autoptr(GOptionContext) context = NULL;
guint signals_id = 0; guint signals_id = 0;
guint exit_on_readable_id = 0;
g_autoptr(GError) local_error = NULL; g_autoptr(GError) local_error = NULL;
GError **error = &local_error; GError **error = &local_error;
gsize i; gsize i;
...@@ -957,6 +1059,16 @@ main (int argc, ...@@ -957,6 +1059,16 @@ main (int argc,
goto out; goto out;
} }
if (opt_exit_on_readable_fd >= 0)
{
if (!set_up_exit_on_readable (opt_exit_on_readable_fd,
&exit_on_readable_id, error))
{
ret = EX_OSERR;
goto out;
}
}
/* We have to block the signals we want to forward before we start any /* We have to block the signals we want to forward before we start any
* other thread, and in particular the GDBus worker thread, because * other thread, and in particular the GDBus worker thread, because
* the signal mask is per-thread. We need all threads to have the same * the signal mask is per-thread. We need all threads to have the same
...@@ -1154,6 +1266,9 @@ out: ...@@ -1154,6 +1266,9 @@ out:
if (local_error != NULL) if (local_error != NULL)
g_warning ("%s", local_error->message); g_warning ("%s", local_error->message);
if (exit_on_readable_id > 0)
g_source_remove (exit_on_readable_id);
if (signals_id > 0) if (signals_id > 0)
g_source_remove (signals_id); g_source_remove (signals_id);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment