chiark / gitweb /
Prep v234: Re-add user/session to the gc_queue when stopping.
[elogind.git] / src / login / logind-inhibit.c
index fce2f4d..1e6f383 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
 
 #include <errno.h>
 #include <fcntl.h>
-#include <sys/epoll.h>
 #include <string.h>
-#include <errno.h>
 #include <unistd.h>
 
-#include "util.h"
-#include "mkdir.h"
-#include "path-util.h"
+#include "alloc-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
 #include "logind-inhibit.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "util.h"
 
 Inhibitor* inhibitor_new(Manager *m, const char* id) {
         Inhibitor *i;
@@ -41,17 +45,14 @@ Inhibitor* inhibitor_new(Manager *m, const char* id) {
                 return NULL;
 
         i->state_file = strappend("/run/systemd/inhibit/", id);
-        if (!i->state_file) {
-                free(i);
-                return NULL;
-        }
+        if (!i->state_file)
+                return mfree(i);
 
-        i->id = path_get_file_name(i->state_file);
+        i->id = basename(i->state_file);
 
         if (hashmap_put(m->inhibitors, i->id, i) < 0) {
                 free(i->state_file);
-                free(i);
-                return NULL;
+                return mfree(i);
         }
 
         i->manager = m;
@@ -63,12 +64,13 @@ Inhibitor* inhibitor_new(Manager *m, const char* id) {
 void inhibitor_free(Inhibitor *i) {
         assert(i);
 
-        free(i->who);
-        free(i->why);
-
         hashmap_remove(i->manager->inhibitors, i->id);
+
         inhibitor_remove_fifo(i);
 
+        free(i->who);
+        free(i->why);
+
         if (i->state_file) {
                 unlink(i->state_file);
                 free(i->state_file);
@@ -78,19 +80,19 @@ void inhibitor_free(Inhibitor *i) {
 }
 
 int inhibitor_save(Inhibitor *i) {
-        char *temp_path, *cc;
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
         int r;
-        FILE *f;
 
         assert(i);
 
         r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0);
         if (r < 0)
-                goto finish;
+                goto fail;
 
         r = fopen_temporary(i->state_file, &f, &temp_path);
         if (r < 0)
-                goto finish;
+                goto fail;
 
         fchmod(fileno(f), 0644);
 
@@ -98,52 +100,58 @@ int inhibitor_save(Inhibitor *i) {
                 "# This is private data. Do not parse.\n"
                 "WHAT=%s\n"
                 "MODE=%s\n"
-                "UID=%lu\n"
-                "PID=%lu\n",
+                "UID="UID_FMT"\n"
+                "PID="PID_FMT"\n",
                 inhibit_what_to_string(i->what),
                 inhibit_mode_to_string(i->mode),
-                (unsigned long) i->uid,
-                (unsigned long) i->pid);
+                i->uid,
+                i->pid);
 
         if (i->who) {
+                _cleanup_free_ char *cc = NULL;
+
                 cc = cescape(i->who);
-                if (!cc)
+                if (!cc) {
                         r = -ENOMEM;
-                else {
-                        fprintf(f, "WHO=%s\n", cc);
-                        free(cc);
+                        goto fail;
                 }
+
+                fprintf(f, "WHO=%s\n", cc);
         }
 
         if (i->why) {
+                _cleanup_free_ char *cc = NULL;
+
                 cc = cescape(i->why);
-                if (!cc)
+                if (!cc) {
                         r = -ENOMEM;
-                else {
-                        fprintf(f, "WHY=%s\n", cc);
-                        free(cc);
+                        goto fail;
                 }
+
+                fprintf(f, "WHY=%s\n", cc);
         }
 
         if (i->fifo_path)
                 fprintf(f, "FIFO=%s\n", i->fifo_path);
 
-        fflush(f);
+        r = fflush_and_check(f);
+        if (r < 0)
+                goto fail;
 
-        if (ferror(f) || rename(temp_path, i->state_file) < 0) {
+        if (rename(temp_path, i->state_file) < 0) {
                 r = -errno;
-                unlink(i->state_file);
-                unlink(temp_path);
+                goto fail;
         }
 
-        fclose(f);
-        free(temp_path);
+        return 0;
 
-finish:
-        if (r < 0)
-                log_error("Failed to save inhibit data for %s: %s", i->id, strerror(-r));
+fail:
+        (void) unlink(i->state_file);
 
-        return r;
+        if (temp_path)
+                (void) unlink(temp_path);
+
+        return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
 }
 
 int inhibitor_start(Inhibitor *i) {
@@ -154,16 +162,16 @@ int inhibitor_start(Inhibitor *i) {
 
         dual_timestamp_get(&i->since);
 
-        log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s started.",
+        log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
                   strna(i->who), strna(i->why),
-                  (unsigned long) i->pid, (unsigned long) i->uid,
+                  i->pid, i->uid,
                   inhibit_mode_to_string(i->mode));
 
         inhibitor_save(i);
 
         i->started = true;
 
-        manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited\0" : "DelayInhibited\0");
+        manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
 
         return 0;
 }
@@ -172,9 +180,9 @@ int inhibitor_stop(Inhibitor *i) {
         assert(i);
 
         if (i->started)
-                log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s stopped.",
+                log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
                           strna(i->who), strna(i->why),
-                          (unsigned long) i->pid, (unsigned long) i->uid,
+                          i->pid, i->uid,
                           inhibit_mode_to_string(i->mode));
 
         if (i->state_file)
@@ -182,16 +190,14 @@ int inhibitor_stop(Inhibitor *i) {
 
         i->started = false;
 
-        manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited\0" : "DelayInhibited\0");
+        manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
 
         return 0;
 }
 
 int inhibitor_load(Inhibitor *i) {
-        InhibitWhat w;
-        InhibitMode mm;
-        int r;
-        char *cc,
+
+        _cleanup_free_ char
                 *what = NULL,
                 *uid = NULL,
                 *pid = NULL,
@@ -199,6 +205,11 @@ int inhibitor_load(Inhibitor *i) {
                 *why = NULL,
                 *mode = NULL;
 
+        InhibitWhat w;
+        InhibitMode mm;
+        char *cc;
+        int r;
+
         r = parse_env_file(i->state_file, NEWLINE,
                            "WHAT", &what,
                            "UID", &uid,
@@ -209,7 +220,7 @@ int inhibitor_load(Inhibitor *i) {
                            "FIFO", &i->fifo_path,
                            NULL);
         if (r < 0)
-                goto finish;
+                return r;
 
         w = what ? inhibit_what_from_string(what) : 0;
         if (w >= 0)
@@ -222,32 +233,28 @@ int inhibitor_load(Inhibitor *i) {
         if (uid) {
                 r = parse_uid(uid, &i->uid);
                 if (r < 0)
-                        goto finish;
+                        return r;
         }
 
         if (pid) {
                 r = parse_pid(pid, &i->pid);
                 if (r < 0)
-                        goto finish;
+                        return r;
         }
 
         if (who) {
-                cc = cunescape(who);
-                if (!cc) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                r = cunescape(who, 0, &cc);
+                if (r < 0)
+                        return r;
 
                 free(i->who);
                 i->who = cc;
         }
 
         if (why) {
-                cc = cunescape(why);
-                if (!cc) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                r = cunescape(why, 0, &cc);
+                if (r < 0)
+                        return r;
 
                 free(i->why);
                 i->why = cc;
@@ -257,18 +264,23 @@ int inhibitor_load(Inhibitor *i) {
                 int fd;
 
                 fd = inhibitor_create_fifo(i);
-                if (fd >= 0)
-                        close_nointr_nofail(fd);
+                safe_close(fd);
         }
 
-finish:
-        free(what);
-        free(uid);
-        free(pid);
-        free(who);
-        free(why);
+        return 0;
+}
 
-        return r;
+static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        Inhibitor *i = userdata;
+
+        assert(s);
+        assert(fd == i->fifo_fd);
+        assert(i);
+
+        inhibitor_stop(i);
+        inhibitor_free(i);
+
+        return 0;
 }
 
 int inhibitor_create_fifo(Inhibitor *i) {
@@ -282,7 +294,8 @@ int inhibitor_create_fifo(Inhibitor *i) {
                 if (r < 0)
                         return r;
 
-                if (asprintf(&i->fifo_path, "/run/systemd/inhibit/%s.ref", i->id) < 0)
+                i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref");
+                if (!i->fifo_path)
                         return -ENOMEM;
 
                 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
@@ -291,22 +304,19 @@ int inhibitor_create_fifo(Inhibitor *i) {
 
         /* Open reading side */
         if (i->fifo_fd < 0) {
-                struct epoll_event ev;
-
                 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
                 if (i->fifo_fd < 0)
                         return -errno;
+        }
 
-                r = hashmap_put(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1), i);
+        if (!i->event_source) {
+                r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i);
                 if (r < 0)
                         return r;
 
-                zero(ev);
-                ev.events = 0;
-                ev.data.u32 = FD_OTHER_BASE + i->fifo_fd;
-
-                if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0)
-                        return -errno;
+                r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10);
+                if (r < 0)
+                        return r;
         }
 
         /* Open writing side */
@@ -320,17 +330,12 @@ int inhibitor_create_fifo(Inhibitor *i) {
 void inhibitor_remove_fifo(Inhibitor *i) {
         assert(i);
 
-        if (i->fifo_fd >= 0) {
-                assert_se(hashmap_remove(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1)) == i);
-                assert_se(epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_DEL, i->fifo_fd, NULL) == 0);
-                close_nointr_nofail(i->fifo_fd);
-                i->fifo_fd = -1;
-        }
+        i->event_source = sd_event_source_unref(i->event_source);
+        i->fifo_fd = safe_close(i->fifo_fd);
 
         if (i->fifo_path) {
                 unlink(i->fifo_path);
-                free(i->fifo_path);
-                i->fifo_path = NULL;
+                i->fifo_path = mfree(i->fifo_path);
         }
 }
 
@@ -341,8 +346,8 @@ InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
 
         assert(m);
 
-        HASHMAP_FOREACH(i, m->inhibitor_fds, j)
-                if (i->mode == mm)
+        HASHMAP_FOREACH(i, m->inhibitors, j)
+                if (i->mode == mm && i->started)
                         what |= i->what;
 
         return what;
@@ -353,9 +358,14 @@ static int pid_is_active(Manager *m, pid_t pid) {
         int r;
 
         r = manager_get_session_by_pid(m, pid, &s);
-        if (r <= 0)
+        if (r < 0)
                 return r;
 
+        /* If there's no session assigned to it, then it's globally
+         * active on all ttys */
+        if (r == 0)
+                return 1;
+
         return session_is_active(s);
 }
 
@@ -364,24 +374,33 @@ bool manager_is_inhibited(
                 InhibitWhat w,
                 InhibitMode mm,
                 dual_timestamp *since,
-                bool only_active) {
+                bool ignore_inactive,
+                bool ignore_uid,
+                uid_t uid,
+                Inhibitor **offending) {
 
         Inhibitor *i;
         Iterator j;
-        struct dual_timestamp ts = { 0, 0 };
+        struct dual_timestamp ts = DUAL_TIMESTAMP_NULL;
         bool inhibited = false;
 
         assert(m);
         assert(w > 0 && w < _INHIBIT_WHAT_MAX);
 
-        HASHMAP_FOREACH(i, m->inhibitor_fds, j) {
+        HASHMAP_FOREACH(i, m->inhibitors, j) {
+                if (!i->started)
+                        continue;
+
                 if (!(i->what & w))
                         continue;
 
                 if (i->mode != mm)
                         continue;
 
-                if (only_active && pid_is_active(m, i->pid) <= 0)
+                if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
+                        continue;
+
+                if (ignore_uid && i->uid == uid)
                         continue;
 
                 if (!inhibited ||
@@ -389,6 +408,9 @@ bool manager_is_inhibited(
                         ts = i->since;
 
                 inhibited = true;
+
+                if (offending)
+                        *offending = i;
         }
 
         if (since)
@@ -398,7 +420,7 @@ bool manager_is_inhibited(
 }
 
 const char *inhibit_what_to_string(InhibitWhat w) {
-        static __thread char buffer[73];
+        static thread_local char buffer[97];
         char *p;
 
         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
@@ -413,8 +435,10 @@ const char *inhibit_what_to_string(InhibitWhat w) {
                 p = stpcpy(p, "idle:");
         if (w & INHIBIT_HANDLE_POWER_KEY)
                 p = stpcpy(p, "handle-power-key:");
-        if (w & INHIBIT_HANDLE_SLEEP_KEY)
-                p = stpcpy(p, "handle-sleep-key:");
+        if (w & INHIBIT_HANDLE_SUSPEND_KEY)
+                p = stpcpy(p, "handle-suspend-key:");
+        if (w & INHIBIT_HANDLE_HIBERNATE_KEY)
+                p = stpcpy(p, "handle-hibernate-key:");
         if (w & INHIBIT_HANDLE_LID_SWITCH)
                 p = stpcpy(p, "handle-lid-switch:");
 
@@ -428,21 +452,23 @@ const char *inhibit_what_to_string(InhibitWhat w) {
 
 InhibitWhat inhibit_what_from_string(const char *s) {
         InhibitWhat what = 0;
-        char *w, *state;
+        const char *word, *state;
         size_t l;
 
-        FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
-                if (l == 8 && strncmp(w, "shutdown", l) == 0)
+        FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
+                if (l == 8 && strneq(word, "shutdown", l))
                         what |= INHIBIT_SHUTDOWN;
-                else if (l == 5 && strncmp(w, "sleep", l) == 0)
+                else if (l == 5 && strneq(word, "sleep", l))
                         what |= INHIBIT_SLEEP;
-                else if (l == 4 && strncmp(w, "idle", l) == 0)
+                else if (l == 4 && strneq(word, "idle", l))
                         what |= INHIBIT_IDLE;
-                else if (l == 16 && strncmp(w, "handle-power-key", l) == 0)
+                else if (l == 16 && strneq(word, "handle-power-key", l))
                         what |= INHIBIT_HANDLE_POWER_KEY;
-                else if (l == 16 && strncmp(w, "handle-sleep-key", l) == 0)
-                        what |= INHIBIT_HANDLE_SLEEP_KEY;
-                else if (l == 17 && strncmp(w, "handle-lid-switch", l) == 0)
+                else if (l == 18 && strneq(word, "handle-suspend-key", l))
+                        what |= INHIBIT_HANDLE_SUSPEND_KEY;
+                else if (l == 20 && strneq(word, "handle-hibernate-key", l))
+                        what |= INHIBIT_HANDLE_HIBERNATE_KEY;
+                else if (l == 17 && strneq(word, "handle-lid-switch", l))
                         what |= INHIBIT_HANDLE_LID_SWITCH;
                 else
                         return _INHIBIT_WHAT_INVALID;