chiark / gitweb /
unit: introduce ConditionFileIsExecutable= and use it where we check for a binary...
[elogind.git] / src / logind-seat.c
index 743ded66bd4bb2a841417b0d86fd84e20e3f413e..3cf3958c8d47330d13cac925573238713a54ef55 100644 (file)
@@ -41,28 +41,30 @@ Seat *seat_new(Manager *m, const char *id) {
         if (!s)
                 return NULL;
 
-        s->state_file = strappend("/run/systemd/seat/", id);
+        s->state_file = strappend("/run/systemd/seats/", id);
         if (!s->state_file) {
                 free(s);
                 return NULL;
         }
 
         s->id = file_name_from_path(s->state_file);
+        s->manager = m;
 
         if (hashmap_put(m->seats, s->id, s) < 0) {
-                free(s->id);
+                free(s->state_file);
                 free(s);
                 return NULL;
         }
 
-        s->manager = m;
-
         return s;
 }
 
 void seat_free(Seat *s) {
         assert(s);
 
+        if (s->in_gc_queue)
+                LIST_REMOVE(Seat, gc_queue, s->manager->seat_gc_queue, s);
+
         while (s->sessions)
                 session_free(s->sessions);
 
@@ -78,22 +80,29 @@ void seat_free(Seat *s) {
 }
 
 int seat_save(Seat *s) {
-        FILE *f;
         int r;
+        FILE *f;
+        char *temp_path;
 
         assert(s);
 
-        r = safe_mkdir("/run/systemd/seat", 0755, 0, 0);
+        if (!s->started)
+                return 0;
+
+        r = safe_mkdir("/run/systemd/seats", 0755, 0, 0);
         if (r < 0)
-                return r;
+                goto finish;
 
-        f = fopen(s->state_file, "we");
-        if (!f)
-                return -errno;
+        r = fopen_temporary(s->state_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
+
+        fchmod(fileno(f), 0644);
 
         fprintf(f,
+                "# This is private data. Do not parse.\n"
                 "IS_VTCONSOLE=%i\n",
-                s->manager->vtconsole == s);
+                seat_is_vtconsole(s));
 
         if (s->active) {
                 assert(s->active->user);
@@ -107,34 +116,46 @@ int seat_save(Seat *s) {
 
         if (s->sessions) {
                 Session *i;
-                fputs("OTHER_UIDS=", f);
 
+                fputs("SESSIONS=", f);
                 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
-                        assert(i->user);
-
-                        if (i == s->active)
-                                continue;
-
                         fprintf(f,
-                                "%s%lu",
-                                i == s->sessions ? "" : " ",
-                                (unsigned long) i->user->uid);
+                                "%s%c",
+                                i->id,
+                                i->sessions_by_seat_next ? ' ' : '\n');
                 }
+
+                fputs("UIDS=", f);
+                LIST_FOREACH(sessions_by_seat, i, s->sessions)
+                        fprintf(f,
+                                "%lu%c",
+                                (unsigned long) i->user->uid,
+                                i->sessions_by_seat_next ? ' ' : '\n');
         }
 
         fflush(f);
-        if (ferror(f)) {
+
+        if (ferror(f) || rename(temp_path, s->state_file) < 0) {
                 r = -errno;
                 unlink(s->state_file);
+                unlink(temp_path);
         }
 
         fclose(f);
+        free(temp_path);
+
+finish:
+        if (r < 0)
+                log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
+
         return r;
 }
 
 int seat_load(Seat *s) {
         assert(s);
 
+        /* There isn't actually anything to read here ... */
+
         return 0;
 }
 
@@ -159,23 +180,28 @@ static int vt_allocate(int vtnr) {
 }
 
 int seat_preallocate_vts(Seat *s) {
-        int i, r = 0;
+        int r = 0;
+        unsigned i;
 
         assert(s);
         assert(s->manager);
 
+        log_debug("Preallocating VTs...");
+
         if (s->manager->n_autovts <= 0)
                 return 0;
 
-        if (s->manager->vtconsole != s)
+        if (!seat_is_vtconsole(s))
                 return 0;
 
-        for (i = 1; i < s->manager->n_autovts; i++) {
+        for (i = 1; i <= s->manager->n_autovts; i++) {
                 int q;
 
                 q = vt_allocate(i);
-                if (r >= 0 && q < 0)
+                if (q < 0) {
+                        log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
                         r = q;
+                }
         }
 
         return r;
@@ -198,41 +224,157 @@ int seat_apply_acls(Seat *s, Session *old_active) {
         return r;
 }
 
-int seat_active_vt_changed(Seat *s, int vtnr) {
-        Session *i;
+int seat_set_active(Seat *s, Session *session) {
         Session *old_active;
 
         assert(s);
-        assert(vtnr >= 1);
-        assert(s->manager->vtconsole == s);
+        assert(!session || session->seat == s);
+
+        if (session == s->active)
+                return 0;
 
         old_active = s->active;
-        s->active = NULL;
+        s->active = session;
+
+        seat_apply_acls(s, old_active);
+
+        if (session && session->started)
+                session_send_changed(session, "Active\0");
+
+        if (!session || session->started)
+                seat_send_changed(s, "ActiveSession\0");
+
+        seat_save(s);
+
+        if (session) {
+                session_save(session);
+                user_save(session->user);
+        }
+
+        if (old_active) {
+                session_save(old_active);
+                user_save(old_active->user);
+        }
+
+        return 0;
+}
+
+int seat_active_vt_changed(Seat *s, int vtnr) {
+        Session *i, *new_active = NULL;
+        int r;
+
+        assert(s);
+        assert(vtnr >= 1);
+
+        if (!seat_is_vtconsole(s))
+                return -EINVAL;
+
+        log_debug("VT changed to %i", vtnr);
 
         LIST_FOREACH(sessions_by_seat, i, s->sessions)
                 if (i->vtnr == vtnr) {
-                        s->active = i;
+                        new_active = i;
                         break;
                 }
 
-        if (old_active == s->active)
+        r = seat_set_active(s, new_active);
+        manager_spawn_autovt(s->manager, vtnr);
+
+        return r;
+}
+
+int seat_read_active_vt(Seat *s) {
+        char t[64];
+        ssize_t k;
+        int r, vtnr;
+
+        assert(s);
+
+        if (!seat_is_vtconsole(s))
                 return 0;
 
-        seat_apply_acls(s, old_active);
-        manager_spawn_autovt(s->manager, vtnr);
+        lseek(s->manager->console_active_fd, SEEK_SET, 0);
+
+        k = read(s->manager->console_active_fd, t, sizeof(t)-1);
+        if (k <= 0) {
+                log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
+                return k < 0 ? -errno : -EIO;
+        }
+
+        t[k] = 0;
+        truncate_nl(t);
+
+        if (!startswith(t, "tty")) {
+                log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
+                return -EIO;
+        }
+
+        r = safe_atoi(t+3, &vtnr);
+        if (r < 0) {
+                log_error("Failed to parse VT number %s", t+3);
+                return r;
+        }
+
+        if (vtnr <= 0) {
+                log_error("VT number invalid: %s", t+3);
+                return -EIO;
+        }
+
+        return seat_active_vt_changed(s, vtnr);
+}
+
+int seat_start(Seat *s) {
+        assert(s);
+
+        if (s->started)
+                return 0;
+
+        log_info("New seat %s.", s->id);
+
+        /* Initialize VT magic stuff */
+        seat_preallocate_vts(s);
+
+        /* Read current VT */
+        seat_read_active_vt(s);
+
+        s->started = true;
+
+        /* Save seat data */
+        seat_save(s);
+
+        seat_send_signal(s, true);
 
         return 0;
 }
 
 int seat_stop(Seat *s) {
-        Session *session;
         int r = 0;
 
         assert(s);
 
-        LIST_FOREACH(sessions_by_seat, session, s->sessions) {
-                int k;
+        if (s->started)
+                log_info("Removed seat %s.", s->id);
+
+        seat_stop_sessions(s);
+
+        unlink(s->state_file);
+        seat_add_to_gc_queue(s);
+
+        if (s->started)
+                seat_send_signal(s, false);
+
+        s->started = false;
+
+        return r;
+}
 
+int seat_stop_sessions(Seat *s) {
+        Session *session;
+        int r = 0, k;
+
+        assert(s);
+
+        LIST_FOREACH(sessions_by_seat, session, s->sessions) {
                 k = session_stop(session);
                 if (k < 0)
                         r = k;
@@ -240,3 +382,118 @@ int seat_stop(Seat *s) {
 
         return r;
 }
+
+int seat_attach_session(Seat *s, Session *session) {
+        assert(s);
+        assert(session);
+        assert(!session->seat);
+
+        if (!seat_is_vtconsole(s) && s->sessions)
+                return -EEXIST;
+
+        session->seat = s;
+        LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
+
+        seat_send_changed(s, "Sessions\0");
+
+        if (!seat_is_vtconsole(s)) {
+                assert(!s->active);
+                seat_set_active(s, session);
+        }
+
+        return 0;
+}
+
+bool seat_is_vtconsole(Seat *s) {
+        assert(s);
+
+        return s->manager->vtconsole == s;
+}
+
+int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
+        Session *session;
+        bool idle_hint = true;
+        dual_timestamp ts = { 0, 0 };
+
+        assert(s);
+
+        LIST_FOREACH(sessions_by_seat, session, s->sessions) {
+                dual_timestamp k;
+                int ih;
+
+                ih = session_get_idle_hint(session, &k);
+                if (ih < 0)
+                        return ih;
+
+                if (!ih) {
+                        if (!idle_hint) {
+                                if (k.monotonic < ts.monotonic)
+                                        ts = k;
+                        } else {
+                                idle_hint = false;
+                                ts = k;
+                        }
+                } else if (idle_hint) {
+
+                        if (k.monotonic > ts.monotonic)
+                                ts = k;
+                }
+        }
+
+        if (t)
+                *t = ts;
+
+        return idle_hint;
+}
+
+int seat_check_gc(Seat *s, bool drop_not_started) {
+        assert(s);
+
+        if (drop_not_started && !s->started)
+                return 0;
+
+        if (seat_is_vtconsole(s))
+                return 1;
+
+        return !!s->devices;
+}
+
+void seat_add_to_gc_queue(Seat *s) {
+        assert(s);
+
+        if (s->in_gc_queue)
+                return;
+
+        LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
+        s->in_gc_queue = true;
+}
+
+static bool seat_name_valid_char(char c) {
+        return
+                (c >= 'a' && c <= 'z') ||
+                (c >= 'A' && c <= 'Z') ||
+                (c >= '0' && c <= '9') ||
+                c == '-' ||
+                c == '_';
+}
+
+bool seat_name_is_valid(const char *name) {
+        const char *p;
+
+        assert(name);
+
+        if (!startswith(name, "seat"))
+                return false;
+
+        if (!name[4])
+                return false;
+
+        for (p = name; *p; p++)
+                if (!seat_name_valid_char(*p))
+                        return false;
+
+        if (strlen(name) > 255)
+                return false;
+
+        return true;
+}