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>
28 #include "systemd/sd-id128.h"
29 #include "systemd/sd-messages.h"
33 #include "path-util.h"
34 #include "cgroup-util.h"
35 #include "logind-session.h"
38 Session* session_new(Manager *m, User *u, const char *id) {
48 s->state_file = strappend("/run/systemd/sessions/", id);
54 s->id = path_get_file_name(s->state_file);
56 if (hashmap_put(m->sessions, s->id, s) < 0) {
66 LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
71 void session_free(Session *s) {
75 LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s);
78 LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
80 if (s->user->display == s)
81 s->user->display = NULL;
85 if (s->seat->active == s)
86 s->seat->active = NULL;
88 LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
92 hashmap_remove(s->manager->session_cgroups, s->cgroup_path);
95 strv_free(s->controllers);
100 free(s->remote_user);
103 hashmap_remove(s->manager->sessions, s->id);
104 session_remove_fifo(s);
110 int session_save(Session *s) {
120 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
124 r = fopen_temporary(s->state_file, &f, &temp_path);
130 fchmod(fileno(f), 0644);
133 "# This is private data. Do not parse.\n"
139 "KILL_PROCESSES=%i\n",
140 (unsigned long) s->user->uid,
142 session_is_active(s),
143 session_state_to_string(session_get_state(s)),
150 session_type_to_string(s->type));
155 session_class_to_string(s->class));
197 if (s->seat && seat_can_multi_session(s->seat))
205 (unsigned long) s->leader);
210 (unsigned long long) s->audit_id);
214 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
216 unlink(s->state_file);
225 log_error("Failed to save session data for %s: %s", s->id, strerror(-r));
230 int session_load(Session *s) {
232 *kill_processes = NULL,
244 r = parse_env_file(s->state_file, NEWLINE,
246 "KILL_PROCESSES", &kill_processes,
247 "CGROUP", &s->cgroup_path,
248 "FIFO", &s->fifo_path,
251 "DISPLAY", &s->display,
252 "REMOTE_HOST", &s->remote_host,
253 "REMOTE_USER", &s->remote_user,
254 "SERVICE", &s->service,
265 k = parse_boolean(remote);
270 if (kill_processes) {
271 k = parse_boolean(kill_processes);
273 s->kill_processes = k;
276 if (seat && !s->seat) {
279 o = hashmap_get(s->manager->seats, seat);
281 seat_attach_session(o, s);
284 if (vtnr && s->seat && seat_can_multi_session(s->seat)) {
287 k = safe_atoi(vtnr, &v);
288 if (k >= 0 && v >= 1)
293 k = parse_pid(leader, &s->leader);
295 audit_session_from_pid(s->leader, &s->audit_id);
301 t = session_type_from_string(type);
309 c = session_class_from_string(class);
317 /* If we open an unopened pipe for reading we will not
318 get an EOF. to trigger an EOF we hence open it for
319 reading, but close it right-away which then will
322 fd = session_create_fifo(s);
324 close_nointr_nofail(fd);
329 free(kill_processes);
339 int session_activate(Session *s) {
350 if (s->seat->active == s)
353 assert(seat_is_vtconsole(s->seat));
359 return seat_set_active(s->seat, s);
362 static int session_link_x11_socket(Session *s) {
368 assert(s->user->runtime_path);
370 if (s->user->display)
373 if (!s->display || !display_is_local(s->display))
376 k = strspn(s->display+1, "0123456789");
377 f = new(char, sizeof("/tmp/.X11-unix/X") + k);
381 c = stpcpy(f, "/tmp/.X11-unix/X");
382 memcpy(c, s->display+1, k);
385 if (access(f, F_OK) < 0) {
386 log_warning("Session %s has display %s with non-existing socket %s.", s->id, s->display, f);
391 /* Note that this cannot be in a subdir to avoid
392 * vulnerabilities since we are privileged but the runtime
393 * path is owned by the user */
395 t = strappend(s->user->runtime_path, "/X11-display");
401 if (link(f, t) < 0) {
402 if (errno == EEXIST) {
409 if (symlink(f, t) < 0) {
411 if (errno == EEXIST) {
414 if (symlink(f, t) >= 0)
418 log_error("Failed to link %s to %s: %m", f, t);
426 log_info("Linked %s to %s.", f, t);
430 s->user->display = s;
435 static int session_create_one_group(Session *s, const char *controller, const char *path) {
443 r = cg_create_and_attach(controller, path, s->leader);
445 r = cg_create(controller, path);
447 r = cg_create(controller, path);
452 r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
454 r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
459 static int session_create_cgroup(Session *s) {
466 assert(s->user->cgroup_path);
468 if (!s->cgroup_path) {
469 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0)
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 r = hashmap_put(s->manager->session_cgroups, s->cgroup_path, s);
530 log_warning("Failed to create mapping between cgroup and session");
535 int session_start(Session *s) {
544 r = user_start(s->user);
548 log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
549 MESSAGE_ID(SD_MESSAGE_SESSION_START),
550 "SESSION_ID=%s", s->id,
551 "USER_ID=%s", s->user->name,
552 "LEADER=%lu", (unsigned long) s->leader,
553 "MESSAGE=New session %s of user %s.", s->id, s->user->name,
557 r = session_create_cgroup(s);
561 /* Create X11 symlink */
562 session_link_x11_socket(s);
564 dual_timestamp_get(&s->timestamp);
567 seat_read_active_vt(s->seat);
571 /* Save session data */
575 session_send_signal(s, true);
580 if (s->seat->active == s)
581 seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
583 seat_send_changed(s->seat, "Sessions\0");
586 user_send_changed(s->user, "Sessions\0");
591 static bool session_shall_kill(Session *s) {
594 if (!s->kill_processes)
597 if (strv_contains(s->manager->kill_exclude_users, s->user->name))
600 if (strv_isempty(s->manager->kill_only_users))
603 return strv_contains(s->manager->kill_only_users, s->user->name);
606 static int session_terminate_cgroup(Session *s) {
615 cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
617 if (session_shall_kill(s)) {
619 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
621 log_error("Failed to kill session cgroup: %s", strerror(-r));
627 /* We still send a HUP to the leader process,
628 * even if we are not supposed to kill the
629 * whole cgroup. But let's first check the
630 * leader still exists and belongs to our
633 r = manager_get_session_by_pid(s->manager, s->leader, &t);
634 if (r > 0 && t == s) {
635 kill(s->leader, SIGTERM); /* for normal processes */
636 kill(s->leader, SIGHUP); /* for shells */
637 kill(s->leader, SIGCONT); /* in case they are stopped */
641 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
643 log_error("Failed to check session cgroup: %s", strerror(-r));
645 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
647 log_error("Failed to delete session cgroup: %s", strerror(-r));
651 STRV_FOREACH(k, s->user->manager->controllers)
652 cg_trim(*k, s->cgroup_path, true);
654 hashmap_remove(s->manager->session_cgroups, s->cgroup_path);
656 free(s->cgroup_path);
657 s->cgroup_path = NULL;
662 static int session_unlink_x11_socket(Session *s) {
669 if (s->user->display != s)
672 s->user->display = NULL;
674 t = strappend(s->user->runtime_path, "/X11-display");
681 return r < 0 ? -errno : 0;
684 int session_stop(Session *s) {
690 log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
691 MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
692 "SESSION_ID=%s", s->id,
693 "USER_ID=%s", s->user->name,
694 "LEADER=%lu", (unsigned long) s->leader,
695 "MESSAGE=Removed session %s.", s->id,
699 k = session_terminate_cgroup(s);
703 /* Remove X11 symlink */
704 session_unlink_x11_socket(s);
706 unlink(s->state_file);
707 session_add_to_gc_queue(s);
708 user_add_to_gc_queue(s->user);
711 session_send_signal(s, false);
714 if (s->seat->active == s)
715 seat_set_active(s->seat, NULL);
717 seat_send_changed(s->seat, "Sessions\0");
721 user_send_changed(s->user, "Sessions\0");
729 bool session_is_active(Session *s) {
735 return s->seat->active == s;
738 static int get_tty_atime(const char *tty, usec_t *atime) {
739 _cleanup_free_ char *p = NULL;
745 if (!path_is_absolute(tty)) {
746 p = strappend("/dev/", tty);
751 } else if (!path_startswith(tty, "/dev/"))
754 if (lstat(tty, &st) < 0)
757 *atime = timespec_load(&st.st_atim);
761 static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
762 _cleanup_free_ char *p = NULL;
768 r = get_ctty(pid, NULL, &p);
772 return get_tty_atime(p, atime);
775 int session_get_idle_hint(Session *s, dual_timestamp *t) {
781 /* Explicit idle hint is set */
784 *t = s->idle_hint_timestamp;
789 /* Graphical sessions should really implement a real
794 /* For sessions with an explicitly configured tty, let's check
797 r = get_tty_atime(s->tty, &atime);
802 /* For sessions with a leader but no explicitly configured
803 * tty, let's check the controlling tty of the leader */
805 r = get_process_ctty_atime(s->leader, &atime);
810 /* For other TTY sessions, let's find the most recent atime of
811 * the ttys of any of the processes of the session */
812 if (s->cgroup_path) {
813 _cleanup_fclose_ FILE *f = NULL;
815 if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &f) >= 0) {
819 while (cg_read_pid(f, &pid) > 0) {
822 if (get_process_ctty_atime(pid, &a) >= 0)
823 if (atime == 0 || atime < a)
834 *t = s->idle_hint_timestamp;
840 dual_timestamp_from_realtime(t, atime);
842 n = now(CLOCK_REALTIME);
844 if (s->manager->idle_action_usec <= 0)
847 return atime + s->manager->idle_action_usec <= n;
850 void session_set_idle_hint(Session *s, bool b) {
853 if (s->idle_hint == b)
857 dual_timestamp_get(&s->idle_hint_timestamp);
859 session_send_changed(s,
862 "IdleSinceHintMonotonic\0");
865 seat_send_changed(s->seat,
868 "IdleSinceHintMonotonic\0");
870 user_send_changed(s->user,
873 "IdleSinceHintMonotonic\0");
875 manager_send_changed(s->manager,
878 "IdleSinceHintMonotonic\0");
881 int session_create_fifo(Session *s) {
888 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
892 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
895 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
899 /* Open reading side */
900 if (s->fifo_fd < 0) {
901 struct epoll_event ev;
903 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
907 r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s);
913 ev.data.u32 = FD_OTHER_BASE + s->fifo_fd;
915 if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
919 /* Open writing side */
920 r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
927 void session_remove_fifo(Session *s) {
930 if (s->fifo_fd >= 0) {
931 assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s);
932 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
933 close_nointr_nofail(s->fifo_fd);
941 unlink(s->fifo_path);
947 int session_check_gc(Session *s, bool drop_not_started) {
952 if (drop_not_started && !s->started)
955 if (s->fifo_fd >= 0) {
957 r = pipe_eof(s->fifo_fd);
965 if (s->cgroup_path) {
967 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
978 void session_add_to_gc_queue(Session *s) {
984 LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
985 s->in_gc_queue = true;
988 SessionState session_get_state(Session *s) {
992 return SESSION_CLOSING;
994 if (session_is_active(s))
995 return SESSION_ACTIVE;
997 return SESSION_ONLINE;
1000 int session_kill(Session *s, KillWho who, int signo) {
1002 Set *pid_set = NULL;
1006 if (!s->cgroup_path)
1009 if (s->leader <= 0 && who == KILL_LEADER)
1013 if (kill(s->leader, signo) < 0)
1016 if (who == KILL_ALL) {
1019 pid_set = set_new(trivial_hash_func, trivial_compare_func);
1023 if (s->leader > 0) {
1024 q = set_put(pid_set, LONG_TO_PTR(s->leader));
1029 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, signo, false, true, false, pid_set);
1031 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
1041 static const char* const session_state_table[_SESSION_TYPE_MAX] = {
1042 [SESSION_ONLINE] = "online",
1043 [SESSION_ACTIVE] = "active",
1044 [SESSION_CLOSING] = "closing"
1047 DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
1049 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
1050 [SESSION_TTY] = "tty",
1051 [SESSION_X11] = "x11",
1052 [SESSION_UNSPECIFIED] = "unspecified"
1055 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
1057 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
1058 [SESSION_USER] = "user",
1059 [SESSION_GREETER] = "greeter",
1060 [SESSION_LOCK_SCREEN] = "lock-screen"
1063 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
1065 static const char* const kill_who_table[_KILL_WHO_MAX] = {
1066 [KILL_LEADER] = "leader",
1070 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);