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_apply_acls(Seat *s, Session *old_active) {
184 r = devnode_acl_all(s->manager->udev,
187 !!old_active, old_active ? old_active->user->uid : 0,
188 !!s->active, s->active ? s->active->user->uid : 0);
191 log_error_errno(r, "Failed to apply ACLs: %m");
196 int seat_set_active(Seat *s, Session *session) {
200 assert(!session || session->seat == s);
202 if (session == s->active)
205 old_active = s->active;
209 session_device_pause_all(old_active);
210 session_send_changed(old_active, "Active", NULL);
213 seat_apply_acls(s, old_active);
215 if (session && session->started) {
216 session_send_changed(session, "Active", NULL);
217 session_device_resume_all(session);
220 if (!session || session->started)
221 seat_send_changed(s, "ActiveSession", NULL);
226 session_save(session);
227 user_save(session->user);
231 session_save(old_active);
232 if (!session || session->user != old_active->user)
233 user_save(old_active->user);
239 int seat_switch_to(Seat *s, unsigned int num) {
240 /* Public session positions skip 0 (there is only F1-F12). Maybe it
241 * will get reassigned in the future, so return error for now. */
245 if (num >= s->position_count || !s->positions[num]) {
246 /* allow switching to unused VTs to trigger auto-activate */
247 if (seat_has_vts(s) && num < 64)
253 return session_activate(s->positions[num]);
256 int seat_switch_to_next(Seat *s) {
257 unsigned int start, i;
259 if (!s->position_count)
263 if (s->active && s->active->pos > 0)
264 start = s->active->pos;
266 for (i = start + 1; i < s->position_count; ++i)
268 return session_activate(s->positions[i]);
270 for (i = 1; i < start; ++i)
272 return session_activate(s->positions[i]);
277 int seat_switch_to_previous(Seat *s) {
278 unsigned int start, i;
280 if (!s->position_count)
284 if (s->active && s->active->pos > 0)
285 start = s->active->pos;
287 for (i = start - 1; i > 0; --i)
289 return session_activate(s->positions[i]);
291 for (i = s->position_count - 1; i > start; --i)
293 return session_activate(s->positions[i]);
298 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
299 Session *i, *new_active = NULL;
305 if (!seat_has_vts(s))
308 log_debug("VT changed to %u", vtnr);
310 /* we might have earlier closing sessions on the same VT, so try to
311 * find a running one first */
312 LIST_FOREACH(sessions_by_seat, i, s->sessions)
313 if (i->vtnr == vtnr && !i->stopping) {
319 /* no running one? then we can't decide which one is the
320 * active one, let the first one win */
321 LIST_FOREACH(sessions_by_seat, i, s->sessions)
322 if (i->vtnr == vtnr) {
328 r = seat_set_active(s, new_active);
333 int seat_read_active_vt(Seat *s) {
341 if (!seat_has_vts(s))
344 lseek(s->manager->console_active_fd, SEEK_SET, 0);
346 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
348 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
349 return k < 0 ? -errno : -EIO;
355 if (!startswith(t, "tty")) {
356 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
360 r = safe_atou(t+3, &vtnr);
362 log_error("Failed to parse VT number %s", t+3);
367 log_error("VT number invalid: %s", t+3);
371 return seat_active_vt_changed(s, vtnr);
374 int seat_start(Seat *s) {
381 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
383 LOG_MESSAGE("New seat %s.", s->id),
386 /* Read current VT */
387 seat_read_active_vt(s);
394 seat_send_signal(s, true);
399 int seat_stop(Seat *s, bool force) {
406 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
408 LOG_MESSAGE("Removed seat %s.", s->id),
411 seat_stop_sessions(s, force);
413 unlink(s->state_file);
414 seat_add_to_gc_queue(s);
417 seat_send_signal(s, false);
424 int seat_stop_sessions(Seat *s, bool force) {
430 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
431 k = session_stop(session, force);
439 void seat_evict_position(Seat *s, Session *session) {
441 unsigned int pos = session->pos;
448 if (pos < s->position_count && s->positions[pos] == session) {
449 s->positions[pos] = NULL;
451 /* There might be another session claiming the same
452 * position (eg., during gdm->session transition), so lets look
453 * for it and set it on the free slot. */
454 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
455 if (iter->pos == pos) {
456 s->positions[pos] = iter;
463 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
464 /* with VTs, the position is always the same as the VTnr */
468 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
471 seat_evict_position(s, session);
474 if (pos > 0 && !s->positions[pos])
475 s->positions[pos] = session;
478 static void seat_assign_position(Seat *s, Session *session) {
481 if (session->pos > 0)
484 for (pos = 1; pos < s->position_count; ++pos)
485 if (!s->positions[pos])
488 seat_claim_position(s, session, pos);
491 int seat_attach_session(Seat *s, Session *session) {
494 assert(!session->seat);
496 if (!seat_has_vts(s) != !session->vtnr)
500 LIST_PREPEND(sessions_by_seat, s->sessions, session);
501 seat_assign_position(s, session);
503 seat_send_changed(s, "Sessions", NULL);
505 /* On seats with VTs, the VT logic defines which session is active. On
506 * seats without VTs, we automatically activate new sessions. */
507 if (!seat_has_vts(s))
508 seat_set_active(s, session);
513 void seat_complete_switch(Seat *s) {
518 /* if no session-switch is pending or if it got canceled, do nothing */
519 if (!s->pending_switch)
522 session = s->pending_switch;
523 s->pending_switch = NULL;
525 seat_set_active(s, session);
528 bool seat_has_vts(Seat *s) {
531 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
534 bool seat_is_seat0(Seat *s) {
537 return s->manager->seat0 == s;
540 bool seat_can_multi_session(Seat *s) {
543 return seat_has_vts(s);
546 bool seat_can_tty(Seat *s) {
549 return seat_has_vts(s);
552 bool seat_has_master_device(Seat *s) {
555 /* device list is ordered by "master" flag */
556 return !!s->devices && s->devices->master;
559 bool seat_can_graphical(Seat *s) {
562 return seat_has_master_device(s);
565 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
567 bool idle_hint = true;
568 dual_timestamp ts = { 0, 0 };
572 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
576 ih = session_get_idle_hint(session, &k);
582 if (k.monotonic > ts.monotonic)
588 } else if (idle_hint) {
590 if (k.monotonic > ts.monotonic)
601 bool seat_check_gc(Seat *s, bool drop_not_started) {
604 if (drop_not_started && !s->started)
607 if (seat_is_seat0(s))
610 return seat_has_master_device(s);
613 void seat_add_to_gc_queue(Seat *s) {
619 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
620 s->in_gc_queue = true;
623 static bool seat_name_valid_char(char c) {
625 (c >= 'a' && c <= 'z') ||
626 (c >= 'A' && c <= 'Z') ||
627 (c >= '0' && c <= '9') ||
632 bool seat_name_is_valid(const char *name) {
637 if (!startswith(name, "seat"))
643 for (p = name; *p; p++)
644 if (!seat_name_valid_char(*p))
647 if (strlen(name) > 255)