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>
31 #include "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 = basename(s->state_file);
57 if (hashmap_put(m->seats, s->id, s) < 0) {
66 void seat_free(Seat *s) {
70 LIST_REMOVE(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);
87 int seat_save(Seat *s) {
88 _cleanup_free_ char *temp_path = NULL;
89 _cleanup_fclose_ FILE *f = NULL;
97 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
101 r = fopen_temporary(s->state_file, &f, &temp_path);
105 fchmod(fileno(f), 0644);
108 "# This is private data. Do not parse.\n"
110 "CAN_MULTI_SESSION=%i\n"
112 "CAN_GRAPHICAL=%i\n",
114 seat_can_multi_session(s),
116 seat_can_graphical(s));
119 assert(s->active->user);
123 "ACTIVE_UID="UID_FMT"\n",
125 s->active->user->uid);
131 fputs("SESSIONS=", f);
132 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
136 i->sessions_by_seat_next ? ' ' : '\n');
140 LIST_FOREACH(sessions_by_seat, i, s->sessions)
144 i->sessions_by_seat_next ? ' ' : '\n');
149 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
151 unlink(s->state_file);
157 log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
162 int seat_load(Seat *s) {
165 /* There isn't actually anything to read here ... */
170 static int vt_allocate(unsigned int vtnr) {
171 char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
172 _cleanup_close_ int fd = -1;
176 snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
177 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
184 int seat_preallocate_vts(Seat *s) {
191 log_debug("Preallocating VTs...");
193 if (s->manager->n_autovts <= 0)
196 if (!seat_has_vts(s))
199 for (i = 1; i <= s->manager->n_autovts; i++) {
204 log_error_errno(q, "Failed to preallocate VT %u: %m", i);
212 int seat_apply_acls(Seat *s, Session *old_active) {
217 r = devnode_acl_all(s->manager->udev,
220 !!old_active, old_active ? old_active->user->uid : 0,
221 !!s->active, s->active ? s->active->user->uid : 0);
224 log_error_errno(r, "Failed to apply ACLs: %m");
229 int seat_set_active(Seat *s, Session *session) {
233 assert(!session || session->seat == s);
235 if (session == s->active)
238 old_active = s->active;
242 session_device_pause_all(old_active);
243 session_send_changed(old_active, "Active", NULL);
246 seat_apply_acls(s, old_active);
248 if (session && session->started) {
249 session_send_changed(session, "Active", NULL);
250 session_device_resume_all(session);
253 if (!session || session->started)
254 seat_send_changed(s, "ActiveSession", NULL);
259 session_save(session);
260 user_save(session->user);
264 session_save(old_active);
265 if (!session || session->user != old_active->user)
266 user_save(old_active->user);
272 int seat_switch_to(Seat *s, unsigned int num) {
273 /* Public session positions skip 0 (there is only F1-F12). Maybe it
274 * will get reassigned in the future, so return error for now. */
278 if (num >= s->position_count || !s->positions[num]) {
279 /* allow switching to unused VTs to trigger auto-activate */
280 if (seat_has_vts(s) && num < 64)
286 return session_activate(s->positions[num]);
289 int seat_switch_to_next(Seat *s) {
290 unsigned int start, i;
292 if (!s->position_count)
296 if (s->active && s->active->pos > 0)
297 start = s->active->pos;
299 for (i = start + 1; i < s->position_count; ++i)
301 return session_activate(s->positions[i]);
303 for (i = 1; i < start; ++i)
305 return session_activate(s->positions[i]);
310 int seat_switch_to_previous(Seat *s) {
311 unsigned int start, i;
313 if (!s->position_count)
317 if (s->active && s->active->pos > 0)
318 start = s->active->pos;
320 for (i = start - 1; i > 0; --i)
322 return session_activate(s->positions[i]);
324 for (i = s->position_count - 1; i > start; --i)
326 return session_activate(s->positions[i]);
331 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
332 Session *i, *new_active = NULL;
338 if (!seat_has_vts(s))
341 log_debug("VT changed to %u", vtnr);
343 /* we might have earlier closing sessions on the same VT, so try to
344 * find a running one first */
345 LIST_FOREACH(sessions_by_seat, i, s->sessions)
346 if (i->vtnr == vtnr && !i->stopping) {
352 /* no running one? then we can't decide which one is the
353 * active one, let the first one win */
354 LIST_FOREACH(sessions_by_seat, i, s->sessions)
355 if (i->vtnr == vtnr) {
361 r = seat_set_active(s, new_active);
362 manager_spawn_autovt(s->manager, vtnr);
367 int seat_read_active_vt(Seat *s) {
375 if (!seat_has_vts(s))
378 lseek(s->manager->console_active_fd, SEEK_SET, 0);
380 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
382 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
383 return k < 0 ? -errno : -EIO;
389 if (!startswith(t, "tty")) {
390 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
394 r = safe_atou(t+3, &vtnr);
396 log_error("Failed to parse VT number %s", t+3);
401 log_error("VT number invalid: %s", t+3);
405 return seat_active_vt_changed(s, vtnr);
408 int seat_start(Seat *s) {
415 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
417 LOG_MESSAGE("New seat %s.", s->id),
420 /* Initialize VT magic stuff */
421 seat_preallocate_vts(s);
423 /* Read current VT */
424 seat_read_active_vt(s);
431 seat_send_signal(s, true);
436 int seat_stop(Seat *s, bool force) {
443 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
445 LOG_MESSAGE("Removed seat %s.", s->id),
448 seat_stop_sessions(s, force);
450 unlink(s->state_file);
451 seat_add_to_gc_queue(s);
454 seat_send_signal(s, false);
461 int seat_stop_sessions(Seat *s, bool force) {
467 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
468 k = session_stop(session, force);
476 void seat_evict_position(Seat *s, Session *session) {
478 unsigned int pos = session->pos;
485 if (pos < s->position_count && s->positions[pos] == session) {
486 s->positions[pos] = NULL;
488 /* There might be another session claiming the same
489 * position (eg., during gdm->session transition), so lets look
490 * for it and set it on the free slot. */
491 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
492 if (iter->pos == pos) {
493 s->positions[pos] = iter;
500 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
501 /* with VTs, the position is always the same as the VTnr */
505 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
508 seat_evict_position(s, session);
511 if (pos > 0 && !s->positions[pos])
512 s->positions[pos] = session;
515 static void seat_assign_position(Seat *s, Session *session) {
518 if (session->pos > 0)
521 for (pos = 1; pos < s->position_count; ++pos)
522 if (!s->positions[pos])
525 seat_claim_position(s, session, pos);
528 int seat_attach_session(Seat *s, Session *session) {
531 assert(!session->seat);
533 if (!seat_has_vts(s) != !session->vtnr)
537 LIST_PREPEND(sessions_by_seat, s->sessions, session);
538 seat_assign_position(s, session);
540 seat_send_changed(s, "Sessions", NULL);
542 /* On seats with VTs, the VT logic defines which session is active. On
543 * seats without VTs, we automatically activate new sessions. */
544 if (!seat_has_vts(s))
545 seat_set_active(s, session);
550 void seat_complete_switch(Seat *s) {
555 /* if no session-switch is pending or if it got canceled, do nothing */
556 if (!s->pending_switch)
559 session = s->pending_switch;
560 s->pending_switch = NULL;
562 seat_set_active(s, session);
565 bool seat_has_vts(Seat *s) {
568 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
571 bool seat_is_seat0(Seat *s) {
574 return s->manager->seat0 == s;
577 bool seat_can_multi_session(Seat *s) {
580 return seat_has_vts(s);
583 bool seat_can_tty(Seat *s) {
586 return seat_has_vts(s);
589 bool seat_has_master_device(Seat *s) {
592 /* device list is ordered by "master" flag */
593 return !!s->devices && s->devices->master;
596 bool seat_can_graphical(Seat *s) {
599 return seat_has_master_device(s);
602 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
604 bool idle_hint = true;
605 dual_timestamp ts = { 0, 0 };
609 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
613 ih = session_get_idle_hint(session, &k);
619 if (k.monotonic > ts.monotonic)
625 } else if (idle_hint) {
627 if (k.monotonic > ts.monotonic)
638 bool seat_check_gc(Seat *s, bool drop_not_started) {
641 if (drop_not_started && !s->started)
644 if (seat_is_seat0(s))
647 return seat_has_master_device(s);
650 void seat_add_to_gc_queue(Seat *s) {
656 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
657 s->in_gc_queue = true;
660 static bool seat_name_valid_char(char c) {
662 (c >= 'a' && c <= 'z') ||
663 (c >= 'A' && c <= 'Z') ||
664 (c >= '0' && c <= '9') ||
669 bool seat_name_is_valid(const char *name) {
674 if (!startswith(name, "seat"))
680 for (p = name; *p; p++)
681 if (!seat_name_valid_char(*p))
684 if (strlen(name) > 255)