803fb7
From 92b12c7dc013c95bd0d35bae99ff6df023ce0e1f Mon Sep 17 00:00:00 2001
803fb7
From: Lennart Poettering <lennart@poettering.net>
803fb7
Date: Wed, 4 May 2016 20:43:23 +0200
803fb7
Subject: [PATCH] core: use an AF_UNIX/SOCK_DGRAM socket for cgroup agent
803fb7
 notification
803fb7
803fb7
dbus-daemon currently uses a backlog of 30 on its D-bus system bus socket. On
803fb7
overloaded systems this means that only 30 connections may be queued without
803fb7
dbus-daemon processing them before further connection attempts fail. Our
803fb7
cgroups-agent binary so far used D-Bus for its messaging, and hitting this
803fb7
limit hence may result in us losing cgroup empty messages.
803fb7
803fb7
This patch adds a seperate cgroup agent socket of type AF_UNIX/SOCK_DGRAM.
803fb7
Since sockets of these types need no connection set up, no listen() backlog
803fb7
applies. Our cgroup-agent binary will hence simply block as long as it can't
803fb7
enqueue its datagram message, so that we won't lose cgroup empty messages as
803fb7
likely anymore.
803fb7
803fb7
This also rearranges the ordering of the processing of SIGCHLD signals, service
803fb7
notification messages (sd_notify()...) and the two types of cgroup
803fb7
notifications (inotify for the unified hierarchy support, and agent for the
803fb7
classic hierarchy support). We now always process events for these in the
803fb7
following order:
803fb7
803fb7
  1. service notification messages  (SD_EVENT_PRIORITY_NORMAL-7)
803fb7
  2. SIGCHLD signals (SD_EVENT_PRIORITY_NORMAL-6)
803fb7
  3. cgroup inotify and cgroup agent (SD_EVENT_PRIORITY_NORMAL-5)
803fb7
803fb7
This is because when receiving SIGCHLD we invalidate PID information, which we
803fb7
need to process the service notification messages which are bound to PIDs.
803fb7
Hence the order between the first two items. And we want to process SIGCHLD
803fb7
metadata to detect whether a service is gone, before using cgroup
803fb7
notifications, to decide when a service is gone, since the former carries more
803fb7
useful metadata.
803fb7
803fb7
Related to this:
803fb7
https://bugs.freedesktop.org/show_bug.cgi?id=95264
803fb7
https://github.com/systemd/systemd/issues/1961
803fb7
803fb7
Cherry-picked from: d8fdc62037b5b0a9fd603ad5efd6b49f956f86b5
803fb7
Resolves: #1305608
803fb7
---
de8967
 src/cgroups-agent/cgroups-agent.c |  48 +++++-----
803fb7
 src/core/cgroup.c                 |   2 +
de8967
 src/core/dbus.c                   |  56 ++++++-----
803fb7
 src/core/dbus.h                   |   2 +
de8967
 src/core/manager.c                | 149 ++++++++++++++++++++++++++++--
803fb7
 src/core/manager.h                |   3 +
803fb7
 6 files changed, 198 insertions(+), 62 deletions(-)
803fb7
803fb7
diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c
803fb7
index 529e84303..2fe65830e 100644
803fb7
--- a/src/cgroups-agent/cgroups-agent.c
803fb7
+++ b/src/cgroups-agent/cgroups-agent.c
803fb7
@@ -1,5 +1,3 @@
803fb7
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
803fb7
-
803fb7
 /***
803fb7
   This file is part of systemd.
803fb7
 
803fb7
@@ -20,14 +18,21 @@
803fb7
 ***/
803fb7
 
803fb7
 #include <stdlib.h>
803fb7
+#include <sys/socket.h>
803fb7
 
803fb7
-#include "sd-bus.h"
803fb7
 #include "log.h"
803fb7
-#include "bus-util.h"
803fb7
+#include "socket-util.h"
803fb7
 
