summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Persch <chpe@src.gnome.org>2020-02-04 18:13:47 +0100
committerChristian Persch <chpe@src.gnome.org>2020-02-04 18:13:47 +0100
commit1d488966c70f97a86962e0f4a470451bf1cd223d (patch)
treed000fe8293f9c59c1c470eeaa8268a9991283afe
parent427eebbf210c0d090abe8f220e4dac7fc1021bb5 (diff)
downloadvte-1d488966c70f97a86962e0f4a470451bf1cd223d.tar.gz
systemd: 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. Fixes: 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
-rw-r--r--doc/reference/vte-sections.txt2
-rw-r--r--meson.build15
-rw-r--r--src/app/app.cc11
-rw-r--r--src/meson.build10
-rw-r--r--src/pty.cc61
-rw-r--r--src/systemd.cc104
-rw-r--r--src/systemd.hh33
-rw-r--r--src/vte/vtepty.h4
-rw-r--r--src/vtegtk.cc13
-rw-r--r--src/vtepty.cc34
10 files changed, 266 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..33534b4c 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -2715,7 +2715,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 */,
@@ -2724,6 +2724,7 @@ vte_terminal_spawn_sync(VteTerminal *terminal,
{
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
g_return_val_if_fail(argv != NULL, FALSE);
+ g_return_val_if_fail((spawn_flags & (VTE_SPAWN_NO_SYSTEMD_SCOPE | VTE_SPAWN_REQUIRE_SYSTEMD_SCOPE)) == 0, FALSE);
g_return_val_if_fail(child_setup_data == NULL || child_setup, FALSE);
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
@@ -2736,7 +2737,7 @@ vte_terminal_spawn_sync(VteTerminal *terminal,
working_directory,
argv,
envv,
- spawn_flags_,
+ spawn_flags,
child_setup, child_setup_data,
&pid,
-1 /* no timeout */,
@@ -2881,6 +2882,14 @@ spawn_async_cb (GObject *source,
* When the operation fails, @callback will be called with a -1 #GPid,
* and a non-%NULL #GError containing the error information.
*
+ * Beginning with 0.60, and 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.
+ * 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.
+ *
* Note that if @terminal has been destroyed before the operation is called,
* @callback will be called with a %NULL @terminal; you must not do anything
* in the callback besides freeing any resources associated with @user_data,
diff --git a/src/vtepty.cc b/src/vtepty.cc
index 01d7d3eb..5dfba033 100644
--- a/src/vtepty.cc
+++ b/src/vtepty.cc
@@ -95,6 +95,32 @@ _vte_pty_get_impl(VtePty* pty)
*/
/**
+ * 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,14 @@ async_spawn_run_in_thread(GTask *task,
* use a child setup function that unsets the FD_CLOEXEC flag on that file
* descriptor.
*
+ * Beginning with 0.60, and 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.
+ * 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