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/>.
27 #include "mount-setup.h"
28 #include "parse-util.h"
29 #include "process-util.h"
30 #include "signal-util.h"
31 #include "socket-util.h"
32 #include "stdio-util.h"
33 #include "string-util.h"
35 #include "umask-util.h"
38 #define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024)
39 #ifndef ELOGIND_PID_FILE
40 # define ELOGIND_PID_FILE "/run/elogind.pid"
41 #endif // ELOGIND_PID_FILE
44 static int elogind_signal_handler(sd_event_source *s,
45 const struct signalfd_siginfo *si,
47 Manager *m = userdata;
50 log_warning("Received signal %u [%s]", si->ssi_signo,
51 signal_to_string(si->ssi_signo));
53 r = sd_event_get_state(m->event);
55 if (r != SD_EVENT_FINISHED)
56 sd_event_exit(m->event, si->ssi_signo);
62 static void remove_pid_file(void) {
63 if (access(ELOGIND_PID_FILE, F_OK) == 0)
64 unlink_noerrno(ELOGIND_PID_FILE);
68 static void write_pid_file(void) {
69 char c[DECIMAL_STR_MAX(pid_t) + 2];
73 pid = getpid_cached();
75 xsprintf(c, PID_FMT "\n", pid);
77 r = write_string_file(ELOGIND_PID_FILE, c,
78 WRITE_STRING_FILE_CREATE |
79 WRITE_STRING_FILE_VERIFY_ON_FAILURE);
81 log_error_errno(-r, "Failed to write PID file %s: %m",
84 /* Make sure the PID file gets cleaned up on exit! */
85 atexit(remove_pid_file);
89 /** daemonize elogind by double forking.
90 * The grand child returns 0.
91 * The parent and child return their forks PID.
92 * On error, a value < 0 is returned.
94 static int elogind_daemonize(void) {
100 #ifdef ENABLE_DEBUG_ELOGIND
101 log_notice("Double forking elogind");
102 log_notice("Parent PID : %5d", getpid_cached());
103 log_notice("Parent SID : %5d", getsid(getpid_cached()));
104 #endif // ENABLE_DEBUG_ELOGIND
109 return log_error_errno(errno, "Failed to fork: %m");
112 /* Wait for the child to terminate, so the decoupling
113 * is guaranteed to succeed.
115 r = wait_for_terminate_and_warn("elogind control child", child, true);
121 #ifdef ENABLE_DEBUG_ELOGIND
122 log_notice("Child PID : %5d", getpid_cached());
123 log_notice("Child SID : %5d", getsid(getpid_cached()));
124 #endif // ENABLE_DEBUG_ELOGIND
126 /* The first child has to become a new session leader. */
127 close_all_fds(NULL, 0);
129 /* close_all_fds() does not close 0,1,2 */
135 if ((pid_t)-1 == SID)
136 return log_error_errno(errno, "Failed to create new SID: %m");
138 #ifdef ENABLE_DEBUG_ELOGIND
139 log_notice("Child new SID : %5d", getsid(getpid_cached()));
140 #endif // ENABLE_DEBUG_ELOGIND
144 /* Now the grandchild, the true daemon, can be created. */
148 return log_error_errno(errno, "Failed to double fork: %m");
151 /* Exit immediately! */
154 close_all_fds(NULL, 0);
157 #ifdef ENABLE_DEBUG_ELOGIND
158 log_notice("Grand child PID: %5d", getpid_cached());
159 log_notice("Grand child SID: %5d", getsid(getpid_cached()));
160 #endif // ENABLE_DEBUG_ELOGIND
162 /* Take care of our PID-file now */
169 /// Simple tool to see, if elogind is already running
170 static pid_t elogind_is_already_running(bool need_pid_file) {
171 _cleanup_free_ char *s = NULL;
175 r = read_one_line_file(ELOGIND_PID_FILE, &s);
180 r = safe_atoi32(s, &pid);
185 if ( (pid != getpid_cached()) && pid_is_alive(pid))
190 /* Take care of our PID-file now.
191 If the user is going to fork elogind, the PID file
192 will be overwritten. */
200 static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
201 Manager *m = userdata;
202 char buf[PATH_MAX+1];
205 n = recv(fd, buf, sizeof(buf), 0);
207 return log_error_errno(errno, "Failed to read cgroups agent message: %m");
209 log_error("Got zero-length cgroups agent message, ignoring.");
212 if ((size_t) n >= sizeof(buf)) {
213 log_error("Got overly long cgroups agent message, ignoring.");
217 if (memchr(buf, 0, n)) {
218 log_error("Got cgroups agent message with embedded NUL byte, ignoring.");
223 manager_notify_cgroup_empty(m, buf);
229 /// Add-On for manager_connect_bus()
230 /// Original: src/core/manager.c:manager_setup_cgroups_agent()
231 int elogind_setup_cgroups_agent(Manager *m) {
233 static const union sockaddr_union sa = {
234 .un.sun_family = AF_UNIX,
235 .un.sun_path = "/run/systemd/cgroups-agent",
239 /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering
240 * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and
241 * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on
242 * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number
243 * of D-Bus connections may be queued until the kernel will start dropping further incoming connections,
244 * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX
245 * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and
246 * we thus won't lose messages.
248 * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen
249 * to it. The system instance hence listens on this special socket, but the user instances listen on the system
250 * bus for these messages. */
252 if (m->test_run_flags)
255 if (!MANAGER_IS_SYSTEM(m))
258 r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
260 return log_error_errno(r, "Failed to determine whether unified cgroups hierarchy is used: %m");
261 if (r > 0) /* We don't need this anymore on the unified hierarchy */
264 if (m->cgroups_agent_fd < 0) {
265 _cleanup_close_ int fd = -1;
267 /* First free all secondary fields */
268 m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source);
270 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
272 return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m");
274 fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE);
276 (void) unlink(sa.un.sun_path);
278 /* Only allow root to connect to this socket */
280 r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
282 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
284 m->cgroups_agent_fd = fd;
288 if (!m->cgroups_agent_event_source) {
289 r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m);
291 return log_error_errno(r, "Failed to allocate cgroups agent event source: %m");
293 /* Process cgroups notifications early, but after having processed service notification messages or
294 * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification,
295 * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of
296 * cgroup inotify for the unified cgroup stuff. */
297 r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5);
299 return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m");
301 (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent");
308 /** Extra functionality at startup, exclusive to elogind
309 * return < 0 on error, exit with failure.
310 * return = 0 on success, continue normal operation.
311 * return > 0 if elogind is already running or forked, exit with success.
313 int elogind_startup(int argc, char *argv[]) {
314 bool daemonize = false;
317 bool show_help = false;
318 bool wrong_arg = false;
320 /* add a -h/--help and a -d/--daemon argument. */
321 if ( (argc == 2) && argv[1] && strlen(argv[1]) ) {
322 if ( streq(argv[1], "-D") || streq(argv[1], "--daemon") )
324 else if ( streq(argv[1], "-h") || streq(argv[1], "--help") ) {
332 /* Note: At this point, the logging is not initialized, so we can not
333 use log_debug_elogind(). */
334 #ifdef ENABLE_DEBUG_ELOGIND
335 log_notice("elogind startup: Daemonize: %s, Show Help: %s, Wrong arg: %s",
336 daemonize ? "True" : "False",
337 show_help ? "True" : "False",
338 wrong_arg ? "True" : "False");
339 #endif // ENABLE_DEBUG_ELOGIND
341 /* try to get some meaningful output in case of an error */
343 log_error("Unknown arguments");
348 log_info("%s [<-D|--daemon>|<-h|--help>]", basename(argv[0]));
352 /* Do not continue if elogind is already running */
353 pid = elogind_is_already_running(!daemonize);
355 log_error("elogind is already running as PID " PID_FMT, pid);
359 /* elogind allows to be daemonized using one argument "-D" / "--daemon" */
361 r = elogind_daemonize();
367 /// Add-On for manager_free()
368 void elogind_manager_free(Manager* m) {
370 manager_shutdown_cgroup(m, true);
372 sd_event_source_unref(m->cgroups_agent_event_source);
374 safe_close(m->cgroups_agent_fd);
376 strv_free(m->suspend_mode);
377 strv_free(m->suspend_state);
378 strv_free(m->hibernate_mode);
379 strv_free(m->hibernate_state);
380 strv_free(m->hybrid_sleep_mode);
381 strv_free(m->hybrid_sleep_state);
385 /// Add-On for manager_new()
386 int elogind_manager_new(Manager* m) {
389 m->cgroups_agent_fd = -1;
390 m->pin_cgroupfs_fd = -1;
391 m->test_run_flags = 0;
393 /* Init sleep modes and states */
394 m->suspend_mode = NULL;
395 m->suspend_state = NULL;
396 m->hibernate_mode = NULL;
397 m->hibernate_state = NULL;
398 m->hybrid_sleep_mode = NULL;
399 m->hybrid_sleep_state = NULL;
401 /* If elogind should be its own controller, mount its cgroup */
402 if (streq(SYSTEMD_CGROUP_CONTROLLER, "_elogind")) {
404 r = mount_setup(true);
406 m->is_system = false;
410 r = manager_setup_cgroup(m);
416 /// Add-On for manager_reset_config()
417 void elogind_manager_reset_config(Manager* m) {
419 #ifdef ENABLE_DEBUG_ELOGIND
421 #endif // ENABLE_DEBUG_ELOGIND
423 /* Set default Sleep config if not already set by logind.conf */
424 if (!m->suspend_state)
425 m->suspend_state = strv_new("mem", "standby", "freeze", NULL);
426 if (!m->hibernate_mode)
427 m->hibernate_mode = strv_new("platform", "shutdown", NULL);
428 if (!m->hibernate_state)
429 m->hibernate_state = strv_new("disk", NULL);
430 if (!m->hybrid_sleep_mode)
431 m->hybrid_sleep_mode = strv_new("suspend", "platform", "shutdown", NULL);
432 if (!m->hybrid_sleep_state)
433 m->hybrid_sleep_state = strv_new("disk", NULL);
435 #ifdef ENABLE_DEBUG_ELOGIND
437 while (m->suspend_mode && m->suspend_mode[++dbg_cnt])
438 log_debug_elogind("suspend_mode[%d] = %s",
439 dbg_cnt, m->suspend_mode[dbg_cnt]);
441 while (m->suspend_state[++dbg_cnt])
442 log_debug_elogind("suspend_state[%d] = %s",
443 dbg_cnt, m->suspend_state[dbg_cnt]);
445 while (m->hibernate_mode[++dbg_cnt])
446 log_debug_elogind("hibernate_mode[%d] = %s",
447 dbg_cnt, m->hibernate_mode[dbg_cnt]);
449 while (m->hibernate_state[++dbg_cnt])
450 log_debug_elogind("hibernate_state[%d] = %s",
451 dbg_cnt, m->hibernate_state[dbg_cnt]);
453 while (m->hybrid_sleep_mode[++dbg_cnt])
454 log_debug_elogind("hybrid_sleep_mode[%d] = %s",
455 dbg_cnt, m->hybrid_sleep_mode[dbg_cnt]);
457 while (m->hybrid_sleep_state[++dbg_cnt])
458 log_debug_elogind("hybrid_sleep_state[%d] = %s",
459 dbg_cnt, m->hybrid_sleep_state[dbg_cnt]);
460 #endif // ENABLE_DEBUG_ELOGIND
464 /// Add-On for manager_startup()
465 int elogind_manager_startup(Manager *m) {
470 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, -1) >= 0);
471 r = sd_event_add_signal(m->event, NULL, SIGINT, elogind_signal_handler, m);
473 return log_error_errno(r, "Failed to register SIGINT handler: %m");
475 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGQUIT, -1) >= 0);
476 r = sd_event_add_signal(m->event, NULL, SIGQUIT, elogind_signal_handler, m);
478 return log_error_errno(r, "Failed to register SIGQUIT handler: %m");
480 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGTERM, -1) >= 0);
481 r = sd_event_add_signal(m->event, NULL, SIGTERM, elogind_signal_handler, m);
483 return log_error_errno(r, "Failed to register SIGTERM handler: %m");