diff options
author | Lennart Poettering <lennart@poettering.net> | 2011-05-23 23:55:06 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2011-06-21 19:29:44 +0200 |
commit | 202630822f52e06dce8404633407329c38099278 (patch) | |
tree | 3cc32d83d3659b4b182af66bad5ee877be95d90a /src/logind-session.c | |
parent | f41607a6e10202003efe307eab975e8aca37d3de (diff) |
logind: first version that compiles fine
Diffstat (limited to 'src/logind-session.c')
-rw-r--r-- | src/logind-session.c | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/src/logind-session.c b/src/logind-session.c new file mode 100644 index 0000000000..9af99d054a --- /dev/null +++ b/src/logind-session.c @@ -0,0 +1,472 @@ +/*-*- 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 <errno.h> +#include <string.h> +#include <unistd.h> + +#include "logind.h" +#include "strv.h" +#include "util.h" +#include "cgroup-util.h" + +Session* session_new(Manager *m, User *u, const char *id) { + Session *s; + + assert(m); + assert(id); + + s = new(Session, 1); + if (!s) + return NULL; + + s->state_file = strappend("/run/systemd/session/", id); + if (!s->state_file) { + free(s); + return NULL; + } + + s->id = file_name_from_path(s->state_file); + + if (hashmap_put(m->sessions, s->id, s) < 0) { + free(s->id); + free(s); + return NULL; + } + + s->manager = m; + s->pipe_fd = -1; + s->user = u; + + dual_timestamp_get(&s->timestamp); + + return s; +} + +void session_free(Session *s) { + assert(s); + + if (s->user) { + LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s); + + if (s->user->display == s) + s->user->display = NULL; + } + + if (s->seat) + LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s); + + free(s->cgroup_path); + strv_free(s->controllers); + + free(s->tty); + free(s->display); + free(s->remote_host); + + hashmap_remove(s->manager->sessions, s->id); + + free(s->state_file); + free(s); +} + +int session_save(Session *s) { + FILE *f; + int r = 0; + + assert(s); + + r = safe_mkdir("/run/systemd/session", 0755, 0, 0); + if (r < 0) + return r; + + f = fopen(s->state_file, "we"); + if (!f) + return -errno; + + assert(s->user); + + fprintf(f, + "# This is private data. Do not parse.\n" + "UID=%lu\n" + "USER=%s\n" + "ACTIVE=%i\n" + "REMOTE=%i\n" + "KILL_PROCESSES=%i\n", + (unsigned long) s->user->uid, + s->user->name, + session_is_active(s), + s->remote, + s->kill_processes); + + if (s->cgroup_path) + fprintf(f, + "CGROUP=%s\n", + s->cgroup_path); + + if (s->seat) + fprintf(f, + "SEAT=%s\n", + s->seat->id); + + if (s->tty) + fprintf(f, + "TTY=%s\n", + s->tty); + + if (s->display) + fprintf(f, + "DISPLAY=%s\n", + s->display); + + if (s->remote_host) + fprintf(f, + "REMOTE_HOST=%s\n", + s->remote_host); + + if (s->seat && s->seat->manager->vtconsole == s->seat) + fprintf(f, + "VTNR=%i\n", + s->vtnr); + + if (s->leader > 0) + fprintf(f, + "LEADER=%lu\n", + (unsigned long) s->leader); + + if (s->audit_id > 0) + fprintf(f, + "AUDIT=%llu\n", + (unsigned long long) s->audit_id); + + fflush(f); + if (ferror(f)) { + r = -errno; + unlink(s->state_file); + } + + fclose(f); + return r; +} + +int session_load(Session *s) { + assert(s); + + return 0; +} + +int session_activate(Session *s) { + int r; + + assert(s); + + if (s->vtnr < 0) + return -ENOTSUP; + + if (!s->seat) + return -ENOTSUP; + + if (s->seat->active == s) + return 0; + + assert(s->manager->vtconsole == s->seat); + + r = chvt(s->vtnr); + if (r < 0) + return r; + + s->seat->active = s; + + return seat_apply_acls(s->seat); +} + +bool x11_display_is_local(const char *display) { + assert(display); + + return + display[0] == ':' && + display[1] >= '0' && + display[1] <= '9'; +} + +static int session_link_x11_socket(Session *s) { + char *t, *f, *c; + size_t k; + + assert(s); + assert(s->user); + assert(s->user->runtime_path); + + if (s->user->display) + return 0; + + if (!s->display || !x11_display_is_local(s->display)) + return 0; + + k = strspn(s->display+1, "0123456789"); + f = new(char, sizeof("/tmp/.X11-unix/X") + k); + if (!f) { + log_error("Out of memory"); + return -ENOMEM; + } + + c = stpcpy(f, "/tmp/.X11-unix/X"); + memcpy(c, s->display+1, k); + c[k] = 0; + + if (access(f, F_OK) < 0) { + log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f); + free(f); + return -ENOENT; + } + + t = strappend(s->user->runtime_path, "/display"); + if (!t) { + log_error("Out of memory"); + free(f); + return -ENOMEM; + } + + if (link(f, t) < 0) { + if (errno == EEXIST) { + unlink(t); + + if (link(f, t) >= 0) + goto done; + } + + if (symlink(f, t) < 0) { + + if (errno == EEXIST) { + unlink(t); + + if (symlink(f, t) >= 0) + goto done; + } + + log_error("Failed to link %s to %s: %m", f, t); + free(f); + free(t); + return -errno; + } + } + +done: + log_info("Linked %s to %s.", f, t); + free(f); + free(t); + + s->user->display = s; + + return 0; +} + +static int session_create_cgroup(Session *s) { + char **k; + char *p; + int r; + + assert(s); + assert(s->user); + assert(s->user->cgroup_path); + + if (!s->cgroup_path) { + if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) { + log_error("Out of memory"); + return -ENOMEM; + } + } else + p = s->cgroup_path; + + if (s->leader > 0) + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, p, s->leader); + else + r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p); + + if (r < 0) { + free(p); + s->cgroup_path = NULL; + log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r)); + return r; + } + + s->cgroup_path = p; + + STRV_FOREACH(k, s->manager->controllers) { + if (s->leader > 0) + r = cg_create_and_attach(*k, p, s->leader); + else + r = cg_create(*k, p); + + if (r < 0) + log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r)); + } + + return 0; +} + +int session_start(Session *s) { + int r; + + assert(s); + assert(s->user); + + /* Create user first */ + r = user_start(s->user); + if (r < 0) + return r; + + /* Create cgroup */ + r = session_create_cgroup(s); + if (r < 0) + return r; + + /* Create X11 symlink */ + session_link_x11_socket(s); + return 0; +} + +static bool session_shall_kill(Session *s) { + assert(s); + + return s->kill_processes; +} + +static int session_kill_cgroup(Session *s) { + int r; + char **k; + + assert(s); + + if (!s->cgroup_path) + return 0; + + cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false); + + if (session_shall_kill(s)) { + + r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true); + if (r < 0) + log_error("Failed to kill session cgroup: %s", strerror(-r)); + + } else { + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true); + if (r < 0) + log_error("Failed to check session cgroup: %s", strerror(-r)); + else if (r > 0) { + r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path); + if (r < 0) + log_error("Failed to delete session cgroup: %s", strerror(-r)); + } else + r = -EBUSY; + } + + STRV_FOREACH(k, s->user->manager->controllers) + cg_trim(*k, s->cgroup_path, true); + + free(s->cgroup_path); + s->cgroup_path = NULL; + + return r; +} + +static int session_unlink_x11_socket(Session *s) { + char *t; + int r; + + assert(s); + assert(s->user); + + if (s->user->display != s) + return 0; + + s->user->display = NULL; + + t = strappend(s->user->runtime_path, "/display"); + if (!t) { + log_error("Out of memory"); + return -ENOMEM; + } + + r = unlink(t); + free(t); + + return r < 0 ? -errno : 0; +} + +int session_stop(Session *s) { + int r = 0, k; + + assert(s); + + /* Kill cgroup */ + k = session_kill_cgroup(s); + if (k < 0) + r = k; + + /* Remove X11 symlink */ + session_unlink_x11_socket(s); + + return r; +} + +bool session_is_active(Session *s) { + assert(s); + + if (!s->seat) + return true; + + return s->seat->active == s; +} + +int session_check_gc(Session *s) { + int r; + + assert(s); + + if (s->pipe_fd >= 0) { + + r = pipe_eof(s->pipe_fd); + if (r < 0) + return r; + + if (r <= 0) + return 1; + } + + if (s->cgroup_path) { + + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false); + if (r < 0) + return r; + + if (r <= 0) + return 1; + } + + return 0; +} + +static const char* const session_type_table[_SESSION_TYPE_MAX] = { + [SESSION_TERMINAL] = "terminal", + [SESSION_X11] = "x11" +}; + +DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); |