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"
34 Seat *seat_new(Manager *m, const char *id) {
44 s->state_file = strappend("/run/systemd/seats/", id);
50 s->id = basename(s->state_file);
53 if (hashmap_put(m->seats, s->id, s) < 0) {
62 void seat_free(Seat *s) {
66 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
69 session_free(s->sessions);
74 device_free(s->devices);
76 hashmap_remove(s->manager->seats, s->id);
83 int seat_save(Seat *s) {
84 _cleanup_free_ char *temp_path = NULL;
85 _cleanup_fclose_ FILE *f = NULL;
93 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
97 r = fopen_temporary(s->state_file, &f, &temp_path);
101 fchmod(fileno(f), 0644);
104 "# This is private data. Do not parse.\n"
106 "CAN_MULTI_SESSION=%i\n"
108 "CAN_GRAPHICAL=%i\n",
110 seat_can_multi_session(s),
112 seat_can_graphical(s));
115 assert(s->active->user);
119 "ACTIVE_UID="UID_FMT"\n",
121 s->active->user->uid);
127 fputs("SESSIONS=", f);
128 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
132 i->sessions_by_seat_next ? ' ' : '\n');
136 LIST_FOREACH(sessions_by_seat, i, s->sessions)
140 i->sessions_by_seat_next ? ' ' : '\n');
145 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
147 unlink(s->state_file);
153 log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
158 int seat_load(Seat *s) {
161 /* There isn't actually anything to read here ... */
166 static int vt_allocate(unsigned int vtnr) {
167 char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
168 _cleanup_close_ int fd = -1;
172 snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
173 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
180 int seat_apply_acls(Seat *s, Session *old_active) {
185 r = devnode_acl_all(s->manager->udev,
188 !!old_active, old_active ? old_active->user->uid : 0,
189 !!s->active, s->active ? s->active->user->uid : 0);
192 log_error_errno(r, "Failed to apply ACLs: %m");
197 int seat_set_active(Seat *s, Session *session) {
201 assert(!session || session->seat == s);
203 if (session == s->active)
206 old_active = s->active;
210 session_device_pause_all(old_active);
211 session_send_changed(old_active, "Active", NULL);
214 seat_apply_acls(s, old_active);
216 if (session && session->started) {
217 session_send_changed(session, "Active", NULL);
218 session_device_resume_all(session);
221 if (!session || session->started)
222 seat_send_changed(s, "ActiveSession", NULL);
227 session_save(session);
228 user_save(session->user);
232 session_save(old_active);
233 if (!session || session->user != old_active->user)
234 user_save(old_active->user);
240 int seat_switch_to(Seat *s, unsigned int num) {
241 /* Public session positions skip 0 (there is only F1-F12). Maybe it
242 * will get reassigned in the future, so return error for now. */
246 if (num >= s->position_count || !s->positions[num]) {
247 /* allow switching to unused VTs to trigger auto-activate */
248 if (seat_has_vts(s) && num < 64)
254 return session_activate(s->positions[num]);
257 int seat_switch_to_next(Seat *s) {
258 unsigned int start, i;
260 if (!s->position_count)
264 if (s->active && s->active->pos > 0)
265 start = s->active->pos;
267 for (i = start + 1; i < s->position_count; ++i)
269 return session_activate(s->positions[i]);
271 for (i = 1; i < start; ++i)
273 return session_activate(s->positions[i]);
278 int seat_switch_to_previous(Seat *s) {
279 unsigned int start, i;
281 if (!s->position_count)
285 if (s->active && s->active->pos > 0)
286 start = s->active->pos;
288 for (i = start - 1; i > 0; --i)
290 return session_activate(s->positions[i]);
292 for (i = s->position_count - 1; i > start; --i)
294 return session_activate(s->positions[i]);
299 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
300 Session *i, *new_active = NULL;
306 if (!seat_has_vts(s))
309 log_debug("VT changed to %u", vtnr);
311 /* we might have earlier closing sessions on the same VT, so try to
312 * find a running one first */
313 LIST_FOREACH(sessions_by_seat, i, s->sessions)
314 if (i->vtnr == vtnr && !i->stopping) {
320 /* no running one? then we can't decide which one is the
321 * active one, let the first one win */
322 LIST_FOREACH(sessions_by_seat, i, s->sessions)
323 if (i->vtnr == vtnr) {
329 r = seat_set_active(s, new_active);
334 int seat_read_active_vt(Seat *s) {
342 if (!seat_has_vts(s))
345 lseek(s->manager->console_active_fd, SEEK_SET, 0);
347 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
349 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
350 return k < 0 ? -errno : -EIO;
356 if (!startswith(t, "tty")) {
357 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
361 r = safe_atou(t+3, &vtnr);
363 log_error("Failed to parse VT number %s", t+3);
368 log_error("VT number invalid: %s", t+3);
372 return seat_active_vt_changed(s, vtnr);
375 int seat_start(Seat *s) {
382 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
384 LOG_MESSAGE("New seat %s.", s->id),
387 /* Read current VT */
388 seat_read_active_vt(s);
395 seat_send_signal(s, true);
400 int seat_stop(Seat *s, bool force) {
407 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
409 LOG_MESSAGE("Removed seat %s.", s->id),
412 seat_stop_sessions(s, force);
414 unlink(s->state_file);
415 seat_add_to_gc_queue(s);
418 seat_send_signal(s, false);
425 int seat_stop_sessions(Seat *s, bool force) {
431 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
432 k = session_stop(session, force);
440 void seat_evict_position(Seat *s, Session *session) {
442 unsigned int pos = session->pos;
449 if (pos < s->position_count && s->positions[pos] == session) {
450 s->positions[pos] = NULL;
452 /* There might be another session claiming the same
453 * position (eg., during gdm->session transition), so lets look
454 * for it and set it on the free slot. */
455 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
456 if (iter->pos == pos) {
457 s->positions[pos] = iter;
464 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
465 /* with VTs, the position is always the same as the VTnr */
469 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
472 seat_evict_position(s, session);
475 if (pos > 0 && !s->positions[pos])
476 s->positions[pos] = session;
479 static void seat_assign_position(Seat *s, Session *session) {
482 if (session->pos > 0)
485 for (pos = 1; pos < s->position_count; ++pos)
486 if (!s->positions[pos])
489 seat_claim_position(s, session, pos);
492 int seat_attach_session(Seat *s, Session *session) {
495 assert(!session->seat);
497 if (!seat_has_vts(s) != !session->vtnr)
501 LIST_PREPEND(sessions_by_seat, s->sessions, session);
502 seat_assign_position(s, session);
504 seat_send_changed(s, "Sessions", NULL);
506 /* On seats with VTs, the VT logic defines which session is active. On
507 * seats without VTs, we automatically activate new sessions. */
508 if (!seat_has_vts(s))
509 seat_set_active(s, session);
514 void seat_complete_switch(Seat *s) {
519 /* if no session-switch is pending or if it got canceled, do nothing */
520 if (!s->pending_switch)
523 session = s->pending_switch;
524 s->pending_switch = NULL;
526 seat_set_active(s, session);
529 bool seat_has_vts(Seat *s) {
532 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
535 bool seat_is_seat0(Seat *s) {
538 return s->manager->seat0 == s;
541 bool seat_can_multi_session(Seat *s) {
544 return seat_has_vts(s);
547 bool seat_can_tty(Seat *s) {
550 return seat_has_vts(s);
553 bool seat_has_master_device(Seat *s) {
556 /* device list is ordered by "master" flag */
557 return !!s->devices && s->devices->master;
560 bool seat_can_graphical(Seat *s) {
563 return seat_has_master_device(s);
566 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
568 bool idle_hint = true;
569 dual_timestamp ts = { 0, 0 };
573 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
577 ih = session_get_idle_hint(session, &k);
583 if (k.monotonic > ts.monotonic)
589 } else if (idle_hint) {
591 if (k.monotonic > ts.monotonic)
602 bool seat_check_gc(Seat *s, bool drop_not_started) {
605 if (drop_not_started && !s->started)
608 if (seat_is_seat0(s))
611 return seat_has_master_device(s);
614 void seat_add_to_gc_queue(Seat *s) {
620 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
621 s->in_gc_queue = true;
624 static bool seat_name_valid_char(char c) {
626 (c >= 'a' && c <= 'z') ||
627 (c >= 'A' && c <= 'Z') ||
628 (c >= '0' && c <= '9') ||
633 bool seat_name_is_valid(const char *name) {
638 if (!startswith(name, "seat"))
644 for (p = name; *p; p++)
645 if (!seat_name_valid_char(*p))
648 if (strlen(name) > 255)