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 user_save(old_active->user);
272 int seat_active_vt_changed(Seat *s, int vtnr) {
273 Session *i, *new_active = NULL;
279 if (!seat_can_multi_session(s))
282 log_debug("VT changed to %i", vtnr);
284 LIST_FOREACH(sessions_by_seat, i, s->sessions)
285 if (i->vtnr == vtnr) {
290 r = seat_set_active(s, new_active);
291 manager_spawn_autovt(s->manager, vtnr);
296 int seat_read_active_vt(Seat *s) {
303 if (!seat_can_multi_session(s))
306 lseek(s->manager->console_active_fd, SEEK_SET, 0);
308 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
310 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
311 return k < 0 ? -errno : -EIO;
317 if (!startswith(t, "tty")) {
318 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
322 r = safe_atoi(t+3, &vtnr);
324 log_error("Failed to parse VT number %s", t+3);
329 log_error("VT number invalid: %s", t+3);
333 return seat_active_vt_changed(s, vtnr);
336 int seat_start(Seat *s) {
343 "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SEAT_START),
345 "MESSAGE=New seat %s.", s->id,
348 /* Initialize VT magic stuff */
349 seat_preallocate_vts(s);
351 /* Read current VT */
352 seat_read_active_vt(s);
359 seat_send_signal(s, true);
364 int seat_stop(Seat *s) {
371 "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SEAT_STOP),
373 "MESSAGE=Removed seat %s.", s->id,
376 seat_stop_sessions(s);
378 unlink(s->state_file);
379 seat_add_to_gc_queue(s);
382 seat_send_signal(s, false);
389 int seat_stop_sessions(Seat *s) {
395 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
396 k = session_stop(session);
404 int seat_attach_session(Seat *s, Session *session) {
407 assert(!session->seat);
410 LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
412 seat_send_changed(s, "Sessions\0");
414 /* Note that even if a seat is not multi-session capable it
415 * still might have multiple sessions on it since old, dead
416 * sessions might continue to be tracked until all their
417 * processes are gone. The most recently added session
418 * (i.e. the first in s->sessions) is the one that matters. */
420 if (!seat_can_multi_session(s))
421 seat_set_active(s, session);
426 bool seat_is_vtconsole(Seat *s) {
429 return s->manager->vtconsole == s;
432 bool seat_can_multi_session(Seat *s) {
435 if (!seat_is_vtconsole(s))
438 /* If we can't watch which VT is in the foreground, we don't
439 * support VT switching */
441 return s->manager->console_active_fd >= 0;
444 bool seat_can_tty(Seat *s) {
447 return seat_is_vtconsole(s);
450 bool seat_can_graphical(Seat *s) {
456 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
458 bool idle_hint = true;
459 dual_timestamp ts = { 0, 0 };
463 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
467 ih = session_get_idle_hint(session, &k);
473 if (k.monotonic < ts.monotonic)
479 } else if (idle_hint) {
481 if (k.monotonic > ts.monotonic)
492 int seat_check_gc(Seat *s, bool drop_not_started) {
495 if (drop_not_started && !s->started)
498 if (seat_is_vtconsole(s))
504 void seat_add_to_gc_queue(Seat *s) {
510 LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
511 s->in_gc_queue = true;
514 static bool seat_name_valid_char(char c) {
516 (c >= 'a' && c <= 'z') ||
517 (c >= 'A' && c <= 'Z') ||
518 (c >= '0' && c <= '9') ||
523 bool seat_name_is_valid(const char *name) {
528 if (!startswith(name, "seat"))
534 for (p = name; *p; p++)
535 if (!seat_name_valid_char(*p))
538 if (strlen(name) > 255)