summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Marriott <nicholas.marriott@gmail.com>2023-04-03 08:50:49 +0100
committerNicholas Marriott <nicholas.marriott@gmail.com>2023-04-03 08:54:06 +0100
commitb9524f5b72d16bd634fc47ad1a4a9d3240bd4370 (patch)
tree3ba1c13ceb700287d649b6caeb20fc2fe63be977
parent22eb0334c325245e7a49610f91c7842cb6408f4d (diff)
Add support for spawning panes in separate cgroups with systemd and a configure
flag to disable. From Eric T Johnson yut23 AT gvljohnsons DOT com in GitHub issue 3514.
-rw-r--r--compat.h1
-rw-r--r--compat/systemd.c149
-rw-r--r--configure.ac15
-rw-r--r--spawn.c14
4 files changed, 178 insertions, 1 deletions
diff --git a/compat.h b/compat.h
index aefc0d38..cabdf3ad 100644
--- a/compat.h
+++ b/compat.h
@@ -425,6 +425,7 @@ void *recallocarray(void *, size_t, size_t, size_t);
/* systemd.c */
int systemd_activated(void);
int systemd_create_socket(int, char **);
+int systemd_move_pid_to_new_cgroup(pid_t, char **);
#endif
#ifdef HAVE_UTF8PROC
diff --git a/compat/systemd.c b/compat/systemd.c
index cce42ed4..6f790a32 100644
--- a/compat/systemd.c
+++ b/compat/systemd.c
@@ -19,7 +19,10 @@
#include <sys/types.h>
#include <sys/un.h>
+#include <systemd/sd-bus.h>
#include <systemd/sd-daemon.h>
+#include <systemd/sd-login.h>
+#include <systemd/sd-id128.h>
#include <string.h>
@@ -64,3 +67,149 @@ fail:
xasprintf(cause, "systemd socket error (%s)", strerror(errno));
return (-1);
}
+
+int
+systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
+{
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus_message *m = NULL, *reply = NULL;
+ sd_bus *bus = NULL;
+ char *name, *desc, *slice;
+ sd_id128_t uuid;
+ int r;
+ pid_t parent_pid;
+
+ /* Connect to the session bus. */
+ r = sd_bus_default_user(&bus);
+ if (r < 0) {
+ xasprintf(cause, "failed to connect to session bus: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* Start building the method call. */
+ r = sd_bus_message_new_method_call(bus, &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartTransientUnit");
+ if (r < 0) {
+ xasprintf(cause, "failed to create bus message: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* Generate a unique name for the new scope, to avoid collisions. */
+ r = sd_id128_randomize(&uuid);
+ if (r < 0) {
+ xasprintf(cause, "failed to generate uuid: %s", strerror(-r));
+ goto finish;
+ }
+ xasprintf(&name, "tmux-spawn-" SD_ID128_UUID_FORMAT_STR ".scope",
+ SD_ID128_FORMAT_VAL(uuid));
+ r = sd_bus_message_append(m, "s", name);
+ free(name);
+ if (r < 0) {
+ xasprintf(cause, "failed to append to bus message: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* Mode: fail if there's a queued unit with the same name. */
+ r = sd_bus_message_append(m, "s", "fail");
+ if (r < 0) {
+ xasprintf(cause, "failed to append to bus message: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* Start properties array. */
+ r = sd_bus_message_open_container(m, 'a', "(sv)");
+ if (r < 0) {
+ xasprintf(cause, "failed to start properties array: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ parent_pid = getpid();
+ xasprintf(&desc, "tmux child pane %ld launched by process %ld",
+ (long)pid, (long)parent_pid);
+ r = sd_bus_message_append(m, "(sv)", "Description", "s", desc);
+ free(desc);
+ if (r < 0) {
+ xasprintf(cause, "failed to append to properties: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /*
+ * Inherit the slice from the parent process, or default to
+ * "app-tmux.slice" if that fails.
+ */
+ r = sd_pid_get_user_slice(parent_pid, &slice);
+ if (r < 0) {
+ slice = xstrdup("app-tmux.slice");
+ }
+ r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
+ free(slice);
+ if (r < 0) {
+ xasprintf(cause, "failed to append to properties: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* PIDs to add to the scope: length - 1 array of uint32_t. */
+ r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
+ if (r < 0) {
+ xasprintf(cause, "failed to append to properties: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* Clean up the scope even if it fails. */
+ r = sd_bus_message_append(m, "(sv)", "CollectMode", "s",
+ "inactive-or-failed");
+ if (r < 0) {
+ xasprintf(cause, "failed to append to properties: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* End properties array. */
+ r = sd_bus_message_close_container(m);
+ if (r < 0) {
+ xasprintf(cause, "failed to end properties array: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* aux is currently unused and should be passed an empty array. */
+ r = sd_bus_message_append(m, "a(sa(sv))", 0);
+ if (r < 0) {
+ xasprintf(cause, "failed to append to bus message: %s",
+ strerror(-r));
+ goto finish;
+ }
+
+ /* Call the method with a timeout of 1 second = 1e6 us. */
+ r = sd_bus_call(bus, m, 1000000, &error, &reply);
+ if (r < 0) {
+ if (error.message != NULL) {
+ /* We have a specific error message from sd-bus. */
+ xasprintf(cause, "StartTransientUnit call failed: %s",
+ error.message);
+ } else {
+ xasprintf(cause, "StartTransientUnit call failed: %s",
+ strerror(-r));
+ }
+ goto finish;
+ }
+
+finish:
+ sd_bus_error_free(&error);
+ sd_bus_message_unref(m);
+ sd_bus_message_unref(reply);
+ sd_bus_unref(bus);
+
+ return (r);
+}
diff --git a/configure.ac b/configure.ac
index 8e846042..4b9d75b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -420,6 +420,21 @@ if test x"$enable_systemd" = xyes; then
fi
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$found_systemd" = xyes])
+AC_ARG_ENABLE(
+ cgroups,
+ AS_HELP_STRING(--disable-cgroups, disable adding panes to new cgroups with systemd)
+)
+if test "x$enable_cgroups" = x; then
+ # Default to the same as $enable_systemd.
+ enable_cgroups=$enable_systemd
+fi
+if test "x$enable_cgroups" = xyes; then
+ if test "x$found_systemd" = xyes; then
+ AC_DEFINE(ENABLE_CGROUPS)
+ else
+ AC_MSG_ERROR("cgroups requires systemd to be enabled")
+ fi
+fi
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
AC_MSG_CHECKING(for b64_ntop)
diff --git a/spawn.c b/spawn.c
index e63133fe..10604028 100644
--- a/spawn.c
+++ b/spawn.c
@@ -380,8 +380,20 @@ spawn_pane(struct spawn_context *sc, char **cause)
}
/* In the parent process, everything is done now. */
- if (new_wp->pid != 0)
+ if (new_wp->pid != 0) {
+#if defined(HAVE_SYSTEMD) && defined(ENABLE_CGROUPS)
+ /*
+ * Move the child process into a new cgroup for systemd-oomd
+ * isolation.
+ */
+ if (systemd_move_pid_to_new_cgroup(new_wp->pid, cause) < 0) {
+ log_debug("%s: moving pane to new cgroup failed: %s",
+ __func__, *cause);
+ free (*cause);
+ }
+#endif
goto complete;
+ }
/*
* Child process. Change to the working directory or home if that