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/>.
26 #include <sys/ioctl.h>
31 #include "sd-messages.h"
32 #include "logind-seat.h"
33 #include "logind-acl.h"
36 #include "path-util.h"
38 Seat *seat_new(Manager *m, const char *id) {
48 s->state_file = strappend("/run/systemd/seats/", id);
54 s->id = basename(s->state_file);
57 if (hashmap_put(m->seats, s->id, s) < 0) {
66 void seat_free(Seat *s) {
70 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
73 session_free(s->sessions);
78 device_free(s->devices);
80 hashmap_remove(s->manager->seats, s->id);
87 int seat_save(Seat *s) {
88 _cleanup_free_ char *temp_path = NULL;
89 _cleanup_fclose_ FILE *f = NULL;
97 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
101 r = fopen_temporary(s->state_file, &f, &temp_path);
105 fchmod(fileno(f), 0644);
108 "# This is private data. Do not parse.\n"
110 "CAN_MULTI_SESSION=%i\n"
112 "CAN_GRAPHICAL=%i\n",
114 seat_can_multi_session(s),
116 seat_can_graphical(s));
119 assert(s->active->user);
123 "ACTIVE_UID="UID_FMT"\n",
125 s->active->user->uid);
131 fputs("SESSIONS=", f);
132 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
136 i->sessions_by_seat_next ? ' ' : '\n');
140 LIST_FOREACH(sessions_by_seat, i, s->sessions)
144 i->sessions_by_seat_next ? ' ' : '\n');
149 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
151 unlink(s->state_file);
157 log_error("Failed to save seat data %s: %s", s->state_file, strerror(-r));
162 int seat_load(Seat *s) {
165 /* There isn't actually anything to read here ... */
170 static int vt_allocate(unsigned int vtnr) {
171 char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
172 _cleanup_close_ int fd = -1;
176 snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
177 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
184 int seat_preallocate_vts(Seat *s) {
191 log_debug("Preallocating VTs...");
193 if (s->manager->n_autovts <= 0)
196 if (!seat_has_vts(s))
199 for (i = 1; i <= s->manager->n_autovts; i++) {
204 log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
212 int seat_apply_acls(Seat *s, Session *old_active) {
217 r = devnode_acl_all(s->manager->udev,
220 !!old_active, old_active ? old_active->user->uid : 0,
221 !!s->active, s->active ? s->active->user->uid : 0);
224 log_error("Failed to apply ACLs: %s", strerror(-r));
229 int seat_set_active(Seat *s, Session *session) {
233 assert(!session || session->seat == s);
235 if (session == s->active)
238 old_active = s->active;
242 session_device_pause_all(old_active);
243 session_send_changed(old_active, "Active", NULL);
246 seat_apply_acls(s, old_active);
248 if (session && session->started) {
249 session_send_changed(session, "Active", NULL);
250 session_device_resume_all(session);
253 if (!session || session->started)
254 seat_send_changed(s, "ActiveSession", NULL);
259 session_save(session);
260 user_save(session->user);
264 session_save(old_active);
265 if (!session || session->user != old_active->user)
266 user_save(old_active->user);
272 int seat_switch_to(Seat *s, unsigned int num) {
273 /* Public session positions skip 0 (there is only F1-F12). Maybe it
274 * will get reassigned in the future, so return error for now. */
278 if (num >= s->position_count || !s->positions[num])
281 return session_activate(s->positions[num]);
284 int seat_switch_to_next(Seat *s) {
285 unsigned int start, i;
287 if (!s->position_count)
291 if (s->active && s->active->pos > 0)
292 start = s->active->pos;
294 for (i = start + 1; i < s->position_count; ++i)
296 return session_activate(s->positions[i]);
298 for (i = 1; i < start; ++i)
300 return session_activate(s->positions[i]);
305 int seat_switch_to_previous(Seat *s) {
306 unsigned int start, i;
308 if (!s->position_count)
312 if (s->active && s->active->pos > 0)
313 start = s->active->pos;
315 for (i = start - 1; i > 0; --i)
317 return session_activate(s->positions[i]);
319 for (i = s->position_count - 1; i > start; --i)
321 return session_activate(s->positions[i]);
326 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
327 Session *i, *new_active = NULL;
333 if (!seat_has_vts(s))
336 log_debug("VT changed to %u", vtnr);
338 LIST_FOREACH(sessions_by_seat, i, s->sessions)
339 if (i->vtnr == vtnr) {
344 r = seat_set_active(s, new_active);
345 manager_spawn_autovt(s->manager, vtnr);
350 int seat_read_active_vt(Seat *s) {
358 if (!seat_has_vts(s))
361 lseek(s->manager->console_active_fd, SEEK_SET, 0);
363 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
365 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
366 return k < 0 ? -errno : -EIO;
372 if (!startswith(t, "tty")) {
373 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
377 r = safe_atou(t+3, &vtnr);
379 log_error("Failed to parse VT number %s", t+3);
384 log_error("VT number invalid: %s", t+3);
388 return seat_active_vt_changed(s, vtnr);
391 int seat_start(Seat *s) {
398 MESSAGE_ID(SD_MESSAGE_SEAT_START),
400 "MESSAGE=New seat %s.", s->id,
403 /* Initialize VT magic stuff */
404 seat_preallocate_vts(s);
406 /* Read current VT */
407 seat_read_active_vt(s);
414 seat_send_signal(s, true);
419 int seat_stop(Seat *s, bool force) {
426 MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
428 "MESSAGE=Removed seat %s.", s->id,
431 seat_stop_sessions(s, force);
433 unlink(s->state_file);
434 seat_add_to_gc_queue(s);
437 seat_send_signal(s, false);
444 int seat_stop_sessions(Seat *s, bool force) {
450 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
451 k = session_stop(session, force);
459 void seat_evict_position(Seat *s, Session *session) {
461 unsigned int pos = session->pos;
468 if (pos < s->position_count && s->positions[pos] == session) {
469 s->positions[pos] = NULL;
471 /* There might be another session claiming the same
472 * position (eg., during gdm->session transition), so lets look
473 * for it and set it on the free slot. */
474 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
475 if (iter->pos == pos) {
476 s->positions[pos] = iter;
483 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
484 /* with VTs, the position is always the same as the VTnr */
488 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
491 seat_evict_position(s, session);
494 if (pos > 0 && !s->positions[pos])
495 s->positions[pos] = session;
498 static void seat_assign_position(Seat *s, Session *session) {
501 if (session->pos > 0)
504 for (pos = 1; pos < s->position_count; ++pos)
505 if (!s->positions[pos])
508 seat_claim_position(s, session, pos);
511 int seat_attach_session(Seat *s, Session *session) {
514 assert(!session->seat);
516 if (!seat_has_vts(s) != !session->vtnr)
520 LIST_PREPEND(sessions_by_seat, s->sessions, session);
521 seat_assign_position(s, session);
523 seat_send_changed(s, "Sessions", NULL);
525 /* On seats with VTs, the VT logic defines which session is active. On
526 * seats without VTs, we automatically activate new sessions. */
527 if (!seat_has_vts(s))
528 seat_set_active(s, session);
533 void seat_complete_switch(Seat *s) {
538 /* if no session-switch is pending or if it got canceled, do nothing */
539 if (!s->pending_switch)
542 session = s->pending_switch;
543 s->pending_switch = NULL;
545 seat_set_active(s, session);
548 bool seat_has_vts(Seat *s) {
551 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
554 bool seat_is_seat0(Seat *s) {
557 return s->manager->seat0 == s;
560 bool seat_can_multi_session(Seat *s) {
563 return seat_has_vts(s);
566 bool seat_can_tty(Seat *s) {
569 return seat_has_vts(s);
572 bool seat_has_master_device(Seat *s) {
575 /* device list is ordered by "master" flag */
576 return !!s->devices && s->devices->master;
579 bool seat_can_graphical(Seat *s) {
582 return seat_has_master_device(s);
585 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
587 bool idle_hint = true;
588 dual_timestamp ts = { 0, 0 };
592 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
596 ih = session_get_idle_hint(session, &k);
602 if (k.monotonic > ts.monotonic)
608 } else if (idle_hint) {
610 if (k.monotonic > ts.monotonic)
621 bool seat_check_gc(Seat *s, bool drop_not_started) {
624 if (drop_not_started && !s->started)
627 if (seat_is_seat0(s))
630 return seat_has_master_device(s);
633 void seat_add_to_gc_queue(Seat *s) {
639 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
640 s->in_gc_queue = true;
643 static bool seat_name_valid_char(char c) {
645 (c >= 'a' && c <= 'z') ||
646 (c >= 'A' && c <= 'Z') ||
647 (c >= '0' && c <= '9') ||
652 bool seat_name_is_valid(const char *name) {
657 if (!startswith(name, "seat"))
663 for (p = name; *p; p++)
664 if (!seat_name_valid_char(*p))
667 if (strlen(name) > 255)