chiark / gitweb /
service: add minimal access control logic for notifcation socket
[elogind.git] / src / manager.c
index 97d05b52c36950cffcfa1b7508cf2866dbe3b3fa..c2d5e5f0ef8b84c7f05ae8d1e48a416cae2f1a2a 100644 (file)
@@ -53,6 +53,7 @@
 #include "dbus-job.h"
 #include "missing.h"
 #include "path-lookup.h"
+#include "special.h"
 
 /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
 #define GC_QUEUE_ENTRIES_MAX 16
@@ -69,7 +70,6 @@ static int manager_setup_notify(Manager *m) {
                 struct sockaddr_un un;
         } sa;
         struct epoll_event ev;
-        char *ne[2], **t;
         int one = 1;
 
         assert(m);
@@ -105,19 +105,9 @@ static int manager_setup_notify(Manager *m) {
         if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
                 return -errno;
 
-        if (asprintf(&ne[0], "NOTIFY_SOCKET=@%s", sa.un.sun_path+1) < 0)
+        if (!(m->notify_socket = strdup(sa.un.sun_path+1)))
                 return -ENOMEM;
 
-        ne[1] = NULL;
-        t = strv_env_merge(m->environment, ne, NULL);
-        free(ne[0]);
-
-        if (!t)
-                return -ENOMEM;
-
-        strv_free(m->environment);
-        m->environment = t;
-
         return 0;
 }
 
