chiark / gitweb /
Prep v231.6: Allow elogind to be daemonized using -D/--daemon option
authorSven Eden <yamakuzure@gmx.net>
Thu, 7 Sep 2017 09:01:12 +0000 (11:01 +0200)
committerSven Eden <yamakuzure@gmx.net>
Thu, 7 Sep 2017 09:01:12 +0000 (11:01 +0200)
Makefile.am
src/login/elogind.c
src/login/elogind.h
src/login/logind.c
src/login/org.freedesktop.login1.service.in [moved from src/login/org.freedesktop.login1.service with 83% similarity]

index b95a1e0171bac6b5fc9eccc56504fcc64926d309..5e0fa684352a0f0da42713c96d887d8ee6555d53 100644 (file)
@@ -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 $@
index 30cab4949820a90262b2042d0c3919e9e07e28cd..894827074aeca70bbe124a9c2cbd7eb905c2147b 100644 (file)
 #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;
+}
index 3a6b89b744893be8b6c26f6237b6fb056638173e..60b9f80dd1ae9dcf997b87c0c1e1eeee2293d965 100644 (file)
 #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
index 58c91fdfef792252e35f4622173260784f35a758..7452c5b82c125e06c8af6327a627c2d0f7ea9476 100644 (file)
@@ -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) {
similarity index 83%
rename from src/login/org.freedesktop.login1.service
rename to src/login/org.freedesktop.login1.service.in
index e6eaa00ff0808b6c9ca5744687e9814e175df54a..84c80cedd893aab1bdcc6763829066db3c03ab62 100644 (file)
@@ -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