logind-session.c revision a32360f1a5a85c12f00e9dfb7353280067cccb5b
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt This file is part of systemd.
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt Copyright 2011 Lennart Poettering
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt systemd is free software; you can redistribute it and/or modify it
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt under the terms of the GNU Lesser General Public License as published by
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt the Free Software Foundation; either version 2.1 of the License, or
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt (at your option) any later version.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt systemd is distributed in the hope that it will be useful, but
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt WITHOUT ANY WARRANTY; without even the implied warranty of
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt Lesser General Public License for more details.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt You should have received a copy of the GNU Lesser General Public License
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt along with systemd; If not, see <http://www.gnu.org/licenses/>.
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom GundersenSession* session_new(Manager *m, User *u, const char *id) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt s->state_file = strappend("/run/systemd/sessions/", id);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt hashmap_remove(s->manager->session_cgroups, s->cgroup_path);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = fopen_temporary(s->state_file, &f, &temp_path);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "# This is private data. Do not parse.\n"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "ACTIVE=%i\n"
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams "STATE=%s\n"
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams "REMOTE=%i\n"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "KILL_PROCESSES=%i\n",
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams if (s->type >= 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "TYPE=%s\n",
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams "CLASS=%s\n",
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "CGROUP=%s\n",
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen "DISPLAY=%s\n",
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen "REMOTE_HOST=%s\n",
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen "REMOTE_USER=%s\n",
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen "SERVICE=%s\n",
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen if (s->seat && seat_can_multi_session(s->seat))
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams "LEADER=%lu\n",
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams (unsigned long) s->leader);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt "AUDIT=%llu\n",
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt (unsigned long long) s->audit_id);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (ferror(f) || rename(temp_path, s->state_file) < 0) {
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt log_error("Failed to save session data for %s: %s", s->id, strerror(-r));
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmek if (vtnr && s->seat && seat_can_multi_session(s->seat)) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (k >= 0 && v >= 1)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt audit_session_from_pid(s->leader, &s->audit_id);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt /* If we open an unopened pipe for reading we will not
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt get an EOF. to trigger an EOF we hence open it for
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt reading, but close it right-away which then will
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt trigger the EOF. */
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flyktstatic int session_link_x11_socket(Session *s) {
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt char *t, *f, *c;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (!s->display || !display_is_local(s->display))
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_warning("Session %s has display %s with non-existing socket %s.", s->id, s->display, f);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt /* Note that this cannot be in a subdir to avoid
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt * vulnerabilities since we are privileged but the runtime
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt * path is owned by the user */
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller t = strappend(s->user->runtime_path, "/X11-display");
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (link(f, t) < 0) {
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (link(f, t) >= 0)
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (symlink(f, t) < 0) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (symlink(f, t) >= 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_error("Failed to link %s to %s: %m", f, t);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic int session_create_one_group(Session *s, const char *controller, const char *path) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (s->leader > 0) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt r = cg_create_and_attach(controller, path, s->leader);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0)
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (r < 0) {
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
5e91345094a9e983e7abb2313334e7808bcd2cc2Tom Gundersen log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt strv_contains(s->manager->reset_controllers, *k) ||
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (s->leader > 0) {
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt STRV_FOREACH(k, s->manager->reset_controllers) {
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = hashmap_put(s->manager->session_cgroups, s->cgroup_path, s);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt log_warning("Failed to create mapping between cgroup and session");
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt "MESSAGE=New session %s of user %s.", s->id, s->user->name,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* Create cgroup */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* Create X11 symlink */
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt /* Save session data */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return false;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (strv_contains(s->manager->kill_exclude_users, s->user->name))
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return false;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return true;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return strv_contains(s->manager->kill_only_users, s->user->name);
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersenstatic int session_terminate_cgroup(Session *s) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_error("Failed to kill session cgroup: %s", strerror(-r));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (s->leader > 0) {
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen /* We still send a HUP to the leader process,
356779df90a2ecab5da2cb310ad0f8ebc9ca9f46Lennart Poettering * even if we are not supposed to kill the
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen * whole cgroup. But let's first check the
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen * leader still exists and belongs to our
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt * session... */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = manager_get_session_by_pid(s->manager, s->leader, &t);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r > 0 && t == s) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt kill(s->leader, SIGTERM); /* for normal processes */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt kill(s->leader, SIGCONT); /* in case they are stopped */
cfb5b3805759e63dc5e0cae6e92e1df885b5c5b6Tom Gundersen r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt log_error("Failed to check session cgroup: %s", strerror(-r));
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt else if (r > 0) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt log_error("Failed to delete session cgroup: %s", strerror(-r));
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt hashmap_remove(s->manager->session_cgroups, s->cgroup_path);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int session_unlink_x11_socket(Session *s) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt t = strappend(s->user->runtime_path, "/X11-display");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return r < 0 ? -errno : 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt int r = 0, k;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Kill cgroup */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Remove X11 symlink */
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return true;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int get_tty_atime(const char *tty, usec_t *atime) {
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flyktstatic int get_process_ctty_atime(pid_t pid, usec_t *atime) {
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flyktint session_get_idle_hint(Session *s, dual_timestamp *t) {
41e4615d4f4f5c61afa84ba857f23c0ac496687bPatrik Flykt /* Explicit idle hint is set */
c47e8936a43ce546e8a74fa569e9fbfae6c64be7Patrik Flykt /* Graphical sessions should really implement a real
c47e8936a43ce546e8a74fa569e9fbfae6c64be7Patrik Flykt * idle hint logic */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* For sessions with an explicitly configured tty, let's check
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt * its atime */
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt /* For sessions with a leader but no explicitly configured
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt * tty, let's check the controlling tty of the leader */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (s->leader > 0) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* For other TTY sessions, let's find the most recent atime of
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * the ttys of any of the processes of the session */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &f) >= 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return atime + s->manager->idle_action_usec <= n;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktvoid session_set_idle_hint(Session *s, bool b) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "IdleHint\0"
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "IdleSinceHint\0"
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "IdleSinceHintMonotonic\0");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "IdleHint\0"
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "IdleSinceHint\0"
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt "IdleSinceHintMonotonic\0");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "IdleHint\0"
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "IdleSinceHint\0"
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "IdleSinceHintMonotonic\0");
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt "IdleHint\0"
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt "IdleSinceHint\0"
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt "IdleSinceHintMonotonic\0");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Create FIFO */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Open reading side */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (s->fifo_fd < 0) {
6ec60d20724d2a32e20d25ef75d2af178c242bc2Ronny Chevalier s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Open writing side */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (s->fifo_fd >= 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flyktint session_check_gc(Session *s, bool drop_not_started) {
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt if (s->fifo_fd >= 0) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktint session_kill(Session *s, KillWho who, int signo) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt pid_set = set_new(trivial_hash_func, trivial_compare_func);
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen if (s->leader > 0) {
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, signo, false, true, false, pid_set);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic const char* const session_state_table[_SESSION_TYPE_MAX] = {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik FlyktDEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic const char* const session_type_table[_SESSION_TYPE_MAX] = {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik FlyktDEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic const char* const session_class_table[_SESSION_CLASS_MAX] = {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik FlyktDEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic const char* const kill_who_table[_KILL_WHO_MAX] = {