chiark / gitweb /
logind: avoid creating stale session state files
[elogind.git] / src / login / logind-session.c
index 16d4955d5d94bc1f69151140ad689026149ec12b..e2f41d3e575e81d988d1cb216cbb814edaf1a6a6 100644 (file)
 #include <sys/epoll.h>
 #include <fcntl.h>
 
+#include "systemd/sd-id128.h"
+#include "systemd/sd-messages.h"
 #include "strv.h"
 #include "util.h"
 #include "mkdir.h"
 #include "path-util.h"
 #include "cgroup-util.h"
 #include "logind-session.h"
-
-#define IDLE_THRESHOLD_USEC (5*USEC_PER_MINUTE)
+#include "fileio.h"
 
 Session* session_new(Manager *m, User *u, const char *id) {
         Session *s;
@@ -524,7 +525,9 @@ static int session_create_cgroup(Session *s) {
                 }
         }
 
-        hashmap_put(s->manager->session_cgroups, s->cgroup_path, s);
+        r = hashmap_put(s->manager->session_cgroups, s->cgroup_path, s);
+        if (r < 0)
+                log_warning("Failed to create mapping between cgroup and session");
 
         return 0;
 }
@@ -542,8 +545,13 @@ int session_start(Session *s) {
         if (r < 0)
                 return r;
 
-        log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
-                 "New session %s of user %s.", s->id, s->user->name);
+        log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
+                   MESSAGE_ID(SD_MESSAGE_SESSION_START),
+                   "SESSION_ID=%s", s->id,
+                   "USER_ID=%s", s->user->name,
+                   "LEADER=%lu", (unsigned long) s->leader,
+                   "MESSAGE=New session %s of user %s.", s->id, s->user->name,
+                   NULL);
 
         /* Create cgroup */
         r = session_create_cgroup(s);
@@ -679,8 +687,13 @@ int session_stop(Session *s) {
         assert(s);
 
         if (s->started)
-                log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
-                         "Removed session %s.", s->id);
+                log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
+                           MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
+                           "SESSION_ID=%s", s->id,
+                           "USER_ID=%s", s->user->name,
+                           "LEADER=%lu", (unsigned long) s->leader,
+                           "MESSAGE=Removed session %s.", s->id,
+                           NULL);
 
         /* Kill cgroup */
         k = session_terminate_cgroup(s);
@@ -697,16 +710,18 @@ int session_stop(Session *s) {
         if (s->started)
                 session_send_signal(s, false);
 
+        s->started = false;
+
         if (s->seat) {
                 if (s->seat->active == s)
                         seat_set_active(s->seat, NULL);
 
                 seat_send_changed(s->seat, "Sessions\0");
+                seat_save(s->seat);
         }
 
         user_send_changed(s->user, "Sessions\0");
-
-        s->started = false;
+        user_save(s->user);
 
         return r;
 }
@@ -720,15 +735,50 @@ bool session_is_active(Session *s) {
         return s->seat->active == s;
 }
 
