chiark / gitweb /
unit: introduce ConditionFileIsExecutable= and use it where we check for a binary...
[elogind.git] / src / logind-seat.c
index ae89ec9d8385440d3fd1efa698e5185e86affe2d..3cf3958c8d47330d13cac925573238713a54ef55 100644 (file)
@@ -41,7 +41,7 @@ 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;
@@ -86,7 +86,10 @@ int seat_save(Seat *s) {
 
         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)
                 goto finish;
 
@@ -99,7 +102,7 @@ int seat_save(Seat *s) {
         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);
@@ -114,27 +117,20 @@ int seat_save(Seat *s) {
         if (s->sessions) {
                 Session *i;
 
-                fputs("OTHER=", f);
+                fputs("SESSIONS=", f);
                 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
-                        if (i == s->active)
-                                continue;
-
                         fprintf(f,
                                 "%s%c",
                                 i->id,
                                 i->sessions_by_seat_next ? ' ' : '\n');
                 }
 
-                fputs("OTHER_UIDS=", f);
-                LIST_FOREACH(sessions_by_seat, i, s->sessions) {
-                        if (i == s->active)
-                                continue;
-
+                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);
@@ -158,6 +154,8 @@ finish:
 int seat_load(Seat *s) {
         assert(s);
 
+        /* There isn't actually anything to read here ... */
+
         return 0;
 }
 
@@ -181,19 +179,22 @@ static int vt_allocate(int vtnr) {
         return r;
 }
 
-static int seat_preallocate_vts(Seat *s) {
-        int i, r = 0;
+int seat_preallocate_vts(Seat *s) {
+        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);
@@ -223,13 +224,49 @@ int seat_apply_acls(Seat *s, Session *old_active) {
         return r;
 }
 
+int seat_set_active(Seat *s, Session *session) {
+        Session *old_active;
+
+        assert(s);
+        assert(!session || session->seat == s);
+
+        if (session == s->active)
+                return 0;
+
+        old_active = s->active;
+        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, *old_active;
+        Session *i, *new_active = NULL;
+        int r;
 
         assert(s);
         assert(vtnr >= 1);
 
-        if (s->manager->vtconsole != s)
+        if (!seat_is_vtconsole(s))
                 return -EINVAL;
 
         log_debug("VT changed to %i", vtnr);
@@ -240,16 +277,10 @@ int seat_active_vt_changed(Seat *s, int vtnr) {
                         break;
                 }
 
-        if (new_active == s->active)
-                return 0;
-
-        old_active = s->active;
-        s->active = new_active;
-
-        seat_apply_acls(s, old_active);
+        r = seat_set_active(s, new_active);
         manager_spawn_autovt(s->manager, vtnr);
 
-        return 0;
+        return r;
 }
 
 int seat_read_active_vt(Seat *s) {
@@ -259,7 +290,7 @@ int seat_read_active_vt(Seat *s) {
 
         assert(s);
 
-        if (s->manager->vtconsole != s)
+        if (!seat_is_vtconsole(s))
                 return 0;
 
         lseek(s->manager->console_active_fd, SEEK_SET, 0);
@@ -295,19 +326,49 @@ int seat_read_active_vt(Seat *s) {
 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) {
+        int r = 0;
+
+        assert(s);
+
+        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;
 
@@ -319,16 +380,79 @@ int seat_stop(Seat *s) {
                         r = k;
         }
 
-        unlink(s->state_file);
-        seat_add_to_gc_queue(s);
-
         return r;
 }
 
-int seat_check_gc(Seat *s) {
+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);
 
-        if (s->manager->vtconsole == 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;
@@ -343,3 +467,33 @@ void seat_add_to_gc_queue(Seat *s) {
         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;
+}