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"
29 #include "alloc-util.h"
32 #include "formats-util.h"
33 #include "logind-acl.h"
34 #include "logind-seat.h"
36 #include "parse-util.h"
37 #include "stdio-util.h"
38 #include "string-util.h"
39 #include "terminal-util.h"
42 Seat *seat_new(Manager *m, const char *id) {
52 s->state_file = strappend("/run/systemd/seats/", id);
58 s->id = basename(s->state_file);
61 if (hashmap_put(m->seats, s->id, s) < 0) {
70 void seat_free(Seat *s) {
74 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
77 session_free(s->sessions);
82 device_free(s->devices);
84 hashmap_remove(s->manager->seats, s->id);
91 int seat_save(Seat *s) {
92 _cleanup_free_ char *temp_path = NULL;
93 _cleanup_fclose_ FILE *f = NULL;
101 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
105 r = fopen_temporary(s->state_file, &f, &temp_path);
109 fchmod(fileno(f), 0644);
112 "# This is private data. Do not parse.\n"
114 "CAN_MULTI_SESSION=%i\n"
116 "CAN_GRAPHICAL=%i\n",
118 seat_can_multi_session(s),
120 seat_can_graphical(s));
123 assert(s->active->user);
127 "ACTIVE_UID="UID_FMT"\n",
129 s->active->user->uid);
135 fputs("SESSIONS=", f);
136 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
140 i->sessions_by_seat_next ? ' ' : '\n');
144 LIST_FOREACH(sessions_by_seat, i, s->sessions)
148 i->sessions_by_seat_next ? ' ' : '\n');
151 r = fflush_and_check(f);
155 if (rename(temp_path, s->state_file) < 0) {
163 (void) unlink(s->state_file);
166 (void) unlink(temp_path);
168 return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
171 int seat_load(Seat *s) {
174 /* There isn't actually anything to read here ... */
179 #if 0 /// UNNEEDED by elogind
180 static int vt_allocate(unsigned int vtnr) {
181 char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
182 _cleanup_close_ int fd = -1;
186 xsprintf(p, "/dev/tty%u", vtnr);
187 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
194 int seat_preallocate_vts(Seat *s) {
201 log_debug("Preallocating VTs...");
203 if (s->manager->n_autovts <= 0)
206 if (!seat_has_vts(s))
209 for (i = 1; i <= s->manager->n_autovts; i++) {
214 log_error_errno(q, "Failed to preallocate VT %u: %m", i);
223 int seat_apply_acls(Seat *s, Session *old_active) {
228 r = devnode_acl_all(s->manager->udev,
231 !!old_active, old_active ? old_active->user->uid : 0,
232 !!s->active, s->active ? s->active->user->uid : 0);
235 log_error_errno(r, "Failed to apply ACLs: %m");
240 int seat_set_active(Seat *s, Session *session) {
244 assert(!session || session->seat == s);
246 if (session == s->active)
249 old_active = s->active;
253 session_device_pause_all(old_active);
254 session_send_changed(old_active, "Active", NULL);
257 seat_apply_acls(s, old_active);
259 if (session && session->started) {
260 session_send_changed(session, "Active", NULL);
261 session_device_resume_all(session);
264 if (!session || session->started)
265 seat_send_changed(s, "ActiveSession", NULL);
270 session_save(session);
271 user_save(session->user);
275 session_save(old_active);
276 if (!session || session->user != old_active->user)
277 user_save(old_active->user);
283 int seat_switch_to(Seat *s, unsigned int num) {
284 /* Public session positions skip 0 (there is only F1-F12). Maybe it
285 * will get reassigned in the future, so return error for now. */
289 if (num >= s->position_count || !s->positions[num]) {
290 /* allow switching to unused VTs to trigger auto-activate */
291 if (seat_has_vts(s) && num < 64)
297 return session_activate(s->positions[num]);
300 int seat_switch_to_next(Seat *s) {
301 unsigned int start, i;
303 if (s->position_count == 0)
307 if (s->active && s->active->position > 0)
308 start = s->active->position;
310 for (i = start + 1; i < s->position_count; ++i)
312 return session_activate(s->positions[i]);
314 for (i = 1; i < start; ++i)
316 return session_activate(s->positions[i]);
321 int seat_switch_to_previous(Seat *s) {
322 unsigned int start, i;
324 if (s->position_count == 0)
328 if (s->active && s->active->position > 0)
329 start = s->active->position;
331 for (i = start - 1; i > 0; --i)
333 return session_activate(s->positions[i]);
335 for (i = s->position_count - 1; i > start; --i)
337 return session_activate(s->positions[i]);
342 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
343 Session *i, *new_active = NULL;
349 if (!seat_has_vts(s))
352 log_debug("VT changed to %u", vtnr);
354 /* we might have earlier closing sessions on the same VT, so try to
355 * find a running one first */
356 LIST_FOREACH(sessions_by_seat, i, s->sessions)
357 if (i->vtnr == vtnr && !i->stopping) {
363 /* no running one? then we can't decide which one is the
364 * active one, let the first one win */
365 LIST_FOREACH(sessions_by_seat, i, s->sessions)
366 if (i->vtnr == vtnr) {
372 r = seat_set_active(s, new_active);
374 #if 0 /// elogind does not spawn autovt
375 manager_spawn_autovt(s->manager, vtnr);
381 int seat_read_active_vt(Seat *s) {
389 if (!seat_has_vts(s))
392 lseek(s->manager->console_active_fd, SEEK_SET, 0);
394 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
396 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
397 return k < 0 ? -errno : -EIO;
403 if (!startswith(t, "tty")) {
404 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
408 r = safe_atou(t+3, &vtnr);
410 log_error("Failed to parse VT number %s", t+3);
415 log_error("VT number invalid: %s", t+3);
419 return seat_active_vt_changed(s, vtnr);
422 int seat_start(Seat *s) {
429 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
431 LOG_MESSAGE("New seat %s.", s->id),
434 /* Initialize VT magic stuff */
435 #if 0 /// elogind does not support autospawning vts
436 seat_preallocate_vts(s);
439 /* Read current VT */
440 seat_read_active_vt(s);
447 seat_send_signal(s, true);
452 int seat_stop(Seat *s, bool force) {
459 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
461 LOG_MESSAGE("Removed seat %s.", s->id),
464 seat_stop_sessions(s, force);
466 unlink(s->state_file);
467 seat_add_to_gc_queue(s);
470 seat_send_signal(s, false);
477 int seat_stop_sessions(Seat *s, bool force) {
483 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
484 k = session_stop(session, force);
492 void seat_evict_position(Seat *s, Session *session) {
494 unsigned int pos = session->position;
496 session->position = 0;
501 if (pos < s->position_count && s->positions[pos] == session) {
502 s->positions[pos] = NULL;
504 /* There might be another session claiming the same
505 * position (eg., during gdm->session transition), so let's look
506 * for it and set it on the free slot. */
507 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
508 if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) {
509 s->positions[pos] = iter;
516 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
517 /* with VTs, the position is always the same as the VTnr */
521 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
524 seat_evict_position(s, session);
526 session->position = pos;
528 s->positions[pos] = session;
531 static void seat_assign_position(Seat *s, Session *session) {
534 if (session->position > 0)
537 for (pos = 1; pos < s->position_count; ++pos)
538 if (!s->positions[pos])
541 seat_claim_position(s, session, pos);
544 int seat_attach_session(Seat *s, Session *session) {
547 assert(!session->seat);
549 if (!seat_has_vts(s) != !session->vtnr)
553 LIST_PREPEND(sessions_by_seat, s->sessions, session);
554 seat_assign_position(s, session);
556 seat_send_changed(s, "Sessions", NULL);
558 /* On seats with VTs, the VT logic defines which session is active. On
559 * seats without VTs, we automatically activate new sessions. */
560 if (!seat_has_vts(s))
561 seat_set_active(s, session);
566 void seat_complete_switch(Seat *s) {
571 /* if no session-switch is pending or if it got canceled, do nothing */
572 if (!s->pending_switch)
575 session = s->pending_switch;
576 s->pending_switch = NULL;
578 seat_set_active(s, session);
581 bool seat_has_vts(Seat *s) {
584 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
587 bool seat_is_seat0(Seat *s) {
590 return s->manager->seat0 == s;
593 bool seat_can_multi_session(Seat *s) {
596 return seat_has_vts(s);
599 bool seat_can_tty(Seat *s) {
602 return seat_has_vts(s);
605 bool seat_has_master_device(Seat *s) {
608 /* device list is ordered by "master" flag */
609 return !!s->devices && s->devices->master;
612 bool seat_can_graphical(Seat *s) {
615 return seat_has_master_device(s);
618 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
620 bool idle_hint = true;
621 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
625 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
629 ih = session_get_idle_hint(session, &k);
635 if (k.monotonic > ts.monotonic)
641 } else if (idle_hint) {
643 if (k.monotonic > ts.monotonic)
654 bool seat_check_gc(Seat *s, bool drop_not_started) {
657 if (drop_not_started && !s->started)
660 if (seat_is_seat0(s))
663 return seat_has_master_device(s);
666 void seat_add_to_gc_queue(Seat *s) {
672 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
673 s->in_gc_queue = true;
676 static bool seat_name_valid_char(char c) {
678 (c >= 'a' && c <= 'z') ||
679 (c >= 'A' && c <= 'Z') ||
680 (c >= '0' && c <= '9') ||
685 bool seat_name_is_valid(const char *name) {
690 if (!startswith(name, "seat"))
696 for (p = name; *p; p++)
697 if (!seat_name_valid_char(*p))
700 if (strlen(name) > 255)