2 This file is part of elogind.
4 Copyright 2017 Sven Eden
6 elogind is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 elogind is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with elogind; If not, see <http://www.gnu.org/licenses/>.
25 #include "mount-setup.h"
26 #include "socket-util.h"
27 #include "string-util.h"
29 #include "umask-util.h"
32 #define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024)
35 static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) {
36 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
37 Manager *m = userdata;
45 /* only accept org.freedesktop.elogind.Agent from UID=0 */
46 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
50 r = sd_bus_creds_get_euid(creds, &sender_uid);
51 if (r < 0 || sender_uid != 0)
54 /* parse 'cgroup-empty' notification */
55 r = sd_bus_message_read(message, "s", &cgroup);
57 bus_log_parse_error(r);
61 manager_notify_cgroup_empty(m, cgroup);
66 /// Add-On for manager_connect_bus()
67 void elogind_bus_setup_system(Manager* m) {
73 /* if we are a user instance we get the Released message via the system bus */
74 if (MANAGER_IS_USER(m)) {
79 "interface='org.freedesktop.elogind.Agent',"
81 "path='/org/freedesktop/elogind/agent'",
82 signal_agent_released, m);
84 log_warning_errno(r, "Failed to register Released match on system bus: %m");
87 log_debug("Successfully connected to system bus.");
90 static int bus_forward_agent_released(Manager *m, const char *path) {
96 if (!MANAGER_IS_SYSTEM(m))
102 /* If we are running a system instance we forward the agent message on the system bus, so that the user
103 * instances get notified about this, too */
105 r = sd_bus_emit_signal(m->bus,
106 "/org/freedesktop/elogind/agent",
107 "org.freedesktop.elogind.Agent",
111 return log_warning_errno(r, "Failed to propagate agent release message: %m");
116 static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
117 Manager *m = userdata;
118 char buf[PATH_MAX+1];
121 n = recv(fd, buf, sizeof(buf), 0);
123 return log_error_errno(errno, "Failed to read cgroups agent message: %m");
125 log_error("Got zero-length cgroups agent message, ignoring.");
128 if ((size_t) n >= sizeof(buf)) {
129 log_error("Got overly long cgroups agent message, ignoring.");
133 if (memchr(buf, 0, n)) {
134 log_error("Got cgroups agent message with embedded NUL byte, ignoring.");
139 manager_notify_cgroup_empty(m, buf);
140 bus_forward_agent_released(m, buf);
145 /// Add-On for manager_connect_bus()
146 int elogind_setup_cgroups_agent(Manager *m) {
148 static const union sockaddr_union sa = {
149 .un.sun_family = AF_UNIX,
150 .un.sun_path = "/run/systemd/cgroups-agent",
154 /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering
155 * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and
156 * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on
157 * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number
158 * of D-Bus connections may be queued until the kernel will start dropping further incoming connections,
159 * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX
160 * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and
161 * we thus won't lose messages.
163 * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen
164 * to it. The system instance hence listens on this special socket, but the user instances listen on the system
165 * bus for these messages. */
170 if (!MANAGER_IS_SYSTEM(m))
173 if (cg_unified() > 0) /* We don't need this anymore on the unified hierarchy */
176 if (m->cgroups_agent_fd < 0) {
177 _cleanup_close_ int fd = -1;
179 /* First free all secondary fields */
180 m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source);
182 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
184 return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m");
186 fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE);
188 (void) unlink(sa.un.sun_path);
190 /* Only allow root to connect to this socket */
192 r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
194 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
196 m->cgroups_agent_fd = fd;
200 if (!m->cgroups_agent_event_source) {
201 r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m);
203 return log_error_errno(r, "Failed to allocate cgroups agent event source: %m");
205 /* Process cgroups notifications early, but after having processed service notification messages or
206 * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification,
207 * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of
208 * cgroup inotify for the unified cgroup stuff. */
209 r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5);
211 return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m");
213 (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent");
219 /// Add-On for manager_free()
220 void elogind_manager_free(Manager* m) {
222 manager_shutdown_cgroup(m, true);
224 sd_event_source_unref(m->cgroups_agent_event_source);
226 safe_close(m->cgroups_agent_fd);
228 strv_free(m->suspend_mode);
229 strv_free(m->suspend_state);
230 strv_free(m->hibernate_mode);
231 strv_free(m->hibernate_state);
232 strv_free(m->hybrid_sleep_mode);
233 strv_free(m->hybrid_sleep_state);
236 /// Add-On for manager_new()
237 int elogind_manager_new(Manager* m) {
240 m->cgroups_agent_fd = -1;
241 m->pin_cgroupfs_fd = -1;
244 /* Init sleep modes and states */
245 m->suspend_mode = NULL;
246 m->suspend_state = NULL;
247 m->hibernate_mode = NULL;
248 m->hibernate_state = NULL;
249 m->hybrid_sleep_mode = NULL;
250 m->hybrid_sleep_state = NULL;
252 /* If elogind should be its own controller, mount its cgroup */
253 if (streq(SYSTEMD_CGROUP_CONTROLLER, "name=elogind")) {
255 r = mount_setup(true);
257 m->is_system = false;
261 r = manager_setup_cgroup(m);
266 /// Add-On for manager_reset_config()
267 void elogind_manager_reset_config(Manager* m) {
269 #ifdef ENABLE_DEBUG_ELOGIND
271 #endif // ENABLE_DEBUG_ELOGIND
273 /* Set default Sleep config if not already set by logind.conf */
274 if (!m->suspend_state)
275 m->suspend_state = strv_new("mem", "standby", "freeze", NULL);
276 if (!m->hibernate_mode)
277 m->hibernate_mode = strv_new("platform", "shutdown", NULL);
278 if (!m->hibernate_state)
279 m->hibernate_state = strv_new("disk", NULL);
280 if (!m->hybrid_sleep_mode)
281 m->hybrid_sleep_mode = strv_new("suspend", "platform", "shutdown", NULL);
282 if (!m->hybrid_sleep_state)
283 m->hybrid_sleep_state = strv_new("disk", NULL);
285 #ifdef ENABLE_DEBUG_ELOGIND
287 while (m->suspend_mode && m->suspend_mode[++dbg_cnt])
288 log_debug_elogind("suspend_mode[%d] = %s",
289 dbg_cnt, m->suspend_mode[dbg_cnt]);
291 while (m->suspend_state[++dbg_cnt])
292 log_debug_elogind("suspend_state[%d] = %s",
293 dbg_cnt, m->suspend_state[dbg_cnt]);
295 while (m->hibernate_mode[++dbg_cnt])
296 log_debug_elogind("hibernate_mode[%d] = %s",
297 dbg_cnt, m->hibernate_mode[dbg_cnt]);
299 while (m->hibernate_state[++dbg_cnt])
300 log_debug_elogind("hibernate_state[%d] = %s",
301 dbg_cnt, m->hibernate_state[dbg_cnt]);
303 while (m->hybrid_sleep_mode[++dbg_cnt])
304 log_debug_elogind("hybrid_sleep_mode[%d] = %s",
305 dbg_cnt, m->hybrid_sleep_mode[dbg_cnt]);
307 while (m->hybrid_sleep_state[++dbg_cnt])
308 log_debug_elogind("hybrid_sleep_state[%d] = %s",
309 dbg_cnt, m->hybrid_sleep_state[dbg_cnt]);
310 #endif // ENABLE_DEBUG_ELOGIND