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