1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "sd-messages.h"
28 #include "logind-seat.h"
29 #include "logind-acl.h"
32 #include "formats-util.h"
33 #include "terminal-util.h"
35 Seat *seat_new(Manager *m, const char *id) {
45 s->state_file = strappend("/run/systemd/seats/", id);
51 s->id = basename(s->state_file);
54 if (hashmap_put(m->seats, s->id, s) < 0) {
63 void seat_free(Seat *s) {
67 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
70 session_free(s->sessions);
75 device_free(s->devices);
77 hashmap_remove(s->manager->seats, s->id);
84 int seat_save(Seat *s) {
85 _cleanup_free_ char *temp_path = NULL;
86 _cleanup_fclose_ FILE *f = NULL;
94 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
98 r = fopen_temporary(s->state_file, &f, &temp_path);
102 fchmod(fileno(f), 0644);
105 "# This is private data. Do not parse.\n"
107 "CAN_MULTI_SESSION=%i\n"
109 "CAN_GRAPHICAL=%i\n",
111 seat_can_multi_session(s),
113 seat_can_graphical(s));
116 assert(s->active->user);
120 "ACTIVE_UID="UID_FMT"\n",
122 s->active->user->uid);
128 fputs("SESSIONS=", f);
129 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
133 i->sessions_by_seat_next ? ' ' : '\n');
137 LIST_FOREACH(sessions_by_seat, i, s->sessions)
141 i->sessions_by_seat_next ? ' ' : '\n');
144 r = fflush_and_check(f);
148 if (rename(temp_path, s->state_file) < 0) {
156 (void) unlink(s->state_file);
159 (void) unlink(temp_path);
161 return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
164 int seat_load(Seat *s) {
167 /* There isn't actually anything to read here ... */
172 /// UNNEEDED by elogind
174 static int vt_allocate(unsigned int vtnr) {
175 char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
176 _cleanup_close_ int fd = -1;
180 snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
181 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
188 int seat_preallocate_vts(Seat *s) {
195 log_debug("Preallocating VTs...");
197 if (s->manager->n_autovts <= 0)
200 if (!seat_has_vts(s))
203 for (i = 1; i <= s->manager->n_autovts; i++) {
208 log_error_errno(q, "Failed to preallocate VT %u: %m", i);
217 int seat_apply_acls(Seat *s, Session *old_active) {
222 r = devnode_acl_all(s->manager->udev,
225 !!old_active, old_active ? old_active->user->uid : 0,
226 !!s->active, s->active ? s->active->user->uid : 0);
229 log_error_errno(r, "Failed to apply ACLs: %m");
234 int seat_set_active(Seat *s, Session *session) {
238 assert(!session || session->seat == s);
240 if (session == s->active)
243 old_active = s->active;
247 session_device_pause_all(old_active);
248 session_send_changed(old_active, "Active", NULL);
251 seat_apply_acls(s, old_active);
253 if (session && session->started) {
254 session_send_changed(session, "Active", NULL);
255 session_device_resume_all(session);
258 if (!session || session->started)
259 seat_send_changed(s, "ActiveSession", NULL);
264 session_save(session);
265 user_save(session->user);
269 session_save(old_active);
270 if (!session || session->user != old_active->user)
271 user_save(old_active->user);
277 int seat_switch_to(Seat *s, unsigned int num) {
278 /* Public session positions skip 0 (there is only F1-F12). Maybe it
279 * will get reassigned in the future, so return error for now. */
283 if (num >= s->position_count || !s->positions[num]) {
284 /* allow switching to unused VTs to trigger auto-activate */
285 if (seat_has_vts(s) && num < 64)
291 return session_activate(s->positions[num]);
294 int seat_switch_to_next(Seat *s) {
295 unsigned int start, i;
297 if (s->position_count == 0)
301 if (s->active && s->active->position > 0)
302 start = s->active->position;
304 for (i = start + 1; i < s->position_count; ++i)
306 return session_activate(s->positions[i]);
308 for (i = 1; i < start; ++i)
310 return session_activate(s->positions[i]);
315 int seat_switch_to_previous(Seat *s) {
316 unsigned int start, i;
318 if (s->position_count == 0)
322 if (s->active && s->active->position > 0)
323 start = s->active->position;
325 for (i = start - 1; i > 0; --i)
327 return session_activate(s->positions[i]);
329 for (i = s->position_count - 1; i > start; --i)
331 return session_activate(s->positions[i]);
336 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
337 Session *i, *new_active = NULL;
343 if (!seat_has_vts(s))
346 log_debug("VT changed to %u", vtnr);
348 /* we might have earlier closing sessions on the same VT, so try to
349 * find a running one first */
350 LIST_FOREACH(sessions_by_seat, i, s->sessions)
351 if (i->vtnr == vtnr && !i->stopping) {
357 /* no running one? then we can't decide which one is the
358 * active one, let the first one win */
359 LIST_FOREACH(sessions_by_seat, i, s->sessions)
360 if (i->vtnr == vtnr) {
366 r = seat_set_active(s, new_active);
368 /// elogind does not spawn autovt
370 manager_spawn_autovt(s->manager, vtnr);
376 int seat_read_active_vt(Seat *s) {
384 if (!seat_has_vts(s))
387 lseek(s->manager->console_active_fd, SEEK_SET, 0);
389 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
391 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
392 return k < 0 ? -errno : -EIO;
398 if (!startswith(t, "tty")) {
399 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
403 r = safe_atou(t+3, &vtnr);
405 log_error("Failed to parse VT number %s", t+3);
410 log_error("VT number invalid: %s", t+3);
414 return seat_active_vt_changed(s, vtnr);
417 int seat_start(Seat *s) {
424 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
426 LOG_MESSAGE("New seat %s.", s->id),
429 /* Initialize VT magic stuff */
430 /// elogind does not support autospawning vts
432 seat_preallocate_vts(s);
435 /* Read current VT */
436 seat_read_active_vt(s);
443 seat_send_signal(s, true);
448 int seat_stop(Seat *s, bool force) {
455 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
457 LOG_MESSAGE("Removed seat %s.", s->id),
460 seat_stop_sessions(s, force);
462 unlink(s->state_file);
463 seat_add_to_gc_queue(s);
466 seat_send_signal(s, false);
473 int seat_stop_sessions(Seat *s, bool force) {
479 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
480 k = session_stop(session, force);
488 void seat_evict_position(Seat *s, Session *session) {
490 unsigned int pos = session->position;
492 session->position = 0;
497 if (pos < s->position_count && s->positions[pos] == session) {
498 s->positions[pos] = NULL;
500 /* There might be another session claiming the same
501 * position (eg., during gdm->session transition), so let's look
502 * for it and set it on the free slot. */
503 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
504 if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) {
505 s->positions[pos] = iter;
512 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
513 /* with VTs, the position is always the same as the VTnr */
517 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
520 seat_evict_position(s, session);
522 session->position = pos;
524 s->positions[pos] = session;
527 static void seat_assign_position(Seat *s, Session *session) {
530 if (session->position > 0)
533 for (pos = 1; pos < s->position_count; ++pos)
534 if (!s->positions[pos])
537 seat_claim_position(s, session, pos);
540 int seat_attach_session(Seat *s, Session *session) {
543 assert(!session->seat);
545 if (!seat_has_vts(s) != !session->vtnr)
549 LIST_PREPEND(sessions_by_seat, s->sessions, session);
550 seat_assign_position(s, session);
552 seat_send_changed(s, "Sessions", NULL);
554 /* On seats with VTs, the VT logic defines which session is active. On
555 * seats without VTs, we automatically activate new sessions. */
556 if (!seat_has_vts(s))
557 seat_set_active(s, session);
562 void seat_complete_switch(Seat *s) {
567 /* if no session-switch is pending or if it got canceled, do nothing */
568 if (!s->pending_switch)
571 session = s->pending_switch;
572 s->pending_switch = NULL;
574 seat_set_active(s, session);
577 bool seat_has_vts(Seat *s) {
580 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
583 bool seat_is_seat0(Seat *s) {
586 return s->manager->seat0 == s;
589 bool seat_can_multi_session(Seat *s) {
592 return seat_has_vts(s);
595 bool seat_can_tty(Seat *s) {
598 return seat_has_vts(s);
601 bool seat_has_master_device(Seat *s) {
604 /* device list is ordered by "master" flag */
605 return !!s->devices && s->devices->master;
608 bool seat_can_graphical(Seat *s) {
611 return seat_has_master_device(s);
614 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
616 bool idle_hint = true;
617 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
621 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
625 ih = session_get_idle_hint(session, &k);
631 if (k.monotonic > ts.monotonic)
637 } else if (idle_hint) {
639 if (k.monotonic > ts.monotonic)
650 bool seat_check_gc(Seat *s, bool drop_not_started) {
653 if (drop_not_started && !s->started)
656 if (seat_is_seat0(s))
659 return seat_has_master_device(s);
662 void seat_add_to_gc_queue(Seat *s) {
668 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
669 s->in_gc_queue = true;
672 static bool seat_name_valid_char(char c) {
674 (c >= 'a' && c <= 'z') ||
675 (c >= 'A' && c <= 'Z') ||
676 (c >= '0' && c <= '9') ||
681 bool seat_name_is_valid(const char *name) {
686 if (!startswith(name, "seat"))
692 for (p = name; *p; p++)
693 if (!seat_name_valid_char(*p))
696 if (strlen(name) > 255)