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",
112 seat_is_vtconsole(s),
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_can_multi_session(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;
249 seat_apply_acls(s, old_active);
251 if (session && session->started)
252 session_send_changed(session, "Active\0");
254 if (!session || session->started)
255 seat_send_changed(s, "ActiveSession\0");
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, int vtnr) {
274 Session *i, *new_active = NULL;
280 if (!seat_can_multi_session(s))
283 log_debug("VT changed to %i", 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) {
304 if (!seat_can_multi_session(s))
307 lseek(s->manager->console_active_fd, SEEK_SET, 0);
309 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
311 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
312 return k < 0 ? -errno : -EIO;
318 if (!startswith(t, "tty")) {
319 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
323 r = safe_atoi(t+3, &vtnr);
325 log_error("Failed to parse VT number %s", t+3);
330 log_error("VT number invalid: %s", t+3);
334 return seat_active_vt_changed(s, vtnr);
337 int seat_start(Seat *s) {
344 MESSAGE_ID(SD_MESSAGE_SEAT_START),
346 "MESSAGE=New seat %s.", s->id,
349 /* Initialize VT magic stuff */
350 seat_preallocate_vts(s);
352 /* Read current VT */
353 seat_read_active_vt(s);
360 seat_send_signal(s, true);
365 int seat_stop(Seat *s) {
372 MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
374 "MESSAGE=Removed seat %s.", s->id,
377 seat_stop_sessions(s);
379 unlink(s->state_file);
380 seat_add_to_gc_queue(s);
383 seat_send_signal(s, false);
390 int seat_stop_sessions(Seat *s) {
396 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
397 k = session_stop(session);
405 int seat_attach_session(Seat *s, Session *session) {
408 assert(!session->seat);
411 LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
413 seat_send_changed(s, "Sessions\0");
415 /* Note that even if a seat is not multi-session capable it
416 * still might have multiple sessions on it since old, dead
417 * sessions might continue to be tracked until all their
418 * processes are gone. The most recently added session
419 * (i.e. the first in s->sessions) is the one that matters. */
421 if (!seat_can_multi_session(s))
422 seat_set_active(s, session);
427 bool seat_is_vtconsole(Seat *s) {
430 return s->manager->vtconsole == s;
433 bool seat_can_multi_session(Seat *s) {
436 if (!seat_is_vtconsole(s))
439 /* If we can't watch which VT is in the foreground, we don't
440 * support VT switching */
442 return s->manager->console_active_fd >= 0;
445 bool seat_can_tty(Seat *s) {
448 return seat_is_vtconsole(s);
451 bool seat_can_graphical(Seat *s) {
457 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
459 bool idle_hint = true;
460 dual_timestamp ts = { 0, 0 };
464 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
468 ih = session_get_idle_hint(session, &k);
474 if (k.monotonic > ts.monotonic)
480 } else if (idle_hint) {
482 if (k.monotonic > ts.monotonic)
493 int seat_check_gc(Seat *s, bool drop_not_started) {
496 if (drop_not_started && !s->started)
499 if (seat_is_vtconsole(s))
505 void seat_add_to_gc_queue(Seat *s) {
511 LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
512 s->in_gc_queue = true;
515 static bool seat_name_valid_char(char c) {
517 (c >= 'a' && c <= 'z') ||
518 (c >= 'A' && c <= 'Z') ||
519 (c >= '0' && c <= '9') ||
524 bool seat_name_is_valid(const char *name) {
529 if (!startswith(name, "seat"))
535 for (p = name; *p; p++)
536 if (!seat_name_valid_char(*p))
539 if (strlen(name) > 255)