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/>.
27 #include "sd-messages.h"
28 #include "logind-seat.h"
29 #include "logind-acl.h"
33 Seat *seat_new(Manager *m, const char *id) {
43 s->state_file = strappend("/run/systemd/seats/", id);
49 s->id = basename(s->state_file);
52 if (hashmap_put(m->seats, s->id, s) < 0) {
61 void seat_free(Seat *s) {
65 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
68 session_free(s->sessions);
73 device_free(s->devices);
75 hashmap_remove(s->manager->seats, s->id);
82 int seat_save(Seat *s) {
83 _cleanup_free_ char *temp_path = NULL;
84 _cleanup_fclose_ FILE *f = NULL;
92 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
96 r = fopen_temporary(s->state_file, &f, &temp_path);
100 fchmod(fileno(f), 0644);
103 "# This is private data. Do not parse.\n"
105 "CAN_MULTI_SESSION=%i\n"
107 "CAN_GRAPHICAL=%i\n",
109 seat_can_multi_session(s),
111 seat_can_graphical(s));
114 assert(s->active->user);
118 "ACTIVE_UID="UID_FMT"\n",
120 s->active->user->uid);
126 fputs("SESSIONS=", f);
127 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
131 i->sessions_by_seat_next ? ' ' : '\n');
135 LIST_FOREACH(sessions_by_seat, i, s->sessions)
139 i->sessions_by_seat_next ? ' ' : '\n');
144 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
146 unlink(s->state_file);
152 log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
157 int seat_load(Seat *s) {
160 /* There isn't actually anything to read here ... */
165 static int vt_allocate(unsigned int vtnr) {
166 char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
167 _cleanup_close_ int fd = -1;
171 snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
172 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
179 int seat_preallocate_vts(Seat *s) {
186 log_debug("Preallocating VTs...");
188 if (s->manager->n_autovts <= 0)
191 if (!seat_has_vts(s))
194 for (i = 1; i <= s->manager->n_autovts; i++) {
199 log_error_errno(q, "Failed to preallocate VT %u: %m", i);
207 int seat_apply_acls(Seat *s, Session *old_active) {
212 r = devnode_acl_all(s->manager->udev,
215 !!old_active, old_active ? old_active->user->uid : 0,
216 !!s->active, s->active ? s->active->user->uid : 0);
219 log_error_errno(r, "Failed to apply ACLs: %m");
224 int seat_set_active(Seat *s, Session *session) {
228 assert(!session || session->seat == s);
230 if (session == s->active)
233 old_active = s->active;
237 session_device_pause_all(old_active);
238 session_send_changed(old_active, "Active", NULL);
241 seat_apply_acls(s, old_active);
243 if (session && session->started) {
244 session_send_changed(session, "Active", NULL);
245 session_device_resume_all(session);
248 if (!session || session->started)
249 seat_send_changed(s, "ActiveSession", NULL);
254 session_save(session);
255 user_save(session->user);
259 session_save(old_active);
260 if (!session || session->user != old_active->user)
261 user_save(old_active->user);
267 int seat_switch_to(Seat *s, unsigned int num) {
268 /* Public session positions skip 0 (there is only F1-F12). Maybe it
269 * will get reassigned in the future, so return error for now. */
273 if (num >= s->position_count || !s->positions[num]) {
274 /* allow switching to unused VTs to trigger auto-activate */
275 if (seat_has_vts(s) && num < 64)
281 return session_activate(s->positions[num]);
284 int seat_switch_to_next(Seat *s) {
285 unsigned int start, i;
287 if (!s->position_count)
291 if (s->active && s->active->pos > 0)
292 start = s->active->pos;
294 for (i = start + 1; i < s->position_count; ++i)
296 return session_activate(s->positions[i]);
298 for (i = 1; i < start; ++i)
300 return session_activate(s->positions[i]);
305 int seat_switch_to_previous(Seat *s) {
306 unsigned int start, i;
308 if (!s->position_count)
312 if (s->active && s->active->pos > 0)
313 start = s->active->pos;
315 for (i = start - 1; i > 0; --i)
317 return session_activate(s->positions[i]);
319 for (i = s->position_count - 1; i > start; --i)
321 return session_activate(s->positions[i]);
326 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
327 Session *i, *new_active = NULL;
333 if (!seat_has_vts(s))
336 log_debug("VT changed to %u", vtnr);
338 /* we might have earlier closing sessions on the same VT, so try to
339 * find a running one first */
340 LIST_FOREACH(sessions_by_seat, i, s->sessions)
341 if (i->vtnr == vtnr && !i->stopping) {
347 /* no running one? then we can't decide which one is the
348 * active one, let the first one win */
349 LIST_FOREACH(sessions_by_seat, i, s->sessions)
350 if (i->vtnr == vtnr) {
356 r = seat_set_active(s, new_active);
357 manager_spawn_autovt(s->manager, vtnr);
362 int seat_read_active_vt(Seat *s) {
370 if (!seat_has_vts(s))
373 lseek(s->manager->console_active_fd, SEEK_SET, 0);
375 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
377 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
378 return k < 0 ? -errno : -EIO;
384 if (!startswith(t, "tty")) {
385 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
389 r = safe_atou(t+3, &vtnr);
391 log_error("Failed to parse VT number %s", t+3);
396 log_error("VT number invalid: %s", t+3);
400 return seat_active_vt_changed(s, vtnr);
403 int seat_start(Seat *s) {
410 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
412 LOG_MESSAGE("New seat %s.", s->id),
415 /* Initialize VT magic stuff */
416 seat_preallocate_vts(s);
418 /* Read current VT */
419 seat_read_active_vt(s);
426 seat_send_signal(s, true);
431 int seat_stop(Seat *s, bool force) {
438 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
440 LOG_MESSAGE("Removed seat %s.", s->id),
443 seat_stop_sessions(s, force);
445 unlink(s->state_file);
446 seat_add_to_gc_queue(s);
449 seat_send_signal(s, false);
456 int seat_stop_sessions(Seat *s, bool force) {
462 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
463 k = session_stop(session, force);
471 void seat_evict_position(Seat *s, Session *session) {
473 unsigned int pos = session->pos;
480 if (pos < s->position_count && s->positions[pos] == session) {
481 s->positions[pos] = NULL;
483 /* There might be another session claiming the same
484 * position (eg., during gdm->session transition), so lets look
485 * for it and set it on the free slot. */
486 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
487 if (iter->pos == pos) {
488 s->positions[pos] = iter;
495 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
496 /* with VTs, the position is always the same as the VTnr */
500 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
503 seat_evict_position(s, session);
506 if (pos > 0 && !s->positions[pos])
507 s->positions[pos] = session;
510 static void seat_assign_position(Seat *s, Session *session) {
513 if (session->pos > 0)
516 for (pos = 1; pos < s->position_count; ++pos)
517 if (!s->positions[pos])
520 seat_claim_position(s, session, pos);
523 int seat_attach_session(Seat *s, Session *session) {
526 assert(!session->seat);
528 if (!seat_has_vts(s) != !session->vtnr)
532 LIST_PREPEND(sessions_by_seat, s->sessions, session);
533 seat_assign_position(s, session);
535 seat_send_changed(s, "Sessions", NULL);
537 /* On seats with VTs, the VT logic defines which session is active. On
538 * seats without VTs, we automatically activate new sessions. */
539 if (!seat_has_vts(s))
540 seat_set_active(s, session);
545 void seat_complete_switch(Seat *s) {
550 /* if no session-switch is pending or if it got canceled, do nothing */
551 if (!s->pending_switch)
554 session = s->pending_switch;
555 s->pending_switch = NULL;
557 seat_set_active(s, session);
560 bool seat_has_vts(Seat *s) {
563 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
566 bool seat_is_seat0(Seat *s) {
569 return s->manager->seat0 == s;
572 bool seat_can_multi_session(Seat *s) {
575 return seat_has_vts(s);
578 bool seat_can_tty(Seat *s) {
581 return seat_has_vts(s);
584 bool seat_has_master_device(Seat *s) {
587 /* device list is ordered by "master" flag */
588 return !!s->devices && s->devices->master;
591 bool seat_can_graphical(Seat *s) {
594 return seat_has_master_device(s);
597 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
599 bool idle_hint = true;
600 dual_timestamp ts = { 0, 0 };
604 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
608 ih = session_get_idle_hint(session, &k);
614 if (k.monotonic > ts.monotonic)
620 } else if (idle_hint) {
622 if (k.monotonic > ts.monotonic)
633 bool seat_check_gc(Seat *s, bool drop_not_started) {
636 if (drop_not_started && !s->started)
639 if (seat_is_seat0(s))
642 return seat_has_master_device(s);
645 void seat_add_to_gc_queue(Seat *s) {
651 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
652 s->in_gc_queue = true;
655 static bool seat_name_valid_char(char c) {
657 (c >= 'a' && c <= 'z') ||
658 (c >= 'A' && c <= 'Z') ||
659 (c >= '0' && c <= '9') ||
664 bool seat_name_is_valid(const char *name) {
669 if (!startswith(name, "seat"))
675 for (p = name; *p; p++)
676 if (!seat_name_valid_char(*p))
679 if (strlen(name) > 255)