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",
108 seat_is_vtconsole(s),
109 seat_can_multi_session(s));
112 assert(s->active->user);
118 (unsigned long) s->active->user->uid);
124 fputs("SESSIONS=", f);
125 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
129 i->sessions_by_seat_next ? ' ' : '\n');
133 LIST_FOREACH(sessions_by_seat, i, s->sessions)
136 (unsigned long) i->user->uid,
137 i->sessions_by_seat_next ? ' ' : '\n');
142 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
144 unlink(s->state_file);
153 log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
158 int seat_load(Seat *s) {
161 /* There isn't actually anything to read here ... */
166 static int vt_allocate(int vtnr) {
172 if (asprintf(&p, "/dev/tty%i", vtnr) < 0)
175 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
178 r = fd < 0 ? -errno : 0;
181 close_nointr_nofail(fd);
186 int seat_preallocate_vts(Seat *s) {
193 log_debug("Preallocating VTs...");
195 if (s->manager->n_autovts <= 0)
198 if (!seat_can_multi_session(s))
201 for (i = 1; i <= s->manager->n_autovts; i++) {
206 log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
214 int seat_apply_acls(Seat *s, Session *old_active) {
219 r = devnode_acl_all(s->manager->udev,
222 !!old_active, old_active ? old_active->user->uid : 0,
223 !!s->active, s->active ? s->active->user->uid : 0);
226 log_error("Failed to apply ACLs: %s", strerror(-r));
231 int seat_set_active(Seat *s, Session *session) {
235 assert(!session || session->seat == s);
237 if (session == s->active)
240 old_active = s->active;
243 seat_apply_acls(s, old_active);
245 if (session && session->started)
246 session_send_changed(session, "Active\0");
248 if (!session || session->started)
249 seat_send_changed(s, "ActiveSession\0");
254 session_save(session);
255 user_save(session->user);
259 session_save(old_active);
260 user_save(old_active->user);
266 int seat_active_vt_changed(Seat *s, int vtnr) {
267 Session *i, *new_active = NULL;
273 if (!seat_can_multi_session(s))
276 log_debug("VT changed to %i", vtnr);
278 LIST_FOREACH(sessions_by_seat, i, s->sessions)
279 if (i->vtnr == vtnr) {
284 r = seat_set_active(s, new_active);
285 manager_spawn_autovt(s->manager, vtnr);
290 int seat_read_active_vt(Seat *s) {
297 if (!seat_can_multi_session(s))
300 lseek(s->manager->console_active_fd, SEEK_SET, 0);
302 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
304 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
305 return k < 0 ? -errno : -EIO;
311 if (!startswith(t, "tty")) {
312 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
316 r = safe_atoi(t+3, &vtnr);
318 log_error("Failed to parse VT number %s", t+3);
323 log_error("VT number invalid: %s", t+3);
327 return seat_active_vt_changed(s, vtnr);
330 int seat_start(Seat *s) {
336 log_info("New seat %s.", s->id);
338 /* Initialize VT magic stuff */
339 seat_preallocate_vts(s);
341 /* Read current VT */
342 seat_read_active_vt(s);
349 seat_send_signal(s, true);
354 int seat_stop(Seat *s) {
360 log_info("Removed seat %s.", s->id);
362 seat_stop_sessions(s);
364 unlink(s->state_file);
365 seat_add_to_gc_queue(s);
368 seat_send_signal(s, false);
375 int seat_stop_sessions(Seat *s) {
381 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
382 k = session_stop(session);
390 int seat_attach_session(Seat *s, Session *session) {
393 assert(!session->seat);
396 LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
398 seat_send_changed(s, "Sessions\0");
400 /* Note that even if a seat is not multi-session capable it
401 * still might have multiple sessions on it since old, dead
402 * sessions might continue to be tracked until all their
403 * processes are gone. The most recently added session
404 * (i.e. the first in s->sessions) is the one that matters. */
406 if (!seat_can_multi_session(s))
407 seat_set_active(s, session);
412 bool seat_is_vtconsole(Seat *s) {
415 return s->manager->vtconsole == s;
418 bool seat_can_multi_session(Seat *s) {
421 if (!seat_is_vtconsole(s))
424 /* If we can't watch which VT is in the foreground, we don't
425 * support VT switching */
427 return s->manager->console_active_fd >= 0;
430 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
432 bool idle_hint = true;
433 dual_timestamp ts = { 0, 0 };
437 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
441 ih = session_get_idle_hint(session, &k);
447 if (k.monotonic < ts.monotonic)
453 } else if (idle_hint) {
455 if (k.monotonic > ts.monotonic)
466 int seat_check_gc(Seat *s, bool drop_not_started) {
469 if (drop_not_started && !s->started)
472 if (seat_is_vtconsole(s))
478 void seat_add_to_gc_queue(Seat *s) {
484 LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
485 s->in_gc_queue = true;
488 static bool seat_name_valid_char(char c) {
490 (c >= 'a' && c <= 'z') ||
491 (c >= 'A' && c <= 'Z') ||
492 (c >= '0' && c <= '9') ||
497 bool seat_name_is_valid(const char *name) {
502 if (!startswith(name, "seat"))
508 for (p = name; *p; p++)
509 if (!seat_name_valid_char(*p))
512 if (strlen(name) > 255)