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);
251 session_send_changed(old_active, "Active\0");
254 seat_apply_acls(s, old_active);
256 if (session && session->started) {
257 session_send_changed(session, "Active\0");
258 session_device_resume_all(session);
261 if (!session || session->started)
262 seat_send_changed(s, "ActiveSession\0");
267 session_save(session);
268 user_save(session->user);
272 session_save(old_active);
273 if (!session || session->user != old_active->user)
274 user_save(old_active->user);
280 int seat_active_vt_changed(Seat *s, int vtnr) {
281 Session *i, *new_active = NULL;
287 if (!seat_has_vts(s))
290 log_debug("VT changed to %i", vtnr);
292 LIST_FOREACH(sessions_by_seat, i, s->sessions)
293 if (i->vtnr == vtnr) {
298 r = seat_set_active(s, new_active);
299 manager_spawn_autovt(s->manager, vtnr);
304 int seat_read_active_vt(Seat *s) {
311 if (!seat_has_vts(s))
314 lseek(s->manager->console_active_fd, SEEK_SET, 0);
316 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
318 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
319 return k < 0 ? -errno : -EIO;
325 if (!startswith(t, "tty")) {
326 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
330 r = safe_atoi(t+3, &vtnr);
332 log_error("Failed to parse VT number %s", t+3);
337 log_error("VT number invalid: %s", t+3);
341 return seat_active_vt_changed(s, vtnr);
344 int seat_start(Seat *s) {
351 MESSAGE_ID(SD_MESSAGE_SEAT_START),
353 "MESSAGE=New seat %s.", s->id,
356 /* Initialize VT magic stuff */
357 seat_preallocate_vts(s);
359 /* Read current VT */
360 seat_read_active_vt(s);
367 seat_send_signal(s, true);
372 int seat_stop(Seat *s) {
379 MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
381 "MESSAGE=Removed seat %s.", s->id,
384 seat_stop_sessions(s);
386 unlink(s->state_file);
387 seat_add_to_gc_queue(s);
390 seat_send_signal(s, false);
397 int seat_stop_sessions(Seat *s) {
403 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
404 k = session_stop(session);
412 int seat_attach_session(Seat *s, Session *session) {
415 assert(!session->seat);
418 LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
420 seat_send_changed(s, "Sessions\0");
422 /* On seats with VTs, the VT logic defines which session is active. On
423 * seats without VTs, we automatically activate the first session. */
424 if (!seat_has_vts(s) && !s->active)
425 seat_set_active(s, session);
430 void seat_complete_switch(Seat *s) {
435 /* if no session-switch is pending or if it got canceled, do nothing */
436 if (!s->pending_switch)
439 session = s->pending_switch;
440 s->pending_switch = NULL;
442 seat_set_active(s, session);
445 bool seat_has_vts(Seat *s) {
448 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
451 bool seat_is_seat0(Seat *s) {
454 return s->manager->seat0 == s;
457 bool seat_can_multi_session(Seat *s) {
460 return seat_has_vts(s);
463 bool seat_can_tty(Seat *s) {
466 return seat_has_vts(s);
469 bool seat_has_master_device(Seat *s) {
472 /* device list is ordered by "master" flag */
473 return !!s->devices && s->devices->master;
476 bool seat_can_graphical(Seat *s) {
479 return seat_has_master_device(s);
482 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
484 bool idle_hint = true;
485 dual_timestamp ts = { 0, 0 };
489 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
493 ih = session_get_idle_hint(session, &k);
499 if (k.monotonic > ts.monotonic)
505 } else if (idle_hint) {
507 if (k.monotonic > ts.monotonic)
518 int seat_check_gc(Seat *s, bool drop_not_started) {
521 if (drop_not_started && !s->started)
524 if (seat_is_seat0(s))
527 return seat_has_master_device(s);
530 void seat_add_to_gc_queue(Seat *s) {
536 LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
537 s->in_gc_queue = true;
540 static bool seat_name_valid_char(char c) {
542 (c >= 'a' && c <= 'z') ||
543 (c >= 'A' && c <= 'Z') ||
544 (c >= '0' && c <= '9') ||
549 bool seat_name_is_valid(const char *name) {
554 if (!startswith(name, "seat"))
560 for (p = name; *p; p++)
561 if (!seat_name_valid_char(*p))
564 if (strlen(name) > 255)