[vte/wip/systemd: 3/4] lib: Add systemd support
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte/wip/systemd: 3/4] lib: Add systemd support
- Date: Sun, 2 Feb 2020 22:22:12 +0000 (UTC)
commit c68f43cc7f90c13bf6f3b21af320e259aba13673
Author: Christian Persch <chpe src gnome org>
Date: Sun Feb 2 23:16:16 2020 +0100
lib: Add systemd support
Move newly created child processes into their own systemd user scope.
Apparently this is required so that when the OOM killer catches one
of gnome-terminal-server's child processes, it doesn't also kill
gnome-terminal-server itself and thus all and every terminals in it.
https://gitlab.gnome.org/GNOME/gnome-terminal/issues/206
https://bugzilla.gnome.org/show_bug.cgi?id=744736
https://bugzilla.redhat.com/show_bug.cgi?id=1796828
doc/reference/vte-sections.txt | 2 +
meson.build | 15 ++++--
src/app/app.cc | 11 ++++-
src/meson.build | 10 ++++
src/pty.cc | 61 ++++++++++++++++++------
src/systemd.cc | 104 +++++++++++++++++++++++++++++++++++++++++
src/systemd.hh | 33 +++++++++++++
src/vte/vtepty.h | 4 +-
src/vtegtk.cc | 13 +++++-
src/vtepty.cc | 35 ++++++++++++++
10 files changed, 267 insertions(+), 21 deletions(-)
---
diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt
index fa8844b3..20067c88 100644
--- a/doc/reference/vte-sections.txt
+++ b/doc/reference/vte-sections.txt
@@ -198,6 +198,8 @@ vte_pty_set_utf8
<SUBSECTION>
VTE_SPAWN_NO_PARENT_ENVV
+VTE_SPAWN_NO_SYSTEMD_SCOPE
+VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE
vte_pty_spawn_async
vte_pty_spawn_finish
diff --git a/meson.build b/meson.build
index c3b08772..845acb01 100644
--- a/meson.build
+++ b/meson.build
@@ -37,14 +37,15 @@ gtk3_max_allowed_version = '3.20'
gtk4_req_version = '4.0.0'
fribidi_req_version = '1.0.0'
-gio_req_version = '2.44.0'
-glib_req_version = '2.44.0'
-glib_min_req_version = '2.44'
-glib_max_allowed_version = '2.44'
+gio_req_version = '2.52.0'
+glib_req_version = '2.52.0'
+glib_min_req_version = '2.52'
+glib_max_allowed_version = '2.52'
gnutls_req_version = '3.2.7'
icu_uc_req_version = '4.8'
pango_req_version = '1.22.0'
pcre2_req_version = '10.21'
+systemd_req_version = '202'
# API
@@ -435,6 +436,12 @@ else
icu_dep = dependency('', required: false)
endif
+if host_machine.system() == 'linux'
+ systemd_dep = dependency('libsystemd', version: '>=' + systemd_req_version)
+else
+ systemd_dep = dependency('', required: false)
+endif
+
# Write config.h
configure_file(
diff --git a/src/app/app.cc b/src/app/app.cc
index 0a66ad00..6257c2f1 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -59,7 +59,9 @@ public:
gboolean no_rewrap{false};
gboolean no_shaping{false};
gboolean no_shell{false};
+ gboolean no_systemd_scope{false};
gboolean object_notifications{false};
+ gboolean require_systemd_scope{false};
gboolean reverse{false};
gboolean test_mode{false};
gboolean version{false};
@@ -403,12 +405,16 @@ public:
"Disable Arabic shaping", nullptr },
{ "no-shell", 'S', 0, G_OPTION_ARG_NONE, &no_shell,
"Disable spawning a shell inside the terminal", nullptr },
+ { "no-systemd-scope", 0, 0, G_OPTION_ARG_NONE, &no_systemd_scope,
+ "Don't use systemd user scope", nullptr },
{ "object-notifications", 'N', 0, G_OPTION_ARG_NONE, &object_notifications,
"Print VteTerminal object notifications", nullptr },
{ "output-file", 0, 0, G_OPTION_ARG_FILENAME, &output_filename,
"Save terminal contents to file at exit", nullptr },
{ "reverse", 0, 0, G_OPTION_ARG_NONE, &reverse,
"Reverse foreground/background colors", nullptr },
+ { "require-systemd-scope", 0, 0, G_OPTION_ARG_NONE, &require_systemd_scope,
+ "Require use of a systemd user scope", nullptr },
{ "scrollback-lines", 'n', 0, G_OPTION_ARG_INT, &scrollback_lines,
"Specify the number of scrollback-lines (-1 for infinite)", nullptr },
{ "transparent", 'T', 0, G_OPTION_ARG_INT, &transparency_percent,
@@ -1201,12 +1207,15 @@ vteapp_window_launch_argv(VteappWindow* window,
char** argv,
GError** error)
{
+ auto const spawn_flags = GSpawnFlags(G_SPAWN_SEARCH_PATH_FROM_ENVP |
+ (options.no_systemd_scope ? VTE_SPAWN_NO_SYSTEMD_SCOPE : 0) |
+ (options.require_systemd_scope ?
VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE : 0));
vte_terminal_spawn_async(window->terminal,
VTE_PTY_DEFAULT,
options.working_directory,
argv,
options.environment,
- G_SPAWN_SEARCH_PATH_FROM_ENVP,
+ spawn_flags,
nullptr, nullptr, nullptr, /* child setup, data and destroy */
30 * 1000 /* 30s timeout */,
nullptr /* cancellable */,
diff --git a/src/meson.build b/src/meson.build
index a385716f..4242dd61 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -79,6 +79,11 @@ regex_sources = files(
'regex.hh'
)
+systemd_sources = files(
+ 'systemd.cc',
+ 'systemd.hh',
+)
+
utf8_sources = files(
'utf8.cc',
'utf8.hh',
@@ -142,6 +147,10 @@ if get_option('icu')
libvte_common_sources += icu_sources
endif
+if host_machine.system() == 'linux'
+ libvte_common_sources += systemd_sources
+endif
+
libvte_common_doc_sources = files(
# These file contain gtk-doc comments to be extracted for docs and gir
'pty.cc',
@@ -188,6 +197,7 @@ libvte_common_deps = libvte_common_public_deps + [
pcre2_dep,
libm_dep,
pthreads_dep,
+ systemd_dep,
zlib_dep,
]
diff --git a/src/pty.cc b/src/pty.cc
index c17600c0..1333c5d2 100644
--- a/src/pty.cc
+++ b/src/pty.cc
@@ -74,6 +74,10 @@
#include "glib-glue.hh"
+#ifdef __linux__
+#include "systemd.hh"
+#endif
+
/* NSIG isn't in POSIX, so if it doesn't exist use this here. See bug #759196 */
#ifndef NSIG
#define NSIG (8 * sizeof(sigset_t))
@@ -344,20 +348,19 @@ pty_child_setup_cb(void* data)
}
/*
- * __vte_pty_spawn:
- * @pty: a #VtePty
- * @directory: the name of a directory the command should start in, or %NULL
+ * Pty::spawn:
+ * @directory: the name of a directory the command should start in, or %nullptr
* to use the cwd
* @argv: child's argument vector
* @envv: a list of environment variables to be added to the environment before
- * starting the process, or %NULL
+ * starting the process, or %nullptr
* @spawn_flags: flags from #GSpawnFlags
* @child_setup: function to run in the child just before exec()
* @child_setup_data: user data for @child_setup
- * @child_pid: a location to store the child PID, or %NULL
- * @timeout: a timeout value in ms, or %NULL
- * @cancellable: a #GCancellable, or %NULL
- * @error: return location for a #GError, or %NULL
+ * @child_pid: a location to store the child PID, or %nullptr
+ * @timeout: a timeout value in ms, or %nullptr
+ * @cancellable: a #GCancellable, or %nullptr
+ * @error: return location for a #GError, or %nullptr
*
* Uses g_spawn_async() to spawn the command in @argv. The child's environment will
* be the parent environment with the variables in @envv set afterwards.
@@ -393,6 +396,14 @@ Pty::spawn(char const* directory,
int i;
GPollFD pollfd;
+#ifndef __linux__
+ if (spawn_flags & VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE) {
+ g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "systemd not available");
+ return false;
+ }
+#endif
+
if (cancellable && !g_cancellable_make_pollfd(cancellable, &pollfd)) {
vte::util::restore_errno errsv;
g_set_error(error,
@@ -432,13 +443,14 @@ Pty::spawn(char const* directory,
m_extra_child_setup.func = child_setup_func;
m_extra_child_setup.data = child_setup_data;
+ auto pid = pid_t{-1};
auto err = vte::glib::Error{};
ret = vte_spawn_async_with_pipes_cancellable(directory,
argv, envp2,
(GSpawnFlags)spawn_flags,
(GSpawnChildSetupFunc)pty_child_setup_cb,
this,
- child_pid,
+ &pid,
nullptr, nullptr, nullptr,
timeout,
cancellable ? &pollfd : nullptr,
@@ -453,7 +465,7 @@ Pty::spawn(char const* directory,
(GSpawnFlags)spawn_flags,
(GSpawnChildSetupFunc)pty_child_setup_cb,
this,
- child_pid,
+ &pid,
nullptr, nullptr, nullptr,
timeout,
cancellable ? &pollfd : nullptr,
@@ -468,10 +480,33 @@ Pty::spawn(char const* directory,
if (cancellable)
g_cancellable_release_fd(cancellable);
- if (ret)
- return true;
+#ifdef __linux__
+ if (ret &&
+ !(spawn_flags & VTE_SPAWN_NO_SYSTEMD_SCOPE) &&
+ !vte::systemd::create_scope_for_pid_sync(pid,
+ timeout, // FIXME: recalc timeout
+ cancellable,
+ err)) {
+ if (spawn_flags & VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE) {
+ auto pgrp = getpgid(pid);
+ if (pgrp != -1) {
+ kill(-pgrp, SIGHUP);
+ }
+
+ kill(pid, SIGHUP);
+
+ ret = false;
+ } else {
+ err.reset();
+ }
+ }
+#endif // __linux__
+
+ if (!ret)
+ return err.propagate(error);
- return err.propagate(error);
+ *child_pid = pid;
+ return true;
}
/*
diff --git a/src/systemd.cc b/src/systemd.cc
new file mode 100644
index 00000000..4ba40c6a
--- /dev/null
+++ b/src/systemd.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2020 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "systemd.hh"
+
+#include <memory>
+
+#include <systemd/sd-login.h>
+
+#include "glib-glue.hh"
+#include "refptr.hh"
+
+namespace vte::systemd {
+
+bool
+create_scope_for_pid_sync(pid_t pid,
+ int timeout,
+ GCancellable* cancellable,
+ GError** error)
+{
+ {
+ char* unit = nullptr;
+ if (auto r = sd_pid_get_user_unit(pid, &unit) < 0) {
+ g_set_error(error, G_IO_ERROR, g_io_error_from_errno(-r),
+ "Failed sd_pid_get_user_unit(%d): %s",
+ pid,
+ g_strerror(-r));
+ return false;
+ }
+ free(unit);
+ }
+
+ auto bus = vte::glib::take_ref(g_bus_get_sync(G_BUS_TYPE_SESSION, cancellable, error));
+ if (!bus)
+ return false;
+
+ auto uuid = vte::glib::take_string(g_uuid_string_random());
+ auto scope = vte::glib::take_string(g_strdup_printf("vte-spawn-%s.scope", uuid.get()));
+ auto prgname = vte::glib::take_string(g_utf8_make_valid(g_get_prgname(), -1));
+ auto description = vte::glib::take_string(g_strdup_printf("VTE child process %d launched by %s
process %d", pid, prgname.get(), getpid()));
+
+ auto builder_stack = GVariantBuilder{};
+ auto builder = &builder_stack;
+ g_variant_builder_init(builder, G_VARIANT_TYPE("(ssa(sv)a(sa(sv)))"));
+
+ g_variant_builder_add(builder, "s", scope.get()); // unit name
+ g_variant_builder_add(builder, "s", "fail"); // failure mode
+
+ // Unit properties
+ g_variant_builder_open(builder, G_VARIANT_TYPE("a(sv)"));
+
+ g_variant_builder_add(builder, "(sv)", "CollectMode", g_variant_new_string("inactive-or-failed"));
+ g_variant_builder_add(builder, "(sv)", "Description", g_variant_new_string(description.get()));
+
+ g_variant_builder_open(builder, G_VARIANT_TYPE("(sv)"));
+ g_variant_builder_add(builder, "s", "PIDs");
+ g_variant_builder_open(builder, G_VARIANT_TYPE("v"));
+ g_variant_builder_open(builder, G_VARIANT_TYPE("au"));
+ g_variant_builder_add(builder, "u", unsigned(pid));
+ g_variant_builder_close(builder); // au
+ g_variant_builder_close(builder); // v
+ g_variant_builder_close(builder); // (sv)
+
+ g_variant_builder_close(builder); // a(sv)
+
+ // No auxiliary units
+ g_variant_builder_open(builder, G_VARIANT_TYPE("a(sa(sv))"));
+ g_variant_builder_close(builder);
+
+ // Create transient scope
+ auto reply = std::unique_ptr<GVariant, decltype(&g_variant_unref)>
+ {g_dbus_connection_call_sync(bus.get(),
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartTransientUnit",
+ g_variant_builder_end(builder), // parameters
+ G_VARIANT_TYPE("(o)"), // reply type,
+ GDBusCallFlags{G_DBUS_CALL_FLAGS_NO_AUTO_START},
+ timeout, // in ms
+ cancellable,
+ error),
+ &g_variant_unref};
+
+ return bool(reply);
+}
+
+} // namespace vte::systemd
diff --git a/src/systemd.hh b/src/systemd.hh
new file mode 100644
index 00000000..0857fd0e
--- /dev/null
+++ b/src/systemd.hh
@@ -0,0 +1,33 @@
+/*
+ * Copyright © 2020 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+namespace vte::systemd {
+
+bool create_scope_for_pid_sync(pid_t pid,
+ int timeout,
+ GCancellable* cancellable,
+ GError** error);
+
+} // namespace vte::systemd
diff --git a/src/vte/vtepty.h b/src/vte/vtepty.h
index 3b7aac9e..06822740 100644
--- a/src/vte/vtepty.h
+++ b/src/vte/vtepty.h
@@ -30,7 +30,9 @@
G_BEGIN_DECLS
-#define VTE_SPAWN_NO_PARENT_ENVV (1 << 25)
+#define VTE_SPAWN_NO_PARENT_ENVV (1 << 25)
+#define VTE_SPAWN_NO_SYSTEMD_SCOPE (1 << 26)
+#define VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE (1 << 27)
_VTE_PUBLIC
GQuark vte_pty_error_quark (void);
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index 35eee92f..080095ef 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -2705,6 +2705,15 @@ vte_terminal_watch_child (VteTerminal *terminal,
* The caller should also make sure that symlinks were preserved while constructing the value of
@working_directory,
* e.g. by using vte_terminal_get_current_directory_uri(), g_get_current_dir() or get_current_dir_name().
*
+ * On linux only, and unless %VTE_SPAWN_NO_SYSTEMD_SCOPE is passed in @spawn_flags,
+ * the newly created child process will be moved to its own systemd user scope; and
+ * if %VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE is passed, and creation of the systemd user
+ * scope fails, the whole spawn will fail.
+ *
+ * Note that you can override the options used for the systemd user scope by
+ * providing a systemd override file for 'vte-spawn-.scope' unit. See man:systemd.unit(5)
+ * for further information.
+ *
* Returns: %TRUE on success, or %FALSE on error with @error filled in
*
* Deprecated: 0.48: Use vte_terminal_spawn_async() instead.
@@ -2715,7 +2724,7 @@ vte_terminal_spawn_sync(VteTerminal *terminal,
const char *working_directory,
char **argv,
char **envv,
- GSpawnFlags spawn_flags_,
+ GSpawnFlags spawn_flags,
GSpawnChildSetupFunc child_setup,
gpointer child_setup_data,
GPid *child_pid /* out */,
@@ -2736,7 +2745,7 @@ vte_terminal_spawn_sync(VteTerminal *terminal,
working_directory,
argv,
envv,
- spawn_flags_,
+ spawn_flags,
child_setup, child_setup_data,
&pid,
-1 /* no timeout */,
diff --git a/src/vtepty.cc b/src/vtepty.cc
index 01d7d3eb..88415046 100644
--- a/src/vtepty.cc
+++ b/src/vtepty.cc
@@ -94,6 +94,32 @@ _vte_pty_get_impl(VtePty* pty)
* to vte_pty_spawn_async() etc. are passed to the child process.
*/
+/**
+ * VTE_SPAWN_NO_SYSTEMD_SCOPE:
+ *
+ * Use this as a spawn flag (together with flags from #GSpawnFlags) in
+ * vte_pty_spawn_async().
+ *
+ * Prevents vte_pty_spawn_async() etc. from moving the newly created child
+ * process to a systemd user scope.
+ *
+ * Since: 0.60
+ */
+
+/**
+ * VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE
+ *
+ * Use this as a spawn flag (together with flags from #GSpawnFlags) in
+ * vte_pty_spawn_async().
+ *
+ * Requires vte_pty_spawn_async() etc. to move the newly created child
+ * process to a systemd user scope; if that fails, the whole spawn fails.
+ *
+ * This is supported on Linux only.
+ *
+ * Since: 0.60
+ */
+
/**
* vte_pty_child_setup:
* @pty: a #VtePty
@@ -664,6 +690,15 @@ async_spawn_run_in_thread(GTask *task,
* use a child setup function that unsets the FD_CLOEXEC flag on that file
* descriptor.
*
+ * On linux only, and unless %VTE_SPAWN_NO_SYSTEMD_SCOPE is passed in @spawn_flags,
+ * the newly created child process will be moved to its own systemd user scope; and
+ * if %VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE is passed, and creation of the systemd user
+ * scope fails, the whole spawn will fail.
+ *
+ * Note that you can override the options used for the systemd user scope by
+ * providing a systemd override file for 'vte-spawn-.scope' unit. See man:systemd.unit(5)
+ * for further information.
+ *
* See vte_pty_new(), g_spawn_async() and vte_terminal_watch_child() for more information.
*
* Since: 0.48
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]