803fb7
 int main(int argc, char *argv[]) {
803fb7
-        _cleanup_bus_close_unref_ sd_bus *bus = NULL;
803fb7
-        int r;
803fb7
+
803fb7
+        static const union sockaddr_union sa = {
803fb7
+                .un.sun_family = AF_UNIX,
803fb7
+                .un.sun_path = "/run/systemd/cgroups-agent",
803fb7
+        };
803fb7
+
803fb7
+        _cleanup_close_ int fd = -1;
803fb7
+        ssize_t n;
803fb7
+        size_t l;
803fb7
 
803fb7
         if (argc != 2) {
803fb7
                 log_error("Incorrect number of arguments.");
803fb7
@@ -38,27 +43,22 @@ int main(int argc, char *argv[]) {
803fb7
         log_parse_environment();
803fb7
         log_open();
803fb7
 
803fb7
-        /* We send this event to the private D-Bus socket and then the
803fb7
-         * system instance will forward this to the system bus. We do
803fb7
-         * this to avoid an activation loop when we start dbus when we
803fb7
-         * are called when the dbus service is shut down. */
803fb7
-
803fb7
-        r = bus_open_system_systemd(&bus;;
803fb7
-        if (r < 0) {
803fb7
-                /* If we couldn't connect we assume this was triggered
803fb7
-                 * while systemd got restarted/transitioned from
803fb7
-                 * initrd to the system, so let's ignore this */
803fb7
-                log_debug_errno(r, "Failed to get D-Bus connection: %m");
803fb7
+        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
803fb7
+        if (fd < 0) {
803fb7
+                log_debug_errno(errno, "Failed to allocate socket: %m");
803fb7
+                return EXIT_FAILURE;
803fb7
+        }
803fb7
+
803fb7
+        l = strlen(argv[1]);
803fb7
+
803fb7
+        n = sendto(fd, argv[1], l, 0, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
803fb7
+        if (n < 0) {
803fb7
+                log_debug_errno(errno, "Failed to send cgroups agent message: %m");
803fb7
                 return EXIT_FAILURE;
803fb7
         }
803fb7
 
803fb7
-        r = sd_bus_emit_signal(bus,
803fb7
-                               "/org/freedesktop/systemd1/agent",
803fb7
-                               "org.freedesktop.systemd1.Agent",
803fb7
-                               "Released",
803fb7
-                               "s", argv[1]);
803fb7
-        if (r < 0) {
803fb7
-                log_debug_errno(r, "Failed to send signal message on private connection: %m");
803fb7
+        if ((size_t) n != l) {
803fb7
+                log_debug("Datagram size mismatch");
803fb7
                 return EXIT_FAILURE;
803fb7
         }
803fb7
 
803fb7
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
803fb7
index 10fdcc998..b7f08fb42 100644
803fb7
--- a/src/core/cgroup.c
803fb7
+++ b/src/core/cgroup.c
803fb7
@@ -1028,6 +1028,8 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
803fb7
         assert(m);
803fb7
         assert(cgroup);
803fb7
 
803fb7
+        log_debug("Got cgroup empty notification for: %s", cgroup);
803fb7
+
803fb7
         u = manager_get_unit_by_cgroup(m, cgroup);
803fb7
         if (u) {
803fb7
                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
803fb7
diff --git a/src/core/dbus.c b/src/core/dbus.c
803fb7
index 85b517486..29524d49a 100644
803fb7
--- a/src/core/dbus.c
803fb7
+++ b/src/core/dbus.c
803fb7
@@ -72,12 +72,37 @@ int bus_send_queued_message(Manager *m) {
803fb7
         return 0;
803fb7
 }
803fb7
 
803fb7
+int bus_forward_agent_released(Manager *m, const char *path) {
803fb7
+        int r;
803fb7
+
803fb7
+        assert(m);
803fb7
+        assert(path);
803fb7
+
803fb7
+        if (!m->running_as == SYSTEMD_SYSTEM)
803fb7
+                return 0;
803fb7
+
803fb7
+        if (!m->system_bus)
803fb7
+                return 0;
803fb7
+
803fb7
+        /* If we are running a system instance we forward the agent message on the system bus, so that the user
803fb7
+         * instances get notified about this, too */
803fb7
+
803fb7
+        r = sd_bus_emit_signal(m->system_bus,
803fb7
+                               "/org/freedesktop/systemd1/agent",
803fb7
+                               "org.freedesktop.systemd1.Agent",
803fb7
+                               "Released",
803fb7
+                               "s", path);
803fb7
+        if (r < 0)
803fb7
+                return log_warning_errno(r, "Failed to propagate agent release message: %m");
803fb7
+
803fb7
+        return 1;
803fb7
+}
803fb7
+
803fb7
 static int signal_agent_released(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
803fb7
         Manager *m = userdata;
803fb7
         const char *cgroup;
803fb7
         int r;
803fb7
 
803fb7
-        assert(bus);
803fb7
         assert(message);
803fb7
         assert(m);
803fb7
 
803fb7
@@ -88,16 +113,6 @@ static int signal_agent_released(sd_bus *bus, sd_bus_message *message, void *use
803fb7
         }
803fb7
 
803fb7
         manager_notify_cgroup_empty(m, cgroup);
803fb7
-
803fb7
-        if (m->running_as == SYSTEMD_SYSTEM && m->system_bus) {
803fb7
-                /* If we are running as system manager, forward the
803fb7
-                 * message to the system bus */
803fb7
-
803fb7
-                r = sd_bus_send(m->system_bus, message, NULL);
803fb7
-                if (r < 0)
803fb7
-                        log_warning_errno(r, "Failed to forward Released message: %m");
803fb7
-        }
803fb7
-
803fb7
         return 0;
803fb7
 }
803fb7
 
803fb7
@@ -679,25 +694,6 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
803fb7
                 return 0;
803fb7
         }
803fb7
 
803fb7
-        if (m->running_as == SYSTEMD_SYSTEM) {
803fb7
-                /* When we run as system instance we get the Released
803fb7
-                 * signal via a direct connection */
803fb7
-
803fb7
-                r = sd_bus_add_match(
803fb7
-                                bus,
803fb7
-                                NULL,
803fb7
-                                "type='signal',"
803fb7
-                                "interface='org.freedesktop.systemd1.Agent',"
803fb7
-                                "member='Released',"
803fb7
-                                "path='/org/freedesktop/systemd1/agent'",
803fb7
-                                signal_agent_released, m);
803fb7
-
803fb7
-                if (r < 0) {
803fb7
-                        log_warning_errno(r, "Failed to register Released match on new connection bus: %m");
803fb7
-                        return 0;
803fb7
-                }
803fb7
-        }
803fb7
-
803fb7
         r = bus_setup_disconnected_match(m, bus);
803fb7
         if (r < 0)
803fb7
                 return 0;
803fb7
diff --git a/src/core/dbus.h b/src/core/dbus.h
803fb7
index d04f5326c..c27d136e3 100644
803fb7
--- a/src/core/dbus.h
803fb7
+++ b/src/core/dbus.h
803fb7
@@ -40,3 +40,5 @@ int bus_verify_manage_unit_async(Manager *m, sd_bus_message *call, sd_bus_error
803fb7
 int bus_verify_manage_unit_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error);
803fb7
 int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
803fb7
 int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
803fb7
+
803fb7
+int bus_forward_agent_released(Manager *m, const char *path);
803fb7
diff --git a/src/core/manager.c b/src/core/manager.c
803fb7
index ee456fb79..370c8cbbe 100644
803fb7
--- a/src/core/manager.c
803fb7
+++ b/src/core/manager.c
803fb7
@@ -83,8 +83,10 @@
803fb7
 #define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
803fb7
 #define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3)
803fb7
 #define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
803fb7
+#define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024)
803fb7
 
803fb7
 static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
803fb7
+static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
803fb7
 static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
803fb7
 static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
803fb7
 static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
803fb7
@@ -456,11 +458,11 @@ static int manager_setup_signals(Manager *m) {
803fb7
         if (r < 0)
803fb7
                 return r;
803fb7
 
803fb7
-        /* Process signals a bit earlier than the rest of things, but
803fb7
-         * later than notify_fd processing, so that the notify
803fb7
-         * processing can still figure out to which process/service a
803fb7
-         * message belongs, before we reap the process. */
803fb7
-        r = sd_event_source_set_priority(m->signal_event_source, -5);
803fb7
+        /* Process signals a bit earlier than the rest of things, but later than notify_fd processing, so that the
803fb7
+         * notify processing can still figure out to which process/service a message belongs, before we reap the
803fb7
+         * process. Also, process this before handling cgroup notifications, so that we always collect child exit
803fb7
+         * status information before detecting that there's no process in a cgroup. */
803fb7
+        r = sd_event_source_set_priority(m->signal_event_source, -6);
803fb7
         if (r < 0)
803fb7
                 return r;
803fb7
 
803fb7
@@ -541,7 +543,7 @@ int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) {
803fb7
 
803fb7
         m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
803fb7
 
803fb7
-        m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = -1;
803fb7
+        m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = -1;
803fb7
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
803fb7
 
803fb7
         m->ask_password_inotify_fd = -1;
803fb7
@@ -689,8 +691,8 @@ static int manager_setup_notify(Manager *m) {
803fb7
                 if (r < 0)
803fb7
                         return log_error_errno(r, "Failed to allocate notify event source: %m");
803fb7
 
803fb7
-                /* Process signals a bit earlier than SIGCHLD, so that we can
803fb7
-                 * still identify to which service an exit message belongs */
803fb7
+                /* Process notification messages a bit earlier than SIGCHLD, so that we can still identify to which
803fb7
+                 * service an exit message belongs. */
803fb7
                 r = sd_event_source_set_priority(m->notify_event_source, -7);
803fb7
                 if (r < 0)
803fb7
                         return log_error_errno(r, "Failed to set priority of notify event source: %m");
803fb7
@@ -699,6 +701,77 @@ static int manager_setup_notify(Manager *m) {
803fb7
         return 0;
803fb7
 }
803fb7
 
803fb7
+static int manager_setup_cgroups_agent(Manager *m) {
803fb7
+
803fb7
+        static const union sockaddr_union sa = {
803fb7
+                .un.sun_family = AF_UNIX,
803fb7
+                .un.sun_path = "/run/systemd/cgroups-agent",
803fb7
+        };
803fb7
+        int r;
803fb7
+
803fb7
+        /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering
803fb7
+         * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and
803fb7
+         * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on
803fb7
+         * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number
803fb7
+         * of D-Bus connections may be queued until the kernel will start dropping further incoming connections,
803fb7
+         * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX
803fb7
+         * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and
803fb7
+         * we thus won't lose messages.
803fb7
+         *
803fb7
+         * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen
803fb7
+         * to it. The system instance hence listens on this special socket, but the user instances listen on the system
803fb7
+         * bus for these messages. */
803fb7
+
803fb7
+        if (m->test_run)
803fb7
+                return 0;
803fb7
+
803fb7
+        if (!m->running_as == SYSTEMD_SYSTEM)
803fb7
+                return 0;
803fb7
+
803fb7
+        if (m->cgroups_agent_fd < 0) {
803fb7
+                _cleanup_close_ int fd = -1;
803fb7
+
803fb7
+                /* First free all secondary fields */
803fb7
+                m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source);
803fb7
+
803fb7
+                fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
803fb7
+                if (fd < 0)
803fb7
+                        return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m");
803fb7
+
803fb7
+                fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE);
803fb7
+
803fb7
+                (void) unlink(sa.un.sun_path);
803fb7
+
803fb7
+                /* Only allow root to connect to this socket */
803fb7
+                RUN_WITH_UMASK(0077)
803fb7
+                        r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
803fb7
+                if (r < 0)
803fb7
+                        return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
803fb7
+
803fb7
+                m->cgroups_agent_fd = fd;
803fb7
+                fd = -1;
803fb7
+        }
803fb7
+
803fb7
+        if (!m->cgroups_agent_event_source) {
803fb7
+                r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m);
803fb7
+                if (r < 0)
803fb7
+                        return log_error_errno(r, "Failed to allocate cgroups agent event source: %m");
803fb7
+
803fb7
+                /* Process cgroups notifications early, but after having processed service notification messages or
803fb7
+                 * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification,
803fb7
+                 * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of
803fb7
+                 * cgroup inotify for the unified cgroup stuff. */
803fb7
+                r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5);
803fb7
+                if (r < 0)
803fb7
+                        return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m");
803fb7
+
803fb7
+                (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent");
803fb7
+        }
803fb7
+
803fb7
+        return 0;
803fb7
+}
803fb7
+
803fb7
+
803fb7
 static int manager_setup_kdbus(Manager *m) {
803fb7
 #ifdef ENABLE_KDBUS
803fb7
         _cleanup_free_ char *p = NULL;
803fb7
@@ -912,6 +985,7 @@ Manager* manager_free(Manager *m) {
803fb7
 
803fb7
         sd_event_source_unref(m->signal_event_source);
803fb7
         sd_event_source_unref(m->notify_event_source);
803fb7
+        sd_event_source_unref(m->cgroups_agent_event_source);
803fb7
         sd_event_source_unref(m->time_change_event_source);
803fb7
         sd_event_source_unref(m->jobs_in_progress_event_source);
803fb7
         sd_event_source_unref(m->idle_pipe_event_source);
803fb7
@@ -919,6 +993,7 @@ Manager* manager_free(Manager *m) {
803fb7
 
803fb7
         safe_close(m->signal_fd);
803fb7
         safe_close(m->notify_fd);
803fb7
+        safe_close(m->cgroups_agent_fd);
803fb7
         safe_close(m->time_change_fd);
803fb7
         safe_close(m->kdbus_fd);
803fb7
 
803fb7
@@ -1167,6 +1242,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
803fb7
         if (q < 0 && r == 0)
803fb7
                 r = q;
803fb7
 
803fb7
+        q = manager_setup_cgroups_agent(m);
803fb7
+        if (q < 0 && r == 0)
803fb7
+                r = q;
803fb7
+
803fb7
         /* We might have deserialized the kdbus control fd, but if we
803fb7
          * didn't, then let's create the bus now. */
803fb7
         manager_setup_kdbus(m);
803fb7
@@ -1492,6 +1571,35 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) {
803fb7
         return n;
803fb7
 }
803fb7
 
803fb7
+static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
803fb7
+        Manager *m = userdata;
803fb7
+        char buf[PATH_MAX+1];
803fb7
+        ssize_t n;
803fb7
+
803fb7
+        n = recv(fd, buf, sizeof(buf), 0);
803fb7
+        if (n < 0)
803fb7
+                return log_error_errno(errno, "Failed to read cgroups agent message: %m");
803fb7
+        if (n == 0) {
803fb7
+                log_error("Got zero-length cgroups agent message, ignoring.");
803fb7
+                return 0;
803fb7
+        }
803fb7
+        if ((size_t) n >= sizeof(buf)) {
803fb7
+                log_error("Got overly long cgroups agent message, ignoring.");
803fb7
+                return 0;
803fb7
+        }
803fb7
+
803fb7
+        if (memchr(buf, 0, n)) {
803fb7
+                log_error("Got cgroups agent message with embedded NUL byte, ignoring.");
803fb7
+                return 0;
803fb7
+        }
803fb7
+        buf[n] = 0;
803fb7
+
803fb7
+        manager_notify_cgroup_empty(m, buf);
803fb7
+        bus_forward_agent_released(m, buf);
803fb7
+
803fb7
+        return 0;
803fb7
+}
803fb7
+
803fb7
 static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n, FDSet *fds) {
803fb7
         _cleanup_strv_free_ char **tags = NULL;
803fb7
 
803fb7
@@ -2304,6 +2412,16 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
803fb7
                 fprintf(f, "notify-socket=%s\n", m->notify_socket);
803fb7
         }
803fb7
 
803fb7
+        if (m->cgroups_agent_fd >= 0) {
803fb7
+                int copy;
803fb7
+
803fb7
+                copy = fdset_put_dup(fds, m->cgroups_agent_fd);
803fb7
+                if (copy < 0)
803fb7
+                        return copy;
803fb7
+
803fb7
+                fprintf(f, "cgroups-agent-fd=%i\n", copy);
803fb7
+        }
803fb7
+
803fb7
         if (m->kdbus_fd >= 0) {
803fb7
                 int copy;
803fb7
 
803fb7
@@ -2473,6 +2591,17 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
803fb7
                         free(m->notify_socket);
803fb7
                         m->notify_socket = n;
803fb7
 
803fb7
+                } else if (startswith(l, "cgroups-agent-fd=")) {
803fb7
+                        int fd;
803fb7
+
803fb7
+                        if (safe_atoi(l + 17, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
803fb7
+                                log_debug("Failed to parse cgroups agent fd: %s", l + 10);
803fb7
+                        else {
803fb7
+                                m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source);
803fb7
+                                safe_close(m->cgroups_agent_fd);
803fb7
+                                m->cgroups_agent_fd = fdset_remove(fds, fd);
803fb7
+                        }
803fb7
+
803fb7
                 } else if (startswith(l, "kdbus-fd=")) {
803fb7
                         int fd;
803fb7
 
803fb7
@@ -2599,6 +2728,10 @@ int manager_reload(Manager *m) {
803fb7
         if (q < 0 && r >= 0)
803fb7
                 r = q;
803fb7
 
803fb7
+        q = manager_setup_cgroups_agent(m);
803fb7
+        if (q < 0 && r >= 0)
803fb7
+                r = q;
803fb7
+
803fb7
         /* Third, fire things up! */
803fb7
         q = manager_coldplug(m);
803fb7
         if (q < 0 && r >= 0)
803fb7
diff --git a/src/core/manager.h b/src/core/manager.h
803fb7
index d3971f168..3e855db46 100644
803fb7
--- a/src/core/manager.h
803fb7
+++ b/src/core/manager.h
803fb7
@@ -137,6 +137,9 @@ struct Manager {
803fb7
         int notify_fd;
803fb7
         sd_event_source *notify_event_source;
803fb7
 
803fb7
+        int cgroups_agent_fd;
803fb7
+        sd_event_source *cgroups_agent_event_source;
803fb7
+
803fb7
         int signal_fd;
803fb7
         sd_event_source *signal_event_source;
803fb7