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 "logind-seat.h"
31 #include "logind-acl.h"
34 #include "path-util.h"
36 Seat *seat_new(Manager *m, const char *id) {
46 s->state_file = strappend("/run/systemd/seats/", id);
52 s->id = path_get_file_name(s->state_file);
55 if (hashmap_put(m->seats, s->id, s) < 0) {
64 void seat_free(Seat *s) {
68 LIST_REMOVE(Seat, gc_queue, s->manager->seat_gc_queue, s);
71 session_free(s->sessions);
76 device_free(s->devices);
78 hashmap_remove(s->manager->seats, s->id);
84 int seat_save(Seat *s) {
94 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
98 r = fopen_temporary(s->state_file, &f, &temp_path);
102 fchmod(fileno(f), 0644);
105 "# This is private data. Do not parse.\n"
107 "CAN_MULTI_SESSION=%i\n"
109 "CAN_GRAPHICAL=%i\n",
110 seat_is_vtconsole(s),
111 seat_can_multi_session(s),
113 seat_can_graphical(s));
116 assert(s->active->user);
122 (unsigned long) s->active->user->uid);
128 fputs("SESSIONS=", f);
129 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
133 i->sessions_by_seat_next ? ' ' : '\n');
137 LIST_FOREACH(sessions_by_seat, i, s->sessions)
140 (unsigned long) i->user->uid,
141 i->sessions_by_seat_next ? ' ' : '\n');
146 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
148 unlink(s->state_file);
157 log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
162 int seat_load(Seat *s) {
165 /* There isn't actually anything to read here ... */
170 static int vt_allocate(int vtnr) {
176 if (asprintf(&p, "/dev/tty%i", vtnr) < 0)
179 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
182 r = fd < 0 ? -errno : 0;
185 close_nointr_nofail(fd);
190 int seat_preallocate_vts(Seat *s) {
197 log_debug("Preallocating VTs...");
199 if (s->manager->n_autovts <= 0)
202 if (!seat_can_multi_session(s))
205 for (i = 1; i <= s->manager->n_autovts; i++) {
210 log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
218 int seat_apply_acls(Seat *s, Session *old_active) {
223 r = devnode_acl_all(s->manager->udev,
226 !!old_active, old_active ? old_active->user->uid : 0,
227 !!s->active, s->active ? s->active->user->uid : 0);
230 log_error("Failed to apply ACLs: %s", strerror(-r));
235 int seat_set_active(Seat *s, Session *session) {
239 assert(!session || session->seat == s);
241 if (session == s->active)
244 old_active = s->active;
247 seat_apply_acls(s, old_active);
249 if (session && session->started)
250 session_send_changed(session, "Active\0");
252 if (!session || session->started)
253 seat_send_changed(s, "ActiveSession\0");
258 session_save(session);
259 user_save(session->user);
263 session_save(old_active);
264 user_save(old_active->user);
270 int seat_active_vt_changed(Seat *s, int vtnr) {
271 Session *i, *new_active = NULL;
277 if (!seat_can_multi_session(s))
280 log_debug("VT changed to %i", vtnr);
282 LIST_FOREACH(sessions_by_seat, i, s->sessions)
283 if (i->vtnr == vtnr) {
288 r = seat_set_active(s, new_active);
289 manager_spawn_autovt(s->manager, vtnr);
294 int seat_read_active_vt(Seat *s) {
301 if (!seat_can_multi_session(s))
304 lseek(s->manager->console_active_fd, SEEK_SET, 0);
306 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
308 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
309 return k < 0 ? -errno : -EIO;
315 if (!startswith(t, "tty")) {
316 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
320 r = safe_atoi(t+3, &vtnr);
322 log_error("Failed to parse VT number %s", t+3);
327 log_error("VT number invalid: %s", t+3);
331 return seat_active_vt_changed(s, vtnr);
334 int seat_start(Seat *s) {
340 log_info("New seat %s.", s->id);
342 /* Initialize VT magic stuff */
343 seat_preallocate_vts(s);
345 /* Read current VT */
346 seat_read_active_vt(s);
353 seat_send_signal(s, true);
358 int seat_stop(Seat *s) {
364 log_info("Removed seat %s.", s->id);
366 seat_stop_sessions(s);
368 unlink(s->state_file);
369 seat_add_to_gc_queue(s);
372 seat_send_signal(s, false);
379 int seat_stop_sessions(Seat *s) {
385 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
386 k = session_stop(session);
394 int seat_attach_session(Seat *s, Session *session) {
397 assert(!session->seat);
400 LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
402 seat_send_changed(s, "Sessions\0");
404 /* Note that even if a seat is not multi-session capable it
405 * still might have multiple sessions on it since old, dead
406 * sessions might continue to be tracked until all their
407 * processes are gone. The most recently added session
408 * (i.e. the first in s->sessions) is the one that matters. */
410 if (!seat_can_multi_session(s))
411 seat_set_active(s, session);
416 bool seat_is_vtconsole(Seat *s) {
419 return s->manager->vtconsole == s;
422 bool seat_can_multi_session(Seat *s) {
425 if (!seat_is_vtconsole(s))
428 /* If we can't watch which VT is in the foreground, we don't
429 * support VT switching */
431 return s->manager->console_active_fd >= 0;
434 bool seat_can_tty(Seat *s) {
437 return seat_is_vtconsole(s);
440 bool seat_can_graphical(Seat *s) {
446 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
448 bool idle_hint = true;
449 dual_timestamp ts = { 0, 0 };
453 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
457 ih = session_get_idle_hint(session, &k);
463 if (k.monotonic < ts.monotonic)
469 } else if (idle_hint) {
471 if (k.monotonic > ts.monotonic)
482 int seat_check_gc(Seat *s, bool drop_not_started) {
485 if (drop_not_started && !s->started)
488 if (seat_is_vtconsole(s))
494 void seat_add_to_gc_queue(Seat *s) {
500 LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
501 s->in_gc_queue = true;
504 static bool seat_name_valid_char(char c) {
506 (c >= 'a' && c <= 'z') ||
507 (c >= 'A' && c <= 'Z') ||
508 (c >= '0' && c <= '9') ||
513 bool seat_name_is_valid(const char *name) {
518 if (!startswith(name, "seat"))
524 for (p = name; *p; p++)
525 if (!seat_name_valid_char(*p))
528 if (strlen(name) > 255)