@@ -157,14 +147,23 @@ static int manager_setup_signals(Manager *m) {
         assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
 
         assert_se(sigemptyset(&mask) == 0);
-        assert_se(sigaddset(&mask, SIGCHLD) == 0);
-        assert_se(sigaddset(&mask, SIGTERM) == 0);
-        assert_se(sigaddset(&mask, SIGHUP) == 0);
-        assert_se(sigaddset(&mask, SIGUSR1) == 0);
-        assert_se(sigaddset(&mask, SIGUSR2) == 0);
-        assert_se(sigaddset(&mask, SIGINT) == 0);   /* Kernel sends us this on control-alt-del */
-        assert_se(sigaddset(&mask, SIGWINCH) == 0); /* Kernel sends us this on kbrequest (alt-arrowup) */
-        assert_se(sigaddset(&mask, SIGPWR) == 0);   /* Some kernel drivers and upsd send us this on power failure */
+
+        sigset_add_many(&mask,
+                        SIGCHLD,     /* Child died */
+                        SIGTERM,     /* Reexecute daemon */
+                        SIGHUP,      /* Reload configuration */
+                        SIGUSR1,     /* systemd/upstart: reconnect to D-Bus */
+                        SIGUSR2,     /* systemd: dump status */
+                        SIGINT,      /* Kernel sends us this on control-alt-del */
+                        SIGWINCH,    /* Kernel sends us this on kbrequest (alt-arrowup) */
+                        SIGPWR,      /* Some kernel drivers and upsd send us this on power failure */
+                        SIGRTMIN+0,  /* systemd: start default.target */
+                        SIGRTMIN+1,  /* systemd: start rescue.target */
+                        SIGRTMIN+2,  /* systemd: isolate emergency.target */
+                        SIGRTMIN+3,  /* systemd: start halt.target */
+                        SIGRTMIN+4,  /* systemd: start poweroff.target */
+                        SIGRTMIN+5,  /* systemd: start reboot.target */
+                        -1);
         assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
 
         m->signal_watch.type = WATCH_SIGNAL;
@@ -187,6 +186,7 @@ static int manager_setup_signals(Manager *m) {
 int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) {
         Manager *m;
         int r = -ENOMEM;
+        char *p;
 
         assert(_m);
         assert(running_as >= 0);
@@ -201,6 +201,7 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) {
         m->confirm_spawn = confirm_spawn;
         m->name_data_slot = -1;
         m->exit_code = _MANAGER_EXIT_CODE_INVALID;
+        m->pin_cgroupfs_fd = -1;
 
         m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = -1;
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
@@ -246,6 +247,14 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) {
             (r = bus_init_api(m)) < 0)
                 goto fail;
 
+        if (asprintf(&p, "%s/%s", m->cgroup_mount_point, m->cgroup_hierarchy) < 0) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        m->pin_cgroupfs_fd = open(p, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
+        free(p);
+
         *_m = m;
         return 0;
 
@@ -431,14 +440,20 @@ void manager_free(Manager *m) {
         if (m->notify_watch.fd >= 0)
                 close_nointr_nofail(m->notify_watch.fd);
 
+        free(m->notify_socket);
+
         lookup_paths_free(&m->lookup_paths);
         strv_free(m->environment);
 
         free(m->cgroup_controller);
         free(m->cgroup_hierarchy);
+        free(m->cgroup_mount_point);
 
         hashmap_free(m->cgroup_bondings);
 
+        if (m->pin_cgroupfs_fd >= 0)
+                close_nointr_nofail(m->pin_cgroupfs_fd);
+
         free(m);
 }
 
@@ -1648,7 +1663,7 @@ static int manager_process_notify_fd(Manager *m) {
                 log_debug("Got notification message for unit %s", u->meta.id);
 
                 if (UNIT_VTABLE(u)->notify_message)
-                        UNIT_VTABLE(u)->notify_message(u, tags);
+                        UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
 
                 strv_free(tags);
         }
@@ -1731,10 +1746,10 @@ static int manager_dispatch_sigchld(Manager *m) {
         return 0;
 }
 
-static int manager_start_target(Manager *m, const char *name) {
+static int manager_start_target(Manager *m, const char *name, JobMode mode) {
         int r;
 
-        if ((r = manager_add_job_by_name(m, JOB_START, name, JOB_REPLACE, true, NULL)) < 0)
+        if ((r = manager_add_job_by_name(m, JOB_START, name, mode, true, NULL)) < 0)
                 log_error("Failed to enqueue %s job: %s", name, strerror(-r));
 
         return r;
@@ -1777,12 +1792,12 @@ static int manager_process_signal_fd(Manager *m) {
 
                 case SIGINT:
                         if (m->running_as == MANAGER_INIT) {
-                                manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET);
+                                manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE);
                                 break;
                         }
 
                         /* Run the exit target if there is one, if not, just exit. */
-                        if (manager_start_target(m, SPECIAL_EXIT_SERVICE) < 0) {
+                        if (manager_start_target(m, SPECIAL_EXIT_SERVICE, JOB_REPLACE) < 0) {
                                 m->exit_code = MANAGER_EXIT;
                                 return 0;
                         }
@@ -1791,14 +1806,14 @@ static int manager_process_signal_fd(Manager *m) {
 
                 case SIGWINCH:
                         if (m->running_as == MANAGER_INIT)
-                                manager_start_target(m, SPECIAL_KBREQUEST_TARGET);
+                                manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
 
                         /* This is a nop on non-init */
                         break;
 
                 case SIGPWR:
                         if (m->running_as == MANAGER_INIT)
-                                manager_start_target(m, SPECIAL_SIGPWR_TARGET);
+                                manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
 
                         /* This is a nop on non-init */
                         break;
@@ -1816,7 +1831,7 @@ static int manager_process_signal_fd(Manager *m) {
 
                         if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
                                 log_info("Loading D-Bus service...");
-                                manager_start_target(m, SPECIAL_DBUS_SERVICE);
+                                manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
                         }
 
                         break;
@@ -1853,9 +1868,26 @@ static int manager_process_signal_fd(Manager *m) {
                         m->exit_code = MANAGER_RELOAD;
                         break;
 
-                default:
+                default: {
+                        static const char * const table[] = {
+                                [0] = SPECIAL_DEFAULT_TARGET,
+                                [1] = SPECIAL_RESCUE_TARGET,
+                                [2] = SPECIAL_EMERGENCY_SERVICE,
+                                [3] = SPECIAL_HALT_TARGET,
+                                [4] = SPECIAL_POWEROFF_TARGET,
+                                [5] = SPECIAL_REBOOT_TARGET
+                        };
+
+                        if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
+                            (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(table)) {
+                                manager_start_target(m, table[sfsi.ssi_signo - SIGRTMIN],
+                                                     sfsi.ssi_signo == 2 ? JOB_ISOLATE : JOB_REPLACE);
+                                break;
+                        }
+
                         log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo));
                 }
+                }
         }
 
         if (sigchld)