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);
335 int session_activate(Session *s) {
346 if (s->seat->active == s)
349 assert(seat_is_vtconsole(s->seat));
355 return seat_set_active(s->seat, s);
358 static int session_link_x11_socket(Session *s) {
364 assert(s->user->runtime_path);
366 if (s->user->display)
369 if (!s->display || !display_is_local(s->display))
372 k = strspn(s->display+1, "0123456789");
373 f = new(char, sizeof("/tmp/.X11-unix/X") + k);
375 log_error("Out of memory");
379 c = stpcpy(f, "/tmp/.X11-unix/X");
380 memcpy(c, s->display+1, k);
383 if (access(f, F_OK) < 0) {
384 log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f);
389 /* Note that this cannot be in a subdir to avoid
390 * vulnerabilities since we are privileged but the runtime
391 * path is owned by the user */
393 t = strappend(s->user->runtime_path, "/X11-display");
395 log_error("Out of memory");
400 if (link(f, t) < 0) {
401 if (errno == EEXIST) {
408 if (symlink(f, t) < 0) {
410 if (errno == EEXIST) {
413 if (symlink(f, t) >= 0)
417 log_error("Failed to link %s to %s: %m", f, t);
425 log_info("Linked %s to %s.", f, t);
429 s->user->display = s;
434 static int session_create_one_group(Session *s, const char *controller, const char *path) {
442 r = cg_create_and_attach(controller, path, s->leader);
444 r = cg_create(controller, path);
446 r = cg_create(controller, path);
451 r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
453 r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
458 static int session_create_cgroup(Session *s) {
465 assert(s->user->cgroup_path);
467 if (!s->cgroup_path) {
468 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) {
469 log_error("Out of memory");
475 r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
477 log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
479 s->cgroup_path = NULL;
485 STRV_FOREACH(k, s->controllers) {
487 if (strv_contains(s->reset_controllers, *k))
490 r = session_create_one_group(s, *k, p);
492 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
495 STRV_FOREACH(k, s->manager->controllers) {
497 if (strv_contains(s->reset_controllers, *k) ||
498 strv_contains(s->manager->reset_controllers, *k) ||
499 strv_contains(s->controllers, *k))
502 r = session_create_one_group(s, *k, p);
504 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
509 STRV_FOREACH(k, s->reset_controllers) {
510 r = cg_attach(*k, "/", s->leader);
512 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
516 STRV_FOREACH(k, s->manager->reset_controllers) {
518 if (strv_contains(s->reset_controllers, *k) ||
519 strv_contains(s->controllers, *k))
522 r = cg_attach(*k, "/", s->leader);
524 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
529 hashmap_put(s->manager->cgroups, s->cgroup_path, s);
534 int session_start(Session *s) {
543 r = user_start(s->user);
547 log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
548 "New session %s of user %s.", s->id, s->user->name);
551 r = session_create_cgroup(s);
555 /* Create X11 symlink */
556 session_link_x11_socket(s);
558 dual_timestamp_get(&s->timestamp);
561 seat_read_active_vt(s->seat);
565 /* Save session data */
569 session_send_signal(s, true);
574 if (s->seat->active == s)
575 seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
577 seat_send_changed(s->seat, "Sessions\0");
580 user_send_changed(s->user, "Sessions\0");
585 static bool session_shall_kill(Session *s) {
588 if (!s->kill_processes)
591 if (strv_contains(s->manager->kill_exclude_users, s->user->name))
594 if (strv_isempty(s->manager->kill_only_users))
597 return strv_contains(s->manager->kill_only_users, s->user->name);
600 static int session_terminate_cgroup(Session *s) {
609 cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
611 if (session_shall_kill(s)) {
613 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
615 log_error("Failed to kill session cgroup: %s", strerror(-r));
621 /* We still send a HUP to the leader process,
622 * even if we are not supposed to kill the
623 * whole cgroup. But let's first check the
624 * leader still exists and belongs to our
627 r = manager_get_session_by_pid(s->manager, s->leader, &t);
628 if (r > 0 && t == s) {
629 kill(s->leader, SIGTERM); /* for normal processes */
630 kill(s->leader, SIGHUP); /* for shells */
631 kill(s->leader, SIGCONT); /* in case they are stopped */
635 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
637 log_error("Failed to check session cgroup: %s", strerror(-r));
639 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
641 log_error("Failed to delete session cgroup: %s", strerror(-r));
645 STRV_FOREACH(k, s->user->manager->controllers)
646 cg_trim(*k, s->cgroup_path, true);
648 hashmap_remove(s->manager->cgroups, s->cgroup_path);
650 free(s->cgroup_path);
651 s->cgroup_path = NULL;
656 static int session_unlink_x11_socket(Session *s) {
663 if (s->user->display != s)
666 s->user->display = NULL;
668 t = strappend(s->user->runtime_path, "/X11-display");
670 log_error("Out of memory");
677 return r < 0 ? -errno : 0;
680 int session_stop(Session *s) {
686 log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
687 "Removed session %s.", s->id);
690 k = session_terminate_cgroup(s);
694 /* Remove X11 symlink */
695 session_unlink_x11_socket(s);
697 unlink(s->state_file);
698 session_add_to_gc_queue(s);
699 user_add_to_gc_queue(s->user);
702 session_send_signal(s, false);
705 if (s->seat->active == s)
706 seat_set_active(s->seat, NULL);
708 seat_send_changed(s->seat, "Sessions\0");
711 user_send_changed(s->user, "Sessions\0");
718 bool session_is_active(Session *s) {
724 return s->seat->active == s;
727 int session_get_idle_hint(Session *s, dual_timestamp *t) {
738 *t = s->idle_hint_timestamp;
746 if (s->tty[0] != '/') {
747 p = strappend("/dev/", s->tty);
753 if (!startswith(p ? p : s->tty, "/dev/")) {
758 k = lstat(p ? p : s->tty, &st);
764 u = timespec_load(&st.st_atim);
765 n = now(CLOCK_REALTIME);
766 b = u + IDLE_THRESHOLD_USEC < n;
769 dual_timestamp_from_realtime(t, u + b ? IDLE_THRESHOLD_USEC : 0);
775 *t = s->idle_hint_timestamp;
780 void session_set_idle_hint(Session *s, bool b) {
783 if (s->idle_hint == b)
787 dual_timestamp_get(&s->idle_hint_timestamp);
789 session_send_changed(s,
792 "IdleSinceHintMonotonic\0");
795 seat_send_changed(s->seat,
798 "IdleSinceHintMonotonic\0");
800 user_send_changed(s->user,
803 "IdleSinceHintMonotonic\0");
805 manager_send_changed(s->manager,
808 "IdleSinceHintMonotonic\0");
811 int session_create_fifo(Session *s) {
818 r = safe_mkdir("/run/systemd/sessions", 0755, 0, 0);
822 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
825 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
829 /* Open reading side */
830 if (s->fifo_fd < 0) {
831 struct epoll_event ev;
833 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
837 r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s);
843 ev.data.u32 = FD_FIFO_BASE + s->fifo_fd;
845 if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
849 /* Open writing side */
850 r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
857 void session_remove_fifo(Session *s) {
860 if (s->fifo_fd >= 0) {
861 assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s);
862 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
863 close_nointr_nofail(s->fifo_fd);
868 unlink(s->fifo_path);
874 int session_check_gc(Session *s, bool drop_not_started) {
879 if (drop_not_started && !s->started)
882 if (s->fifo_fd >= 0) {
884 r = pipe_eof(s->fifo_fd);
892 if (s->cgroup_path) {
894 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
905 void session_add_to_gc_queue(Session *s) {
911 LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
912 s->in_gc_queue = true;
915 int session_kill(Session *s, KillWho who, int signo) {
924 if (s->leader <= 0 && who == KILL_LEADER)
928 if (kill(s->leader, signo) < 0)
931 if (who == KILL_ALL) {
934 pid_set = set_new(trivial_hash_func, trivial_compare_func);
939 q = set_put(pid_set, LONG_TO_PTR(s->leader));
944 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, signo, false, true, false, pid_set);
946 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
956 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
957 [SESSION_TTY] = "tty",
958 [SESSION_X11] = "x11",
959 [SESSION_UNSPECIFIED] = "unspecified"
962 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
964 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
965 [SESSION_USER] = "user",
966 [SESSION_GREETER] = "greeter",
967 [SESSION_LOCK_SCREEN] = "lock-screen"
970 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
972 static const char* const kill_who_table[_KILL_WHO_MAX] = {
973 [KILL_LEADER] = "leader",
977 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);