From 7c401f91ca900a8ddb2718cbdf7c18dddbe69d59 Mon Sep 17 00:00:00 2001 From: Sven Eden Date: Thu, 7 Sep 2017 11:01:12 +0200 Subject: [PATCH] Prep v231.6: Allow elogind to be daemonized using -D/--daemon option --- Makefile.am | 6 +- src/login/elogind.c | 247 ++++++++++++++++++ src/login/elogind.h | 10 +- src/login/logind.c | 12 + ...vice => org.freedesktop.login1.service.in} | 3 +- 5 files changed, 273 insertions(+), 5 deletions(-) rename src/login/{org.freedesktop.login1.service => org.freedesktop.login1.service.in} (83%) diff --git a/Makefile.am b/Makefile.am index b95a1e017..5e0fa6843 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1274,7 +1274,8 @@ gperf_gperf_sources = \ EXTRA_DIST += \ src/login/71-seat.rules.in \ src/login/73-seat-late.rules.in \ - src/login/logind.conf.in + src/login/logind.conf.in \ + src/login/org.freedesktop.login1.service.in # ------------------------------------------------------------------------------ substitutions = \ @@ -1324,6 +1325,9 @@ shell-completion/%: shell-completion/%.in %.conf: %.conf.in $(SED_PROCESS) +%.login1.service: %.login1.service.in + $(SED_PROCESS) + %.sh: %.sh.in $(SED_PROCESS) $(AM_V_GEN)chmod +x $@ diff --git a/src/login/elogind.c b/src/login/elogind.c index 30cab4949..894827074 100644 --- a/src/login/elogind.c +++ b/src/login/elogind.c @@ -22,14 +22,173 @@ #include "cgroup.h" #include "elogind.h" #include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "mount-setup.h" +#include "parse-util.h" +#include "process-util.h" +#include "signal-util.h" #include "socket-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "umask-util.h" #define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024) +#ifndef ELOGIND_PID_FILE +# define ELOGIND_PID_FILE "/run/elogind.pid" +#endif // ELOGIND_PID_FILE + + +static int elogind_signal_handler(sd_event_source *s, + const struct signalfd_siginfo *si, + void *userdata) { + Manager *m = userdata; + int r; + + log_warning("Received signal %u [%s]", si->ssi_signo, + signal_to_string(si->ssi_signo)); + + r = sd_event_get_state(m->event); + + if (r != SD_EVENT_FINISHED) + sd_event_exit(m->event, si->ssi_signo); + + return 0; +} + + +static void remove_pid_file(void) { + if (access(ELOGIND_PID_FILE, F_OK) == 0) + unlink_noerrno(ELOGIND_PID_FILE); +} + + +static void write_pid_file(void) { + char c[DECIMAL_STR_MAX(pid_t) + 2]; + pid_t pid; + int r; + + pid = getpid(); + + xsprintf(c, PID_FMT "\n", pid); + + r = write_string_file(ELOGIND_PID_FILE, c, + WRITE_STRING_FILE_CREATE | + WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_error_errno(-r, "Failed to write PID file %s: %m", + ELOGIND_PID_FILE); + + /* Make sure the PID file gets cleaned up on exit! */ + atexit(remove_pid_file); +} + + +/** daemonize elogind by double forking. + * The grand child returns 0. + * The parent and child return their forks PID. + * On error, a value < 0 is returned. +**/ +static int elogind_daemonize(void) { + pid_t child; + pid_t grandchild; + pid_t SID; + int r; + +#ifdef ENABLE_DEBUG_ELOGIND + log_notice("Double forking elogind"); + log_notice("Parent PID : %5d", getpid()); + log_notice("Parent SID : %5d", getsid(getpid())); +#endif // ENABLE_DEBUG_ELOGIND + + child = fork(); + + if (child < 0) + return log_error_errno(errno, "Failed to fork: %m"); + + if (child) { + /* Wait for the child to terminate, so the decoupling + * is guaranteed to succeed. + */ + r = wait_for_terminate_and_warn("elogind control child", child, true); + if (r < 0) + return r; + return child; + } + +#ifdef ENABLE_DEBUG_ELOGIND + log_notice("Child PID : %5d", getpid()); + log_notice("Child SID : %5d", getsid(getpid())); +#endif // ENABLE_DEBUG_ELOGIND + + /* The first child has to become a new session leader. */ + close_all_fds(NULL, 0); + SID = setsid(); + if ((pid_t)-1 == SID) + return log_error_errno(errno, "Failed to create new SID: %m"); + +#ifdef ENABLE_DEBUG_ELOGIND + log_notice("Child new SID : %5d", getsid(getpid())); +#endif // ENABLE_DEBUG_ELOGIND + + umask(0022); + + /* Now the grandchild, the true daemon, can be created. */ + grandchild = fork(); + + if (grandchild < 0) + return log_error_errno(errno, "Failed to double fork: %m"); + + if (grandchild) + /* Exit immediately! */ + return grandchild; + + close_all_fds(NULL, 0); + umask(0022); + +#ifdef ENABLE_DEBUG_ELOGIND + log_notice("Grand child PID: %5d", getpid()); + log_notice("Grand child SID: %5d", getsid(getpid())); +#endif // ENABLE_DEBUG_ELOGIND + + /* Take care of our PID-file now */ + write_pid_file(); + + return 0; +} + + +/// Simple tool to see, if elogind is already running +static pid_t elogind_is_already_running(bool need_pid_file) { + _cleanup_free_ char *s = NULL; + pid_t pid; + int r; + + r = read_one_line_file(ELOGIND_PID_FILE, &s); + + if (r < 0) + goto we_are_alone; + + r = safe_atoi32(s, &pid); + + if (r < 0) + goto we_are_alone; + + if ( (pid != getpid()) && pid_is_alive(pid)) + return pid; + +we_are_alone: + + /* Take care of our PID-file now. + If the user is going to fork elogind, the PID file + will be overwritten. */ + if (need_pid_file) + write_pid_file(); + + return 0; +} static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { @@ -60,6 +219,7 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui return 0; } + /// Add-On for manager_connect_bus() /// Original: src/core/manager.c:manager_setup_cgroups_agent() int elogind_setup_cgroups_agent(Manager *m) { @@ -138,6 +298,66 @@ int elogind_setup_cgroups_agent(Manager *m) { return 0; } + +/** Extra functionality at startup, exclusive to elogind + * return < 0 on error, exit with failure. + * return = 0 on success, continue normal operation. + * return > 0 if elogind is already running or forked, exit with success. +**/ +int elogind_startup(int argc, char *argv[]) { + bool daemonize = false; + pid_t pid; + int r = 0; + bool show_help = false; + bool wrong_arg = false; + + /* add a -h/--help and a -d/--daemon argument. */ + if ( (argc == 2) && argv[1] && strlen(argv[1]) ) { + if ( streq(argv[1], "-D") || streq(argv[1], "--daemon") ) + daemonize = true; + else if ( streq(argv[1], "-h") || streq(argv[1], "--help") ) { + show_help = true; + r = 1; + } else + wrong_arg = true; + } else if (argc > 2) + wrong_arg = true; + + /* Note: At this point, the logging is not initialized, so we can not + use log_debug_elogind(). */ +#ifdef ENABLE_DEBUG_ELOGIND + log_notice("elogind startup: Daemonize: %s, Show Help: %s, Wrong arg: %s", + daemonize ? "True" : "False", + show_help ? "True" : "False", + wrong_arg ? "True" : "False"); +#endif // ENABLE_DEBUG_ELOGIND + + /* try to get some meaningful output in case of an error */ + if (wrong_arg) { + log_error("Unknown arguments"); + show_help = true; + r = -EINVAL; + } + if (show_help) { + log_info("%s [<-D|--daemon>|<-h|--help>]", basename(argv[0])); + return r; + } + + /* Do not continue if elogind is already running */ + pid = elogind_is_already_running(!daemonize); + if (pid) { + log_error("elogind is already running as PID " PID_FMT, pid); + return pid; + } + + /* elogind allows to be daemonized using one argument "-D" / "--daemon" */ + if (daemonize) + r = elogind_daemonize(); + + return r; +} + + /// Add-On for manager_free() void elogind_manager_free(Manager* m) { @@ -155,6 +375,7 @@ void elogind_manager_free(Manager* m) { strv_free(m->hybrid_sleep_state); } + /// Add-On for manager_new() int elogind_manager_new(Manager* m) { int r = 0; @@ -185,6 +406,7 @@ int elogind_manager_new(Manager* m) { return r; } + /// Add-On for manager_reset_config() void elogind_manager_reset_config(Manager* m) { @@ -231,3 +453,28 @@ void elogind_manager_reset_config(Manager* m) { dbg_cnt, m->hybrid_sleep_state[dbg_cnt]); #endif // ENABLE_DEBUG_ELOGIND } + + +/// Add-On for manager_startup() +int elogind_manager_startup(Manager *m) { + int r; + + assert(m); + + assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, -1) >= 0); + r = sd_event_add_signal(m->event, NULL, SIGINT, elogind_signal_handler, m); + if (r < 0) + return log_error_errno(r, "Failed to register SIGINT handler: %m"); + + assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGQUIT, -1) >= 0); + r = sd_event_add_signal(m->event, NULL, SIGQUIT, elogind_signal_handler, m); + if (r < 0) + return log_error_errno(r, "Failed to register SIGQUIT handler: %m"); + + assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGTERM, -1) >= 0); + r = sd_event_add_signal(m->event, NULL, SIGTERM, elogind_signal_handler, m); + if (r < 0) + return log_error_errno(r, "Failed to register SIGTERM handler: %m"); + + return 0; +} diff --git a/src/login/elogind.h b/src/login/elogind.h index 3a6b89b74..60b9f80dd 100644 --- a/src/login/elogind.h +++ b/src/login/elogind.h @@ -26,8 +26,11 @@ #include "sd-bus.h" -/// Add-Ons for manager_connect_bus() -int elogind_setup_cgroups_agent(Manager *m); +/// Add-On for manager_connect_bus() +int elogind_setup_cgroups_agent(Manager *m); + +/// elogind has some extra functionality at startup, as it is not hooked into systemd. +int elogind_startup(int argc, char *argv[]); /// Add-On for manager_free() void elogind_manager_free(Manager* m); @@ -38,5 +41,8 @@ int elogind_manager_new(Manager* m); /// Add-On for manager_reset_config() void elogind_manager_reset_config(Manager* m); +/// Add-On for manager_startup() +int elogind_manager_startup(Manager *m); + #endif // ELOGIND_SRC_LOGIN_ELOGIN_H_INCLUDED diff --git a/src/login/logind.c b/src/login/logind.c index 58c91fdfe..7452c5b82 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -1161,6 +1161,10 @@ static int manager_startup(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register SIGHUP handler: %m"); +#if 1 /// elogind needs some extra preparations before connecting... + elogind_manager_startup(m); +#endif // 1 + /* Connect to console */ r = manager_connect_console(m); if (r < 0) @@ -1269,6 +1273,12 @@ int main(int argc, char *argv[]) { Manager *m = NULL; int r; +#if 1 /// perform extra checks for elogind startup + r = elogind_startup(argc, argv); + if (r) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +#endif // 0 + elogind_set_program_name(argv[0]); log_set_target(LOG_TARGET_AUTO); log_set_facility(LOG_AUTH); @@ -1281,11 +1291,13 @@ int main(int argc, char *argv[]) { umask(0022); +#if 0 /// elogind has some extra functionality at startup, argc can be != 1 if (argc != 1) { log_error("This program takes no arguments."); r = -EINVAL; goto finish; } +#endif // 0 r = mac_selinux_init(); if (r < 0) { diff --git a/src/login/org.freedesktop.login1.service b/src/login/org.freedesktop.login1.service.in similarity index 83% rename from src/login/org.freedesktop.login1.service rename to src/login/org.freedesktop.login1.service.in index e6eaa00ff..84c80cedd 100644 --- a/src/login/org.freedesktop.login1.service +++ b/src/login/org.freedesktop.login1.service.in @@ -7,6 +7,5 @@ [D-BUS Service] Name=org.freedesktop.login1 -Exec=/bin/false +Exec=@rootlibexecdir@/elogind --daemon User=root -SystemdService=dbus-org.freedesktop.login1.service -- 2.30.2