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);
86 int seat_save(Seat *s) {
87 _cleanup_free_ char *temp_path = NULL;
88 _cleanup_fclose_ FILE *f = NULL;
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);
156 log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
161 int seat_load(Seat *s) {
164 /* There isn't actually anything to read here ... */
169 static int vt_allocate(unsigned int vtnr) {
170 _cleanup_free_ char *p = NULL;
171 _cleanup_close_ int fd = -1;
175 if (asprintf(&p, "/dev/tty%u", vtnr) < 0)
178 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
185 int seat_preallocate_vts(Seat *s) {
192 log_debug("Preallocating VTs...");
194 if (s->manager->n_autovts <= 0)
197 if (!seat_has_vts(s))
200 for (i = 1; i <= s->manager->n_autovts; i++) {
205 log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
213 int seat_apply_acls(Seat *s, Session *old_active) {
218 r = devnode_acl_all(s->manager->udev,
221 !!old_active, old_active ? old_active->user->uid : 0,
222 !!s->active, s->active ? s->active->user->uid : 0);
225 log_error("Failed to apply ACLs: %s", strerror(-r));
230 int seat_set_active(Seat *s, Session *session) {
234 assert(!session || session->seat == s);
236 if (session == s->active)
239 old_active = s->active;
243 session_device_pause_all(old_active);
244 session_send_changed(old_active, "Active", NULL);
247 seat_apply_acls(s, old_active);
249 if (session && session->started) {
250 session_send_changed(session, "Active", NULL);
251 session_device_resume_all(session);
254 if (!session || session->started)
255 seat_send_changed(s, "ActiveSession", NULL);
260 session_save(session);
261 user_save(session->user);
265 session_save(old_active);
266 if (!session || session->user != old_active->user)
267 user_save(old_active->user);
273 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
274 Session *i, *new_active = NULL;
280 if (!seat_has_vts(s))
283 log_debug("VT changed to %u", vtnr);
285 LIST_FOREACH(sessions_by_seat, i, s->sessions)
286 if (i->vtnr == vtnr) {
291 r = seat_set_active(s, new_active);
292 manager_spawn_autovt(s->manager, vtnr);
297 int seat_read_active_vt(Seat *s) {
305 if (!seat_has_vts(s))
308 lseek(s->manager->console_active_fd, SEEK_SET, 0);
310 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
312 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
313 return k < 0 ? -errno : -EIO;
319 if (!startswith(t, "tty")) {
320 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
324 r = safe_atou(t+3, &vtnr);
326 log_error("Failed to parse VT number %s", t+3);
331 log_error("VT number invalid: %s", t+3);
335 return seat_active_vt_changed(s, vtnr);
338 int seat_start(Seat *s) {
345 MESSAGE_ID(SD_MESSAGE_SEAT_START),
347 "MESSAGE=New seat %s.", s->id,
350 /* Initialize VT magic stuff */
351 seat_preallocate_vts(s);
353 /* Read current VT */
354 seat_read_active_vt(s);
361 seat_send_signal(s, true);
366 int seat_stop(Seat *s) {
373 MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
375 "MESSAGE=Removed seat %s.", s->id,
378 seat_stop_sessions(s);
380 unlink(s->state_file);
381 seat_add_to_gc_queue(s);
384 seat_send_signal(s, false);
391 int seat_stop_sessions(Seat *s) {
397 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
398 k = session_stop(session);
406 int seat_attach_session(Seat *s, Session *session) {
409 assert(!session->seat);
411 if (!seat_has_vts(s) != !session->vtnr)
415 LIST_PREPEND(sessions_by_seat, s->sessions, session);
417 seat_send_changed(s, "Sessions", NULL);
419 /* On seats with VTs, the VT logic defines which session is active. On
420 * seats without VTs, we automatically activate new sessions. */
421 if (!seat_has_vts(s))
422 seat_set_active(s, session);
427 void seat_complete_switch(Seat *s) {
432 /* if no session-switch is pending or if it got canceled, do nothing */
433 if (!s->pending_switch)
436 session = s->pending_switch;
437 s->pending_switch = NULL;
439 seat_set_active(s, session);
442 bool seat_has_vts(Seat *s) {
445 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
448 bool seat_is_seat0(Seat *s) {
451 return s->manager->seat0 == s;
454 bool seat_can_multi_session(Seat *s) {
457 return seat_has_vts(s);
460 bool seat_can_tty(Seat *s) {
463 return seat_has_vts(s);
466 bool seat_has_master_device(Seat *s) {
469 /* device list is ordered by "master" flag */
470 return !!s->devices && s->devices->master;
473 bool seat_can_graphical(Seat *s) {
476 return seat_has_master_device(s);
479 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
481 bool idle_hint = true;
482 dual_timestamp ts = { 0, 0 };
486 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
490 ih = session_get_idle_hint(session, &k);
496 if (k.monotonic > ts.monotonic)
502 } else if (idle_hint) {
504 if (k.monotonic > ts.monotonic)
515 bool seat_check_gc(Seat *s, bool drop_not_started) {
518 if (drop_not_started && !s->started)
521 if (seat_is_seat0(s))
524 return seat_has_master_device(s);
527 void seat_add_to_gc_queue(Seat *s) {
533 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
534 s->in_gc_queue = true;
537 static bool seat_name_valid_char(char c) {
539 (c >= 'a' && c <= 'z') ||
540 (c >= 'A' && c <= 'Z') ||
541 (c >= '0' && c <= '9') ||
546 bool seat_name_is_valid(const char *name) {
551 if (!startswith(name, "seat"))
557 for (p = name; *p; p++)
558 if (!seat_name_valid_char(*p))
561 if (strlen(name) > 255)