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/>.
26 #include "logind-user.h"
29 #include "cgroup-util.h"
33 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
43 u->name = strdup(name);
49 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
55 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) {
69 void user_free(User *u) {
73 LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
76 session_free(u->sessions);
79 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
83 free(u->runtime_path);
85 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
92 int user_save(User *u) {
98 assert(u->state_file);
103 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
107 r = fopen_temporary(u->state_file, &f, &temp_path);
111 fchmod(fileno(f), 0644);
114 "# This is private data. Do not parse.\n"
118 user_state_to_string(user_get_state(u)));
144 fputs("SESSIONS=", f);
146 LIST_FOREACH(sessions_by_user, i, u->sessions) {
155 fputs("\nSEATS=", f);
157 LIST_FOREACH(sessions_by_user, i, u->sessions) {
166 fputs(i->seat->id, f);
169 fputs("\nACTIVE_SESSIONS=", f);
171 LIST_FOREACH(sessions_by_user, i, u->sessions) {
172 if (!session_is_active(i))
183 fputs("\nONLINE_SESSIONS=", f);
185 LIST_FOREACH(sessions_by_user, i, u->sessions) {
186 if (session_get_state(i) == SESSION_CLOSING)
197 fputs("\nACTIVE_SEATS=", f);
199 LIST_FOREACH(sessions_by_user, i, u->sessions) {
200 if (!session_is_active(i) || !i->seat)
208 fputs(i->seat->id, f);
211 fputs("\nONLINE_SEATS=", f);
213 LIST_FOREACH(sessions_by_user, i, u->sessions) {
214 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
222 fputs(i->seat->id, f);
229 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
231 unlink(u->state_file);
240 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
245 int user_load(User *u) {
247 char *display = NULL;
252 r = parse_env_file(u->state_file, NEWLINE,
253 "CGROUP", &u->cgroup_path,
254 "RUNTIME", &u->runtime_path,
255 "SERVICE", &u->service,
264 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
269 s = hashmap_get(u->manager->sessions, display);
273 if (s && s->display && display_is_local(s->display))
279 static int user_mkdir_runtime_path(User *u) {
285 r = mkdir_safe_label("/run/user", 0755, 0, 0);
287 log_error("Failed to create /run/user: %s", strerror(-r));
291 if (!u->runtime_path) {
292 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
297 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
299 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
301 u->runtime_path = NULL;
309 static int user_create_cgroup(User *u) {
316 if (!u->cgroup_path) {
317 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0)
322 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
324 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
326 u->cgroup_path = NULL;
332 STRV_FOREACH(k, u->manager->controllers) {
334 if (strv_contains(u->manager->reset_controllers, *k))
337 r = cg_create(*k, p);
339 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
342 hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
347 static int user_start_service(User *u) {
350 /* FIXME: Fill me in later ... */
355 int user_start(User *u) {
363 log_debug("New user %s logged in.", u->name);
365 /* Make XDG_RUNTIME_DIR */
366 r = user_mkdir_runtime_path(u);
371 r = user_create_cgroup(u);
375 /* Spawn user systemd */
376 r = user_start_service(u);
380 dual_timestamp_get(&u->timestamp);
384 /* Save new user data */
387 user_send_signal(u, true);
392 static int user_stop_service(User *u) {
401 static int user_shall_kill(User *u) {
404 if (!u->manager->kill_user_processes)
407 if (strv_contains(u->manager->kill_exclude_users, u->name))
410 if (strv_isempty(u->manager->kill_only_users))
413 return strv_contains(u->manager->kill_only_users, u->name);
416 static int user_terminate_cgroup(User *u) {
425 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
427 if (user_shall_kill(u)) {
429 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
431 log_error("Failed to kill user cgroup: %s", strerror(-r));
434 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
436 log_error("Failed to check user cgroup: %s", strerror(-r));
438 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
440 log_error("Failed to delete user cgroup: %s", strerror(-r));
445 STRV_FOREACH(k, u->manager->controllers)
446 cg_trim(*k, u->cgroup_path, true);
448 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
450 free(u->cgroup_path);
451 u->cgroup_path = NULL;
456 static int user_remove_runtime_path(User *u) {
461 if (!u->runtime_path)
464 r = rm_rf(u->runtime_path, false, true, false);
466 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
468 free(u->runtime_path);
469 u->runtime_path = NULL;
474 int user_stop(User *u) {
480 log_debug("User %s logged out.", u->name);
482 LIST_FOREACH(sessions_by_user, s, u->sessions) {
489 k = user_stop_service(u);
494 k = user_terminate_cgroup(u);
498 /* Kill XDG_RUNTIME_DIR */
499 k = user_remove_runtime_path(u);
503 unlink(u->state_file);
504 user_add_to_gc_queue(u);
507 user_send_signal(u, false);
514 int user_get_idle_hint(User *u, dual_timestamp *t) {
516 bool idle_hint = true;
517 dual_timestamp ts = { 0, 0 };
521 LIST_FOREACH(sessions_by_user, s, u->sessions) {
525 ih = session_get_idle_hint(s, &k);
531 if (k.monotonic < ts.monotonic)
537 } else if (idle_hint) {
539 if (k.monotonic > ts.monotonic)
550 static int user_check_linger_file(User *u) {
554 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
557 r = access(p, F_OK) >= 0;
563 int user_check_gc(User *u, bool drop_not_started) {
568 if (drop_not_started && !u->started)
574 if (user_check_linger_file(u) > 0)
577 if (u->cgroup_path) {
578 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
589 void user_add_to_gc_queue(User *u) {
595 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
596 u->in_gc_queue = true;
599 UserState user_get_state(User *u) {
601 bool all_closing = true;
606 LIST_FOREACH(sessions_by_user, i, u->sessions) {
607 if (session_is_active(i))
609 if (session_get_state(i) != SESSION_CLOSING)
614 return all_closing ? USER_CLOSING : USER_ONLINE;
616 if (user_check_linger_file(u) > 0)
617 return USER_LINGERING;
622 int user_kill(User *u, int signo) {
631 pid_set = set_new(trivial_hash_func, trivial_compare_func);
635 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
637 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
646 static const char* const user_state_table[_USER_STATE_MAX] = {
647 [USER_OFFLINE] = "offline",
648 [USER_LINGERING] = "lingering",
649 [USER_ONLINE] = "online",
650 [USER_ACTIVE] = "active",
651 [USER_CLOSING] = "closing"
654 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);