-int session_get_idle_hint(Session *s, dual_timestamp *t) {
-        char *p;
+static int get_tty_atime(const char *tty, usec_t *atime) {
+        _cleanup_free_ char *p = NULL;
         struct stat st;
-        usec_t u, n;
-        bool b;
-        int k;
+
+        assert(tty);
+        assert(atime);
+
+        if (!path_is_absolute(tty)) {
+                p = strappend("/dev/", tty);
+                if (!p)
+                        return -ENOMEM;
+
+                tty = p;
+        } else if (!path_startswith(tty, "/dev/"))
+                return -ENOENT;
+
+        if (lstat(tty, &st) < 0)
+                return -errno;
+
+        *atime = timespec_load(&st.st_atim);
+        return 0;
+}
+
+static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        assert(pid > 0);
+        assert(atime);
+
+        r = get_ctty(pid, NULL, &p);
+        if (r < 0)
+                return r;
+
+        return get_tty_atime(p, atime);
+}
+
+int session_get_idle_hint(Session *s, dual_timestamp *t) {
+        usec_t atime = 0, n;
+        int r;
 
         assert(s);
 
+        /* Explicit idle hint is set */
         if (s->idle_hint) {
                 if (t)
                         *t = s->idle_hint_timestamp;
@@ -736,41 +786,65 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
                 return s->idle_hint;
         }
 
-        if (isempty(s->tty))
+        /* Graphical sessions should really implement a real
+         * idle hint logic */
+        if (s->display)
                 goto dont_know;
 
-        if (s->tty[0] != '/') {
-                p = strappend("/dev/", s->tty);
-                if (!p)
-                        return -ENOMEM;
-        } else
-                p = NULL;
+        /* For sessions with an explicitly configured tty, let's check
+         * its atime */
+        if (s->tty) {
+                r = get_tty_atime(s->tty, &atime);
+                if (r >= 0)
+                        goto found_atime;
+        }
 
-        if (!startswith(p ? p : s->tty, "/dev/")) {
-                free(p);
-                goto dont_know;
+        /* For sessions with a leader but no explicitly configured
+         * tty, let's check the controlling tty of the leader */
+        if (s->leader > 0) {
+                r = get_process_ctty_atime(s->leader, &atime);
+                if (r >= 0)
+                        goto found_atime;
         }
 
-        k = lstat(p ? p : s->tty, &st);
-        free(p);
+        /* For other TTY sessions, let's find the most recent atime of
+         * the ttys of any of the processes of the session */
+        if (s->cgroup_path) {
+                _cleanup_fclose_ FILE *f = NULL;
 
-        if (k < 0)
-                goto dont_know;
+                if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &f) >= 0) {
+                        pid_t pid;
 
-        u = timespec_load(&st.st_atim);
-        n = now(CLOCK_REALTIME);
-        b = u + IDLE_THRESHOLD_USEC < n;
+                        atime = 0;
+                        while (cg_read_pid(f, &pid) > 0) {
+                                usec_t a;
 
-        if (t)
-                dual_timestamp_from_realtime(t, u + b*IDLE_THRESHOLD_USEC);
+                                if (get_process_ctty_atime(pid, &a) >= 0)
+                                        if (atime == 0 || atime < a)
+                                                atime = a;
+                        }
 
-        return b;
+                        if (atime != 0)
+                                goto found_atime;
+                }
+        }
 
 dont_know:
         if (t)
                 *t = s->idle_hint_timestamp;
 
         return 0;
+
+found_atime:
+        if (t)
+                dual_timestamp_from_realtime(t, atime);
+
+        n = now(CLOCK_REALTIME);
+
+        if (s->manager->idle_action_usec <= 0)
+                return 0;
+
+        return atime + s->manager->idle_action_usec <= n;
 }
 
 void session_set_idle_hint(Session *s, bool b) {
@@ -824,7 +898,7 @@ int session_create_fifo(Session *s) {
 
         /* Open reading side */
         if (s->fifo_fd < 0) {
-                struct epoll_event ev;
+                struct epoll_event ev = {};
 
                 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
                 if (s->fifo_fd < 0)
@@ -834,7 +908,6 @@ int session_create_fifo(Session *s) {
                 if (r < 0)
                         return r;
 
-                zero(ev);
                 ev.events = 0;
                 ev.data.u32 = FD_OTHER_BASE + s->fifo_fd;
 
@@ -858,6 +931,9 @@ void session_remove_fifo(Session *s) {
                 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
                 close_nointr_nofail(s->fifo_fd);
                 s->fifo_fd = -1;
+
+                session_save(s);
+                user_save(s->user);
         }
 
         if (s->fifo_path) {
@@ -980,7 +1056,8 @@ DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
         [SESSION_USER] = "user",
         [SESSION_GREETER] = "greeter",
-        [SESSION_LOCK_SCREEN] = "lock-screen"
+        [SESSION_LOCK_SCREEN] = "lock-screen",
+        [SESSION_BACKGROUND] = "background"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);