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>
30 #include "systemd/sd-id128.h"
31 #include "systemd/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 = path_get_file_name(s->state_file);
57 if (hashmap_put(m->seats, s->id, s) < 0) {
66 void seat_free(Seat *s) {
70 LIST_REMOVE(Seat, 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);
86 int seat_save(Seat *s) {
96 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
100 r = fopen_temporary(s->state_file, &f, &temp_path);
104 fchmod(fileno(f), 0644);
107 "# This is private data. Do not parse.\n"
109 "CAN_MULTI_SESSION=%i\n"
111 "CAN_GRAPHICAL=%i\n",
113 seat_can_multi_session(s),
115 seat_can_graphical(s));
118 assert(s->active->user);
124 (unsigned long) s->active->user->uid);
130 fputs("SESSIONS=", f);
131 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
135 i->sessions_by_seat_next ? ' ' : '\n');
139 LIST_FOREACH(sessions_by_seat, i, s->sessions)
142 (unsigned long) i->user->uid,
143 i->sessions_by_seat_next ? ' ' : '\n');
148 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
150 unlink(s->state_file);
159 log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
164 int seat_load(Seat *s) {
167 /* There isn't actually anything to read here ... */
172 static int vt_allocate(int vtnr) {
178 if (asprintf(&p, "/dev/tty%i", vtnr) < 0)
181 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
184 r = fd < 0 ? -errno : 0;
187 close_nointr_nofail(fd);
192 int seat_preallocate_vts(Seat *s) {
199 log_debug("Preallocating VTs...");
201 if (s->manager->n_autovts <= 0)
204 if (!seat_has_vts(s))
207 for (i = 1; i <= s->manager->n_autovts; i++) {
212 log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
220 int seat_apply_acls(Seat *s, Session *old_active) {
225 r = devnode_acl_all(s->manager->udev,
228 !!old_active, old_active ? old_active->user->uid : 0,
229 !!s->active, s->active ? s->active->user->uid : 0);
232 log_error("Failed to apply ACLs: %s", strerror(-r));
237 int seat_set_active(Seat *s, Session *session) {
241 assert(!session || session->seat == s);
243 if (session == s->active)
246 old_active = s->active;
250 session_device_pause_all(old_active);
252 seat_apply_acls(s, old_active);
254 if (session && session->started) {
255 session_send_changed(session, "Active\0");
256 session_device_resume_all(session);
259 if (!session || session->started)
260 seat_send_changed(s, "ActiveSession\0");
265 session_save(session);
266 user_save(session->user);
270 session_save(old_active);
271 if (!session || session->user != old_active->user)
272 user_save(old_active->user);
278 int seat_active_vt_changed(Seat *s, int vtnr) {
279 Session *i, *new_active = NULL;
285 if (!seat_has_vts(s))
288 log_debug("VT changed to %i", vtnr);
290 LIST_FOREACH(sessions_by_seat, i, s->sessions)
291 if (i->vtnr == vtnr) {
296 r = seat_set_active(s, new_active);
297 manager_spawn_autovt(s->manager, vtnr);
302 int seat_read_active_vt(Seat *s) {
309 if (!seat_has_vts(s))
312 lseek(s->manager->console_active_fd, SEEK_SET, 0);
314 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
316 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
317 return k < 0 ? -errno : -EIO;
323 if (!startswith(t, "tty")) {
324 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
328 r = safe_atoi(t+3, &vtnr);
330 log_error("Failed to parse VT number %s", t+3);
335 log_error("VT number invalid: %s", t+3);
339 return seat_active_vt_changed(s, vtnr);
342 int seat_start(Seat *s) {
349 MESSAGE_ID(SD_MESSAGE_SEAT_START),
351 "MESSAGE=New seat %s.", s->id,
354 /* Initialize VT magic stuff */
355 seat_preallocate_vts(s);
357 /* Read current VT */
358 seat_read_active_vt(s);
365 seat_send_signal(s, true);
370 int seat_stop(Seat *s) {
377 MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
379 "MESSAGE=Removed seat %s.", s->id,
382 seat_stop_sessions(s);
384 unlink(s->state_file);
385 seat_add_to_gc_queue(s);
388 seat_send_signal(s, false);
395 int seat_stop_sessions(Seat *s) {
401 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
402 k = session_stop(session);
410 int seat_attach_session(Seat *s, Session *session) {
413 assert(!session->seat);
416 LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
418 seat_send_changed(s, "Sessions\0");
420 /* On seats with VTs, the VT logic defines which session is active. On
421 * seats without VTs, we automatically activate the first session. */
422 if (!seat_has_vts(s) && !s->active)
423 seat_set_active(s, session);
428 void seat_complete_switch(Seat *s) {
433 /* if no session-switch is pending or if it got canceled, do nothing */
434 if (!s->pending_switch)
437 session = s->pending_switch;
438 s->pending_switch = NULL;
440 seat_set_active(s, session);
443 bool seat_has_vts(Seat *s) {
446 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
449 bool seat_is_seat0(Seat *s) {
452 return s->manager->seat0 == s;
455 bool seat_can_multi_session(Seat *s) {
458 return seat_has_vts(s);
461 bool seat_can_tty(Seat *s) {
464 return seat_has_vts(s);
467 bool seat_has_master_device(Seat *s) {
470 /* device list is ordered by "master" flag */
471 return !!s->devices && s->devices->master;
474 bool seat_can_graphical(Seat *s) {
477 return seat_has_master_device(s);
480 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
482 bool idle_hint = true;
483 dual_timestamp ts = { 0, 0 };
487 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
491 ih = session_get_idle_hint(session, &k);
497 if (k.monotonic > ts.monotonic)
503 } else if (idle_hint) {
505 if (k.monotonic > ts.monotonic)
516 int seat_check_gc(Seat *s, bool drop_not_started) {
519 if (drop_not_started && !s->started)
522 if (seat_is_seat0(s))
525 return seat_has_master_device(s);
528 void seat_add_to_gc_queue(Seat *s) {
534 LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
535 s->in_gc_queue = true;
538 static bool seat_name_valid_char(char c) {
540 (c >= 'a' && c <= 'z') ||
541 (c >= 'A' && c <= 'Z') ||
542 (c >= '0' && c <= '9') ||
547 bool seat_name_is_valid(const char *name) {
552 if (!startswith(name, "seat"))
558 for (p = name; *p; p++)
559 if (!seat_name_valid_char(*p))
562 if (strlen(name) > 255)