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"
35 Seat *seat_new(Manager *m, const char *id) {
45 s->state_file = strappend("/run/systemd/seats/", id);
51 s->id = file_name_from_path(s->state_file);
54 if (hashmap_put(m->seats, s->id, s) < 0) {
63 void seat_free(Seat *s) {
67 LIST_REMOVE(Seat, gc_queue, s->manager->seat_gc_queue, s);
70 session_free(s->sessions);
75 device_free(s->devices);
77 hashmap_remove(s->manager->seats, s->id);
83 int seat_save(Seat *s) {
93 r = safe_mkdir("/run/systemd/seats", 0755, 0, 0);
97 r = fopen_temporary(s->state_file, &f, &temp_path);
101 fchmod(fileno(f), 0644);
104 "# This is private data. Do not parse.\n"
106 "CAN_MULTI_SESSION=%i\n",
107 seat_is_vtconsole(s),
108 seat_can_multi_session(s));
111 assert(s->active->user);
117 (unsigned long) s->active->user->uid);
123 fputs("SESSIONS=", f);
124 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
128 i->sessions_by_seat_next ? ' ' : '\n');
132 LIST_FOREACH(sessions_by_seat, i, s->sessions)
135 (unsigned long) i->user->uid,
136 i->sessions_by_seat_next ? ' ' : '\n');
141 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
143 unlink(s->state_file);
152 log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
157 int seat_load(Seat *s) {
160 /* There isn't actually anything to read here ... */
165 static int vt_allocate(int vtnr) {
171 if (asprintf(&p, "/dev/tty%i", vtnr) < 0)
174 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
177 r = fd < 0 ? -errno : 0;
180 close_nointr_nofail(fd);
185 int seat_preallocate_vts(Seat *s) {
192 log_debug("Preallocating VTs...");
194 if (s->manager->n_autovts <= 0)
197 if (!seat_can_multi_session(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;
242 seat_apply_acls(s, old_active);
244 if (session && session->started)
245 session_send_changed(session, "Active\0");
247 if (!session || session->started)
248 seat_send_changed(s, "ActiveSession\0");
253 session_save(session);
254 user_save(session->user);
258 session_save(old_active);
259 user_save(old_active->user);
265 int seat_active_vt_changed(Seat *s, int vtnr) {
266 Session *i, *new_active = NULL;
272 if (!seat_can_multi_session(s))
275 log_debug("VT changed to %i", vtnr);
277 LIST_FOREACH(sessions_by_seat, i, s->sessions)
278 if (i->vtnr == vtnr) {
283 r = seat_set_active(s, new_active);
284 manager_spawn_autovt(s->manager, vtnr);
289 int seat_read_active_vt(Seat *s) {
296 if (!seat_can_multi_session(s))
299 lseek(s->manager->console_active_fd, SEEK_SET, 0);
301 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
303 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
304 return k < 0 ? -errno : -EIO;
310 if (!startswith(t, "tty")) {
311 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
315 r = safe_atoi(t+3, &vtnr);
317 log_error("Failed to parse VT number %s", t+3);
322 log_error("VT number invalid: %s", t+3);
326 return seat_active_vt_changed(s, vtnr);
329 int seat_start(Seat *s) {
335 log_info("New seat %s.", s->id);
337 /* Initialize VT magic stuff */
338 seat_preallocate_vts(s);
340 /* Read current VT */
341 seat_read_active_vt(s);
348 seat_send_signal(s, true);
353 int seat_stop(Seat *s) {
359 log_info("Removed seat %s.", s->id);
361 seat_stop_sessions(s);
363 unlink(s->state_file);
364 seat_add_to_gc_queue(s);
367 seat_send_signal(s, false);
374 int seat_stop_sessions(Seat *s) {
380 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
381 k = session_stop(session);
389 int seat_attach_session(Seat *s, Session *session) {
392 assert(!session->seat);
395 LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
397 seat_send_changed(s, "Sessions\0");
399 /* Note that even if a seat is not multi-session capable it
400 * still might have multiple sessions on it since old, dead
401 * sessions might continue to be tracked until all their
402 * processes are gone. The most recently added session
403 * (i.e. the first in s->sessions) is the one that matters. */
405 if (!seat_can_multi_session(s))
406 seat_set_active(s, session);
411 bool seat_is_vtconsole(Seat *s) {
414 return s->manager->vtconsole == s;
417 bool seat_can_multi_session(Seat *s) {
420 if (!seat_is_vtconsole(s))
423 /* If we can't watch which VT is in the foreground, we don't
424 * support VT switching */
426 return s->manager->console_active_fd >= 0;
429 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
431 bool idle_hint = true;
432 dual_timestamp ts = { 0, 0 };
436 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
440 ih = session_get_idle_hint(session, &k);
446 if (k.monotonic < ts.monotonic)
452 } else if (idle_hint) {
454 if (k.monotonic > ts.monotonic)
465 int seat_check_gc(Seat *s, bool drop_not_started) {
468 if (drop_not_started && !s->started)
471 if (seat_is_vtconsole(s))
477 void seat_add_to_gc_queue(Seat *s) {
483 LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
484 s->in_gc_queue = true;
487 static bool seat_name_valid_char(char c) {
489 (c >= 'a' && c <= 'z') ||
490 (c >= 'A' && c <= 'Z') ||
491 (c >= '0' && c <= '9') ||
496 bool seat_name_is_valid(const char *name) {
501 if (!startswith(name, "seat"))
507 for (p = name; *p; p++)
508 if (!seat_name_valid_char(*p))
511 if (strlen(name) > 255)