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);
125 (unsigned long) 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("Failed to save seat data %s: %s", s->state_file, strerror(-r));
162 int seat_load(Seat *s) {
165 /* There isn't actually anything to read here ... */
170 static int vt_allocate(unsigned int vtnr) {
171 _cleanup_free_ char *p = NULL;
172 _cleanup_close_ int fd = -1;
176 if (asprintf(&p, "/dev/tty%u", vtnr) < 0)
179 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
186 int seat_preallocate_vts(Seat *s) {
193 log_debug("Preallocating VTs...");
195 if (s->manager->n_autovts <= 0)
198 if (!seat_has_vts(s))
201 for (i = 1; i <= s->manager->n_autovts; i++) {
206 log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
214 int seat_apply_acls(Seat *s, Session *old_active) {
219 r = devnode_acl_all(s->manager->udev,
222 !!old_active, old_active ? old_active->user->uid : 0,
223 !!s->active, s->active ? s->active->user->uid : 0);
226 log_error("Failed to apply ACLs: %s", strerror(-r));
231 int seat_set_active(Seat *s, Session *session) {
235 assert(!session || session->seat == s);
237 if (session == s->active)
240 old_active = s->active;
244 session_device_pause_all(old_active);
245 session_send_changed(old_active, "Active", NULL);
248 seat_apply_acls(s, old_active);
250 if (session && session->started) {
251 session_send_changed(session, "Active", NULL);
252 session_device_resume_all(session);
255 if (!session || session->started)
256 seat_send_changed(s, "ActiveSession", NULL);
261 session_save(session);
262 user_save(session->user);
266 session_save(old_active);
267 if (!session || session->user != old_active->user)
268 user_save(old_active->user);
274 int seat_switch_to(Seat *s, unsigned int num) {
275 /* Public session positions skip 0 (there is only F1-F12). Maybe it
276 * will get reassigned in the future, so return error for now. */
280 if (num >= s->position_count || !s->positions[num])
283 return session_activate(s->positions[num]);
286 int seat_switch_to_next(Seat *s) {
287 unsigned int start, i;
289 if (!s->position_count)
293 if (s->active && s->active->pos > 0)
294 start = s->active->pos;
296 for (i = start + 1; i < s->position_count; ++i)
298 return session_activate(s->positions[i]);
300 for (i = 1; i < start; ++i)
302 return session_activate(s->positions[i]);
307 int seat_switch_to_previous(Seat *s) {
308 unsigned int start, i;
310 if (!s->position_count)
314 if (s->active && s->active->pos > 0)
315 start = s->active->pos;
317 for (i = start - 1; i > 0; --i)
319 return session_activate(s->positions[i]);
321 for (i = s->position_count - 1; i > start; --i)
323 return session_activate(s->positions[i]);
328 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
329 Session *i, *new_active = NULL;
335 if (!seat_has_vts(s))
338 log_debug("VT changed to %u", vtnr);
340 LIST_FOREACH(sessions_by_seat, i, s->sessions)
341 if (i->vtnr == vtnr) {
346 r = seat_set_active(s, new_active);
347 manager_spawn_autovt(s->manager, vtnr);
352 int seat_read_active_vt(Seat *s) {
360 if (!seat_has_vts(s))
363 lseek(s->manager->console_active_fd, SEEK_SET, 0);
365 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
367 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
368 return k < 0 ? -errno : -EIO;
374 if (!startswith(t, "tty")) {
375 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
379 r = safe_atou(t+3, &vtnr);
381 log_error("Failed to parse VT number %s", t+3);
386 log_error("VT number invalid: %s", t+3);
390 return seat_active_vt_changed(s, vtnr);
393 int seat_start(Seat *s) {
400 MESSAGE_ID(SD_MESSAGE_SEAT_START),
402 "MESSAGE=New seat %s.", s->id,
405 /* Initialize VT magic stuff */
406 seat_preallocate_vts(s);
408 /* Read current VT */
409 seat_read_active_vt(s);
416 seat_send_signal(s, true);
421 int seat_stop(Seat *s, bool force) {
428 MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
430 "MESSAGE=Removed seat %s.", s->id,
433 seat_stop_sessions(s, force);
435 unlink(s->state_file);
436 seat_add_to_gc_queue(s);
439 seat_send_signal(s, false);
446 int seat_stop_sessions(Seat *s, bool force) {
452 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
453 k = session_stop(session, force);
461 void seat_evict_position(Seat *s, Session *session) {
462 unsigned int pos = session->pos;
469 if (pos < s->position_count && s->positions[pos] == session)
470 s->positions[pos] = NULL;
473 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
474 /* with VTs, the position is always the same as the VTnr */
478 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
481 seat_evict_position(s, session);
484 if (pos > 0 && !s->positions[pos])
485 s->positions[pos] = session;
488 static void seat_assign_position(Seat *s, Session *session) {
491 if (session->pos > 0)
494 for (pos = 1; pos < s->position_count; ++pos)
495 if (!s->positions[pos])
498 seat_claim_position(s, session, pos);
501 int seat_attach_session(Seat *s, Session *session) {
504 assert(!session->seat);
506 if (!seat_has_vts(s) != !session->vtnr)
510 LIST_PREPEND(sessions_by_seat, s->sessions, session);
511 seat_assign_position(s, session);
513 seat_send_changed(s, "Sessions", NULL);
515 /* On seats with VTs, the VT logic defines which session is active. On
516 * seats without VTs, we automatically activate new sessions. */
517 if (!seat_has_vts(s))
518 seat_set_active(s, session);
523 void seat_complete_switch(Seat *s) {
528 /* if no session-switch is pending or if it got canceled, do nothing */
529 if (!s->pending_switch)
532 session = s->pending_switch;
533 s->pending_switch = NULL;
535 seat_set_active(s, session);
538 bool seat_has_vts(Seat *s) {
541 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
544 bool seat_is_seat0(Seat *s) {
547 return s->manager->seat0 == s;
550 bool seat_can_multi_session(Seat *s) {
553 return seat_has_vts(s);
556 bool seat_can_tty(Seat *s) {
559 return seat_has_vts(s);
562 bool seat_has_master_device(Seat *s) {
565 /* device list is ordered by "master" flag */
566 return !!s->devices && s->devices->master;
569 bool seat_can_graphical(Seat *s) {
572 return seat_has_master_device(s);
575 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
577 bool idle_hint = true;
578 dual_timestamp ts = { 0, 0 };
582 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
586 ih = session_get_idle_hint(session, &k);
592 if (k.monotonic > ts.monotonic)
598 } else if (idle_hint) {
600 if (k.monotonic > ts.monotonic)
611 bool seat_check_gc(Seat *s, bool drop_not_started) {
614 if (drop_not_started && !s->started)
617 if (seat_is_seat0(s))
620 return seat_has_master_device(s);
623 void seat_add_to_gc_queue(Seat *s) {
629 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
630 s->in_gc_queue = true;
633 static bool seat_name_valid_char(char c) {
635 (c >= 'a' && c <= 'z') ||
636 (c >= 'A' && c <= 'Z') ||
637 (c >= '0' && c <= '9') ||
642 bool seat_name_is_valid(const char *name) {
647 if (!startswith(name, "seat"))
653 for (p = name; *p; p++)
654 if (!seat_name_valid_char(*p))
657 if (strlen(name) > 255)