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"
29 #include "alloc-util.h"
32 #include "formats-util.h"
33 #include "logind-acl.h"
34 #include "logind-seat.h"
36 #include "parse-util.h"
37 #include "string-util.h"
38 #include "terminal-util.h"
41 Seat *seat_new(Manager *m, const char *id) {
51 s->state_file = strappend("/run/systemd/seats/", id);
57 s->id = basename(s->state_file);
60 if (hashmap_put(m->seats, s->id, s) < 0) {
69 void seat_free(Seat *s) {
73 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
76 session_free(s->sessions);
81 device_free(s->devices);
83 hashmap_remove(s->manager->seats, s->id);
90 int seat_save(Seat *s) {
91 _cleanup_free_ char *temp_path = NULL;
92 _cleanup_fclose_ FILE *f = NULL;
100 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
104 r = fopen_temporary(s->state_file, &f, &temp_path);
108 fchmod(fileno(f), 0644);
111 "# This is private data. Do not parse.\n"
113 "CAN_MULTI_SESSION=%i\n"
115 "CAN_GRAPHICAL=%i\n",
117 seat_can_multi_session(s),
119 seat_can_graphical(s));
122 assert(s->active->user);
126 "ACTIVE_UID="UID_FMT"\n",
128 s->active->user->uid);
134 fputs("SESSIONS=", f);
135 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
139 i->sessions_by_seat_next ? ' ' : '\n');
143 LIST_FOREACH(sessions_by_seat, i, s->sessions)
147 i->sessions_by_seat_next ? ' ' : '\n');
150 r = fflush_and_check(f);
154 if (rename(temp_path, s->state_file) < 0) {
162 (void) unlink(s->state_file);
165 (void) unlink(temp_path);
167 return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
170 int seat_load(Seat *s) {
173 /* There isn't actually anything to read here ... */
178 #if 0 /// UNNEEDED by elogind
179 static int vt_allocate(unsigned int vtnr) {
180 char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
181 _cleanup_close_ int fd = -1;
185 snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
186 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
193 int seat_preallocate_vts(Seat *s) {
200 log_debug("Preallocating VTs...");
202 if (s->manager->n_autovts <= 0)
205 if (!seat_has_vts(s))
208 for (i = 1; i <= s->manager->n_autovts; i++) {
213 log_error_errno(q, "Failed to preallocate VT %u: %m", i);
222 int seat_apply_acls(Seat *s, Session *old_active) {
227 r = devnode_acl_all(s->manager->udev,
230 !!old_active, old_active ? old_active->user->uid : 0,
231 !!s->active, s->active ? s->active->user->uid : 0);
234 log_error_errno(r, "Failed to apply ACLs: %m");
239 int seat_set_active(Seat *s, Session *session) {
243 assert(!session || session->seat == s);
245 if (session == s->active)
248 old_active = s->active;
252 session_device_pause_all(old_active);
253 session_send_changed(old_active, "Active", NULL);
256 seat_apply_acls(s, old_active);
258 if (session && session->started) {
259 session_send_changed(session, "Active", NULL);
260 session_device_resume_all(session);
263 if (!session || session->started)
264 seat_send_changed(s, "ActiveSession", NULL);
269 session_save(session);
270 user_save(session->user);
274 session_save(old_active);
275 if (!session || session->user != old_active->user)
276 user_save(old_active->user);
282 int seat_switch_to(Seat *s, unsigned int num) {
283 /* Public session positions skip 0 (there is only F1-F12). Maybe it
284 * will get reassigned in the future, so return error for now. */
288 if (num >= s->position_count || !s->positions[num]) {
289 /* allow switching to unused VTs to trigger auto-activate */
290 if (seat_has_vts(s) && num < 64)
296 return session_activate(s->positions[num]);
299 int seat_switch_to_next(Seat *s) {
300 unsigned int start, i;
302 if (s->position_count == 0)
306 if (s->active && s->active->position > 0)
307 start = s->active->position;
309 for (i = start + 1; i < s->position_count; ++i)
311 return session_activate(s->positions[i]);
313 for (i = 1; i < start; ++i)
315 return session_activate(s->positions[i]);
320 int seat_switch_to_previous(Seat *s) {
321 unsigned int start, i;
323 if (s->position_count == 0)
327 if (s->active && s->active->position > 0)
328 start = s->active->position;
330 for (i = start - 1; i > 0; --i)
332 return session_activate(s->positions[i]);
334 for (i = s->position_count - 1; i > start; --i)
336 return session_activate(s->positions[i]);
341 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
342 Session *i, *new_active = NULL;
348 if (!seat_has_vts(s))
351 log_debug("VT changed to %u", vtnr);
353 /* we might have earlier closing sessions on the same VT, so try to
354 * find a running one first */
355 LIST_FOREACH(sessions_by_seat, i, s->sessions)
356 if (i->vtnr == vtnr && !i->stopping) {
362 /* no running one? then we can't decide which one is the
363 * active one, let the first one win */
364 LIST_FOREACH(sessions_by_seat, i, s->sessions)
365 if (i->vtnr == vtnr) {
371 r = seat_set_active(s, new_active);
373 #if 0 /// elogind does not spawn autovt
374 manager_spawn_autovt(s->manager, vtnr);
380 int seat_read_active_vt(Seat *s) {
388 if (!seat_has_vts(s))
391 lseek(s->manager->console_active_fd, SEEK_SET, 0);
393 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
395 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
396 return k < 0 ? -errno : -EIO;
402 if (!startswith(t, "tty")) {
403 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
407 r = safe_atou(t+3, &vtnr);
409 log_error("Failed to parse VT number %s", t+3);
414 log_error("VT number invalid: %s", t+3);
418 return seat_active_vt_changed(s, vtnr);
421 int seat_start(Seat *s) {
428 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
430 LOG_MESSAGE("New seat %s.", s->id),
433 /* Initialize VT magic stuff */
434 #if 0 /// elogind does not support autospawning vts
435 seat_preallocate_vts(s);
438 /* Read current VT */
439 seat_read_active_vt(s);
446 seat_send_signal(s, true);
451 int seat_stop(Seat *s, bool force) {
458 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
460 LOG_MESSAGE("Removed seat %s.", s->id),
463 seat_stop_sessions(s, force);
465 unlink(s->state_file);
466 seat_add_to_gc_queue(s);
469 seat_send_signal(s, false);
476 int seat_stop_sessions(Seat *s, bool force) {
482 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
483 k = session_stop(session, force);
491 void seat_evict_position(Seat *s, Session *session) {
493 unsigned int pos = session->position;
495 session->position = 0;
500 if (pos < s->position_count && s->positions[pos] == session) {
501 s->positions[pos] = NULL;
503 /* There might be another session claiming the same
504 * position (eg., during gdm->session transition), so let's look
505 * for it and set it on the free slot. */
506 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
507 if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) {
508 s->positions[pos] = iter;
515 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
516 /* with VTs, the position is always the same as the VTnr */
520 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
523 seat_evict_position(s, session);
525 session->position = pos;
527 s->positions[pos] = session;
530 static void seat_assign_position(Seat *s, Session *session) {
533 if (session->position > 0)
536 for (pos = 1; pos < s->position_count; ++pos)
537 if (!s->positions[pos])
540 seat_claim_position(s, session, pos);
543 int seat_attach_session(Seat *s, Session *session) {
546 assert(!session->seat);
548 if (!seat_has_vts(s) != !session->vtnr)
552 LIST_PREPEND(sessions_by_seat, s->sessions, session);
553 seat_assign_position(s, session);
555 seat_send_changed(s, "Sessions", NULL);
557 /* On seats with VTs, the VT logic defines which session is active. On
558 * seats without VTs, we automatically activate new sessions. */
559 if (!seat_has_vts(s))
560 seat_set_active(s, session);
565 void seat_complete_switch(Seat *s) {
570 /* if no session-switch is pending or if it got canceled, do nothing */
571 if (!s->pending_switch)
574 session = s->pending_switch;
575 s->pending_switch = NULL;
577 seat_set_active(s, session);
580 bool seat_has_vts(Seat *s) {
583 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
586 bool seat_is_seat0(Seat *s) {
589 return s->manager->seat0 == s;
592 bool seat_can_multi_session(Seat *s) {
595 return seat_has_vts(s);
598 bool seat_can_tty(Seat *s) {
601 return seat_has_vts(s);
604 bool seat_has_master_device(Seat *s) {
607 /* device list is ordered by "master" flag */
608 return !!s->devices && s->devices->master;
611 bool seat_can_graphical(Seat *s) {
614 return seat_has_master_device(s);
617 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
619 bool idle_hint = true;
620 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
624 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
628 ih = session_get_idle_hint(session, &k);
634 if (k.monotonic > ts.monotonic)
640 } else if (idle_hint) {
642 if (k.monotonic > ts.monotonic)
653 bool seat_check_gc(Seat *s, bool drop_not_started) {
656 if (drop_not_started && !s->started)
659 if (seat_is_seat0(s))
662 return seat_has_master_device(s);
665 void seat_add_to_gc_queue(Seat *s) {
671 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
672 s->in_gc_queue = true;
675 static bool seat_name_valid_char(char c) {
677 (c >= 'a' && c <= 'z') ||
678 (c >= 'A' && c <= 'Z') ||
679 (c >= '0' && c <= '9') ||
684 bool seat_name_is_valid(const char *name) {
689 if (!startswith(name, "seat"))
695 for (p = name; *p; p++)
696 if (!seat_name_valid_char(*p))
699 if (strlen(name) > 255)