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/>.
25 #include <sys/epoll.h>
31 #include "cgroup-util.h"
32 #include "logind-session.h"
34 #define IDLE_THRESHOLD_USEC (5*USEC_PER_MINUTE)
36 Session* session_new(Manager *m, User *u, const char *id) {
46 s->state_file = strappend("/run/systemd/sessions/", id);
52 s->id = file_name_from_path(s->state_file);
54 if (hashmap_put(m->sessions, s->id, s) < 0) {
64 LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
69 void session_free(Session *s) {
73 LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s);
76 LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
78 if (s->user->display == s)
79 s->user->display = NULL;
83 if (s->seat->active == s)
84 s->seat->active = NULL;
86 LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
90 hashmap_remove(s->manager->cgroups, s->cgroup_path);
93 strv_free(s->controllers);
101 hashmap_remove(s->manager->sessions, s->id);
102 session_remove_fifo(s);
108 int session_save(Session *s) {
118 r = safe_mkdir("/run/systemd/sessions", 0755, 0, 0);
122 r = fopen_temporary(s->state_file, &f, &temp_path);
128 fchmod(fileno(f), 0644);
131 "# This is private data. Do not parse.\n"
136 "KILL_PROCESSES=%i\n",
137 (unsigned long) s->user->uid,
139 session_is_active(s),
146 session_type_to_string(s->type));
151 session_class_to_string(s->class));
193 if (s->seat && seat_can_multi_session(s->seat))
201 (unsigned long) s->leader);
206 (unsigned long long) s->audit_id);
210 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
212 unlink(s->state_file);
221 log_error("Failed to save session data for %s: %s", s->id, strerror(-r));
226 int session_load(Session *s) {
228 *kill_processes = NULL,
240 r = parse_env_file(s->state_file, NEWLINE,
242 "KILL_PROCESSES", &kill_processes,
243 "CGROUP", &s->cgroup_path,
244 "FIFO", &s->fifo_path,
247 "DISPLAY", &s->display,
248 "REMOTE_HOST", &s->remote_host,
249 "REMOTE_USER", &s->remote_user,
250 "SERVICE", &s->service,
261 k = parse_boolean(remote);
266 if (kill_processes) {
267 k = parse_boolean(kill_processes);
269 s->kill_processes = k;
272 if (seat && !s->seat) {
275 o = hashmap_get(s->manager->seats, seat);
277 seat_attach_session(o, s);
280 if (vtnr && s->seat && seat_can_multi_session(s->seat)) {
283 k = safe_atoi(vtnr, &v);
284 if (k >= 0 && v >= 1)
289 k = parse_pid(leader, &s->leader);
291 audit_session_from_pid(s->leader, &s->audit_id);
297 t = session_type_from_string(type);
305 c = session_class_from_string(class);
313 /* If we open an unopened pipe for reading we will not
314 get an EOF. to trigger an EOF we hence open it for
315 reading, but close it right-away which then will
318 fd = session_create_fifo(s);
320 close_nointr_nofail(fd);
325 free(kill_processes);
334 int session_activate(Session *s) {
345 if (s->seat->active == s)
348 assert(seat_is_vtconsole(s->seat));
354 return seat_set_active(s->seat, s);
357 static int session_link_x11_socket(Session *s) {
363 assert(s->user->runtime_path);
365 if (s->user->display)
368 if (!s->display || !display_is_local(s->display))
371 k = strspn(s->display+1, "0123456789");
372 f = new(char, sizeof("/tmp/.X11-unix/X") + k);
374 log_error("Out of memory");
378 c = stpcpy(f, "/tmp/.X11-unix/X");
379 memcpy(c, s->display+1, k);
382 if (access(f, F_OK) < 0) {
383 log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f);
388 /* Note that this cannot be in a subdir to avoid
389 * vulnerabilities since we are privileged but the runtime
390 * path is owned by the user */
392 t = strappend(s->user->runtime_path, "/X11-display");
394 log_error("Out of memory");
399 if (link(f, t) < 0) {
400 if (errno == EEXIST) {
407 if (symlink(f, t) < 0) {
409 if (errno == EEXIST) {
412 if (symlink(f, t) >= 0)
416 log_error("Failed to link %s to %s: %m", f, t);
424 log_info("Linked %s to %s.", f, t);
428 s->user->display = s;
433 static int session_create_one_group(Session *s, const char *controller, const char *path) {
441 r = cg_create_and_attach(controller, path, s->leader);
443 r = cg_create(controller, path);
445 r = cg_create(controller, path);
450 r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
452 r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
457 static int session_create_cgroup(Session *s) {
464 assert(s->user->cgroup_path);
466 if (!s->cgroup_path) {
467 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) {
468 log_error("Out of memory");
474 r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
476 log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
478 s->cgroup_path = NULL;
484 STRV_FOREACH(k, s->controllers) {
486 if (strv_contains(s->reset_controllers, *k))
489 r = session_create_one_group(s, *k, p);
491 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
494 STRV_FOREACH(k, s->manager->controllers) {
496 if (strv_contains(s->reset_controllers, *k) ||
497 strv_contains(s->manager->reset_controllers, *k) ||
498 strv_contains(s->controllers, *k))
501 r = session_create_one_group(s, *k, p);
503 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
508 STRV_FOREACH(k, s->reset_controllers) {
509 r = cg_attach(*k, "/", s->leader);
511 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
515 STRV_FOREACH(k, s->manager->reset_controllers) {
517 if (strv_contains(s->reset_controllers, *k) ||
518 strv_contains(s->controllers, *k))
521 r = cg_attach(*k, "/", s->leader);
523 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
528 hashmap_put(s->manager->cgroups, s->cgroup_path, s);
533 int session_start(Session *s) {
542 r = user_start(s->user);
546 log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
547 "New session %s of user %s.", s->id, s->user->name);
550 r = session_create_cgroup(s);
554 /* Create X11 symlink */
555 session_link_x11_socket(s);
557 dual_timestamp_get(&s->timestamp);
560 seat_read_active_vt(s->seat);
564 /* Save session data */
568 session_send_signal(s, true);
573 if (s->seat->active == s)
574 seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
576 seat_send_changed(s->seat, "Sessions\0");
579 user_send_changed(s->user, "Sessions\0");
584 static bool session_shall_kill(Session *s) {
587 if (!s->kill_processes)
590 if (strv_contains(s->manager->kill_exclude_users, s->user->name))
593 if (strv_isempty(s->manager->kill_only_users))
596 return strv_contains(s->manager->kill_only_users, s->user->name);
599 static int session_terminate_cgroup(Session *s) {
608 cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
610 if (session_shall_kill(s)) {
612 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
614 log_error("Failed to kill session cgroup: %s", strerror(-r));
620 /* We still send a HUP to the leader process,
621 * even if we are not supposed to kill the
622 * whole cgroup. But let's first check the
623 * leader still exists and belongs to our
626 r = manager_get_session_by_pid(s->manager, s->leader, &t);
627 if (r > 0 && t == s) {
628 kill(s->leader, SIGTERM); /* for normal processes */
629 kill(s->leader, SIGHUP); /* for shells */
630 kill(s->leader, SIGCONT); /* in case they are stopped */
634 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
636 log_error("Failed to check session cgroup: %s", strerror(-r));
638 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
640 log_error("Failed to delete session cgroup: %s", strerror(-r));
644 STRV_FOREACH(k, s->user->manager->controllers)
645 cg_trim(*k, s->cgroup_path, true);
647 hashmap_remove(s->manager->cgroups, s->cgroup_path);
649 free(s->cgroup_path);
650 s->cgroup_path = NULL;
655 static int session_unlink_x11_socket(Session *s) {
662 if (s->user->display != s)
665 s->user->display = NULL;
667 t = strappend(s->user->runtime_path, "/X11-display");
669 log_error("Out of memory");
676 return r < 0 ? -errno : 0;
679 int session_stop(Session *s) {
685 log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
686 "Removed session %s.", s->id);
689 k = session_terminate_cgroup(s);
693 /* Remove X11 symlink */
694 session_unlink_x11_socket(s);
696 unlink(s->state_file);
697 session_add_to_gc_queue(s);
698 user_add_to_gc_queue(s->user);
701 session_send_signal(s, false);
704 if (s->seat->active == s)
705 seat_set_active(s->seat, NULL);
707 seat_send_changed(s->seat, "Sessions\0");
710 user_send_changed(s->user, "Sessions\0");
717 bool session_is_active(Session *s) {
723 return s->seat->active == s;
726 int session_get_idle_hint(Session *s, dual_timestamp *t) {
737 *t = s->idle_hint_timestamp;
745 if (s->tty[0] != '/') {
746 p = strappend("/dev/", s->tty);
752 if (!startswith(p ? p : s->tty, "/dev/")) {
757 k = lstat(p ? p : s->tty, &st);
763 u = timespec_load(&st.st_atim);
764 n = now(CLOCK_REALTIME);
765 b = u + IDLE_THRESHOLD_USEC < n;
768 dual_timestamp_from_realtime(t, u + b ? IDLE_THRESHOLD_USEC : 0);
774 *t = s->idle_hint_timestamp;
779 void session_set_idle_hint(Session *s, bool b) {
782 if (s->idle_hint == b)
786 dual_timestamp_get(&s->idle_hint_timestamp);
788 session_send_changed(s,
791 "IdleSinceHintMonotonic\0");
794 seat_send_changed(s->seat,
797 "IdleSinceHintMonotonic\0");
799 user_send_changed(s->user,
802 "IdleSinceHintMonotonic\0");
804 manager_send_changed(s->manager,
807 "IdleSinceHintMonotonic\0");
810 int session_create_fifo(Session *s) {
817 r = safe_mkdir("/run/systemd/sessions", 0755, 0, 0);
821 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
824 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
828 /* Open reading side */
829 if (s->fifo_fd < 0) {
830 struct epoll_event ev;
832 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
836 r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s);
842 ev.data.u32 = FD_FIFO_BASE + s->fifo_fd;
844 if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
848 /* Open writing side */
849 r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
856 void session_remove_fifo(Session *s) {
859 if (s->fifo_fd >= 0) {
860 assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s);
861 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
862 close_nointr_nofail(s->fifo_fd);
867 unlink(s->fifo_path);
873 int session_check_gc(Session *s, bool drop_not_started) {
878 if (drop_not_started && !s->started)
881 if (s->fifo_fd >= 0) {
883 r = pipe_eof(s->fifo_fd);
891 if (s->cgroup_path) {
893 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
904 void session_add_to_gc_queue(Session *s) {
910 LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
911 s->in_gc_queue = true;
914 int session_kill(Session *s, KillWho who, int signo) {
923 if (s->leader <= 0 && who == KILL_LEADER)
927 if (kill(s->leader, signo) < 0)
930 if (who == KILL_ALL) {
933 pid_set = set_new(trivial_hash_func, trivial_compare_func);
938 q = set_put(pid_set, LONG_TO_PTR(s->leader));
943 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, signo, false, true, false, pid_set);
945 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
955 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
956 [SESSION_TTY] = "tty",
957 [SESSION_X11] = "x11",
958 [SESSION_UNSPECIFIED] = "unspecified"
961 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
963 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
964 [SESSION_USER] = "user",
965 [SESSION_GREETER] = "greeter",
966 [SESSION_LOCK_SCREEN] = "lock-screen"
969 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
971 static const char* const kill_who_table[_KILL_WHO_MAX] = {
972 [KILL_LEADER] = "leader",
976 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);