diff options
author | Lennart Poettering <lennart@poettering.net> | 2011-12-31 03:16:08 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2011-12-31 03:16:08 +0100 |
commit | 4bba9156da3e1df2cee24d10d7cd88c776ef4179 (patch) | |
tree | d149a225807351578df3b1567fab3585b4439677 /src/login/logind-seat.c | |
parent | fc5e60ee0c64343d1bd08c343275fc1ceff445aa (diff) |
logind: move logind into its own subdirectory
Diffstat (limited to 'src/login/logind-seat.c')
-rw-r--r-- | src/login/logind-seat.c | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c new file mode 100644 index 0000000000..3cf3958c8d --- /dev/null +++ b/src/login/logind-seat.c @@ -0,0 +1,499 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/vt.h> +#include <string.h> + +#include "logind-seat.h" +#include "logind-acl.h" +#include "util.h" + +Seat *seat_new(Manager *m, const char *id) { + Seat *s; + + assert(m); + assert(id); + + s = new0(Seat, 1); + if (!s) + return NULL; + + s->state_file = strappend("/run/systemd/seats/", id); + if (!s->state_file) { + free(s); + return NULL; + } + + s->id = file_name_from_path(s->state_file); + s->manager = m; + + if (hashmap_put(m->seats, s->id, s) < 0) { + free(s->state_file); + free(s); + return NULL; + } + + return s; +} + +void seat_free(Seat *s) { + assert(s); + + if (s->in_gc_queue) + LIST_REMOVE(Seat, gc_queue, s->manager->seat_gc_queue, s); + + while (s->sessions) + session_free(s->sessions); + + assert(!s->active); + + while (s->devices) + device_free(s->devices); + + hashmap_remove(s->manager->seats, s->id); + + free(s->state_file); + free(s); +} + +int seat_save(Seat *s) { + int r; + FILE *f; + char *temp_path; + + assert(s); + + if (!s->started) + return 0; + + r = safe_mkdir("/run/systemd/seats", 0755, 0, 0); + if (r < 0) + goto finish; + + r = fopen_temporary(s->state_file, &f, &temp_path); + if (r < 0) + goto finish; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "IS_VTCONSOLE=%i\n", + seat_is_vtconsole(s)); + + if (s->active) { + assert(s->active->user); + + fprintf(f, + "ACTIVE=%s\n" + "ACTIVE_UID=%lu\n", + s->active->id, + (unsigned long) s->active->user->uid); + } + + if (s->sessions) { + Session *i; + + fputs("SESSIONS=", f); + LIST_FOREACH(sessions_by_seat, i, s->sessions) { + fprintf(f, + "%s%c", + i->id, + i->sessions_by_seat_next ? ' ' : '\n'); + } + + fputs("UIDS=", f); + LIST_FOREACH(sessions_by_seat, i, s->sessions) + fprintf(f, + "%lu%c", + (unsigned long) i->user->uid, + i->sessions_by_seat_next ? ' ' : '\n'); + } + + fflush(f); + + if (ferror(f) || rename(temp_path, s->state_file) < 0) { + r = -errno; + unlink(s->state_file); + unlink(temp_path); + } + + fclose(f); + free(temp_path); + +finish: + if (r < 0) + log_error("Failed to save seat data for %s: %s", s->id, strerror(-r)); + + return r; +} + +int seat_load(Seat *s) { + assert(s); + + /* There isn't actually anything to read here ... */ + + return 0; +} + +static int vt_allocate(int vtnr) { + int fd, r; + char *p; + + assert(vtnr >= 1); + + if (asprintf(&p, "/dev/tty%i", vtnr) < 0) + return -ENOMEM; + + fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + free(p); + + r = fd < 0 ? -errno : 0; + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} + +int seat_preallocate_vts(Seat *s) { + int r = 0; + unsigned i; + + assert(s); + assert(s->manager); + + log_debug("Preallocating VTs..."); + + if (s->manager->n_autovts <= 0) + return 0; + + if (!seat_is_vtconsole(s)) + return 0; + + for (i = 1; i <= s->manager->n_autovts; i++) { + int q; + + q = vt_allocate(i); + if (q < 0) { + log_error("Failed to preallocate VT %i: %s", i, strerror(-q)); + r = q; + } + } + + return r; +} + +int seat_apply_acls(Seat *s, Session *old_active) { + int r; + + assert(s); + + r = devnode_acl_all(s->manager->udev, + s->id, + false, + !!old_active, old_active ? old_active->user->uid : 0, + !!s->active, s->active ? s->active->user->uid : 0); + + if (r < 0) + log_error("Failed to apply ACLs: %s", strerror(-r)); + + return r; +} + +int seat_set_active(Seat *s, Session *session) { + Session *old_active; + + assert(s); + assert(!session || session->seat == s); + + if (session == s->active) + return 0; + + old_active = s->active; + s->active = session; + + seat_apply_acls(s, old_active); + + if (session && session->started) + session_send_changed(session, "Active\0"); + + if (!session || session->started) + seat_send_changed(s, "ActiveSession\0"); + + seat_save(s); + + if (session) { + session_save(session); + user_save(session->user); + } + + if (old_active) { + session_save(old_active); + user_save(old_active->user); + } + + return 0; +} + +int seat_active_vt_changed(Seat *s, int vtnr) { + Session *i, *new_active = NULL; + int r; + + assert(s); + assert(vtnr >= 1); + + if (!seat_is_vtconsole(s)) + return -EINVAL; + + log_debug("VT changed to %i", vtnr); + + LIST_FOREACH(sessions_by_seat, i, s->sessions) + if (i->vtnr == vtnr) { + new_active = i; + break; + } + + r = seat_set_active(s, new_active); + manager_spawn_autovt(s->manager, vtnr); + + return r; +} + +int seat_read_active_vt(Seat *s) { + char t[64]; + ssize_t k; + int r, vtnr; + + assert(s); + + if (!seat_is_vtconsole(s)) + return 0; + + lseek(s->manager->console_active_fd, SEEK_SET, 0); + + k = read(s->manager->console_active_fd, t, sizeof(t)-1); + if (k <= 0) { + log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF"); + return k < 0 ? -errno : -EIO; + } + + t[k] = 0; + truncate_nl(t); + + if (!startswith(t, "tty")) { + log_error("Hm, /sys/class/tty/tty0/active is badly formatted."); + return -EIO; + } + + r = safe_atoi(t+3, &vtnr); + if (r < 0) { + log_error("Failed to parse VT number %s", t+3); + return r; + } + + if (vtnr <= 0) { + log_error("VT number invalid: %s", t+3); + return -EIO; + } + + return seat_active_vt_changed(s, vtnr); +} + +int seat_start(Seat *s) { + assert(s); + + if (s->started) + return 0; + + log_info("New seat %s.", s->id); + + /* Initialize VT magic stuff */ + seat_preallocate_vts(s); + + /* Read current VT */ + seat_read_active_vt(s); + + s->started = true; + + /* Save seat data */ + seat_save(s); + + seat_send_signal(s, true); + + return 0; +} + +int seat_stop(Seat *s) { + int r = 0; + + assert(s); + + if (s->started) + log_info("Removed seat %s.", s->id); + + seat_stop_sessions(s); + + unlink(s->state_file); + seat_add_to_gc_queue(s); + + if (s->started) + seat_send_signal(s, false); + + s->started = false; + + return r; +} + +int seat_stop_sessions(Seat *s) { + Session *session; + int r = 0, k; + + assert(s); + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + k = session_stop(session); + if (k < 0) + r = k; + } + + return r; +} + +int seat_attach_session(Seat *s, Session *session) { + assert(s); + assert(session); + assert(!session->seat); + + if (!seat_is_vtconsole(s) && s->sessions) + return -EEXIST; + + session->seat = s; + LIST_PREPEND(Session, sessions_by_seat, s->sessions, session); + + seat_send_changed(s, "Sessions\0"); + + if (!seat_is_vtconsole(s)) { + assert(!s->active); + seat_set_active(s, session); + } + + return 0; +} + +bool seat_is_vtconsole(Seat *s) { + assert(s); + + return s->manager->vtconsole == s; +} + +int seat_get_idle_hint(Seat *s, dual_timestamp *t) { + Session *session; + bool idle_hint = true; + dual_timestamp ts = { 0, 0 }; + + assert(s); + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + dual_timestamp k; + int ih; + + ih = session_get_idle_hint(session, &k); + if (ih < 0) + return ih; + + if (!ih) { + if (!idle_hint) { + if (k.monotonic < ts.monotonic) + ts = k; + } else { + idle_hint = false; + ts = k; + } + } else if (idle_hint) { + + if (k.monotonic > ts.monotonic) + ts = k; + } + } + + if (t) + *t = ts; + + return idle_hint; +} + +int seat_check_gc(Seat *s, bool drop_not_started) { + assert(s); + + if (drop_not_started && !s->started) + return 0; + + if (seat_is_vtconsole(s)) + return 1; + + return !!s->devices; +} + +void seat_add_to_gc_queue(Seat *s) { + assert(s); + + if (s->in_gc_queue) + return; + + LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s); + s->in_gc_queue = true; +} + +static bool seat_name_valid_char(char c) { + return + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '-' || + c == '_'; +} + +bool seat_name_is_valid(const char *name) { + const char *p; + + assert(name); + + if (!startswith(name, "seat")) + return false; + + if (!name[4]) + return false; + + for (p = name; *p; p++) + if (!seat_name_valid_char(*p)) + return false; + + if (strlen(name) > 255) + return false; + + return true; +} |