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("\nACTIVE_SEATS=", f);
185 LIST_FOREACH(sessions_by_user, i, u->sessions) {
186 if (!session_is_active(i) || !i->seat)
194 fputs(i->seat->id, f);
201 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
203 unlink(u->state_file);
212 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
217 int user_load(User *u) {
219 char *display = NULL;
224 r = parse_env_file(u->state_file, NEWLINE,
225 "CGROUP", &u->cgroup_path,
226 "RUNTIME", &u->runtime_path,
227 "SERVICE", &u->service,
236 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
241 s = hashmap_get(u->manager->sessions, display);
245 if (s && s->display && display_is_local(s->display))
251 static int user_mkdir_runtime_path(User *u) {
257 r = mkdir_safe_label("/run/user", 0755, 0, 0);
259 log_error("Failed to create /run/user: %s", strerror(-r));
263 if (!u->runtime_path) {
264 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
269 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
271 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
273 u->runtime_path = NULL;
281 static int user_create_cgroup(User *u) {
288 if (!u->cgroup_path) {
289 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0)
294 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
296 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
298 u->cgroup_path = NULL;
304 STRV_FOREACH(k, u->manager->controllers) {
306 if (strv_contains(u->manager->reset_controllers, *k))
309 r = cg_create(*k, p);
311 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
314 hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
319 static int user_start_service(User *u) {
322 /* FIXME: Fill me in later ... */
327 int user_start(User *u) {
335 log_debug("New user %s logged in.", u->name);
337 /* Make XDG_RUNTIME_DIR */
338 r = user_mkdir_runtime_path(u);
343 r = user_create_cgroup(u);
347 /* Spawn user systemd */
348 r = user_start_service(u);
352 dual_timestamp_get(&u->timestamp);
356 /* Save new user data */
359 user_send_signal(u, true);
364 static int user_stop_service(User *u) {
373 static int user_shall_kill(User *u) {
376 if (!u->manager->kill_user_processes)
379 if (strv_contains(u->manager->kill_exclude_users, u->name))
382 if (strv_isempty(u->manager->kill_only_users))
385 return strv_contains(u->manager->kill_only_users, u->name);
388 static int user_terminate_cgroup(User *u) {
397 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
399 if (user_shall_kill(u)) {
401 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
403 log_error("Failed to kill user cgroup: %s", strerror(-r));
406 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
408 log_error("Failed to check user cgroup: %s", strerror(-r));
410 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
412 log_error("Failed to delete user cgroup: %s", strerror(-r));
417 STRV_FOREACH(k, u->manager->controllers)
418 cg_trim(*k, u->cgroup_path, true);
420 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
422 free(u->cgroup_path);
423 u->cgroup_path = NULL;
428 static int user_remove_runtime_path(User *u) {
433 if (!u->runtime_path)
436 r = rm_rf(u->runtime_path, false, true, false);
438 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
440 free(u->runtime_path);
441 u->runtime_path = NULL;
446 int user_stop(User *u) {
452 log_debug("User %s logged out.", u->name);
454 LIST_FOREACH(sessions_by_user, s, u->sessions) {
461 k = user_stop_service(u);
466 k = user_terminate_cgroup(u);
470 /* Kill XDG_RUNTIME_DIR */
471 k = user_remove_runtime_path(u);
475 unlink(u->state_file);
476 user_add_to_gc_queue(u);
479 user_send_signal(u, false);
486 int user_get_idle_hint(User *u, dual_timestamp *t) {
488 bool idle_hint = true;
489 dual_timestamp ts = { 0, 0 };
493 LIST_FOREACH(sessions_by_user, s, u->sessions) {
497 ih = session_get_idle_hint(s, &k);
503 if (k.monotonic < ts.monotonic)
509 } else if (idle_hint) {
511 if (k.monotonic > ts.monotonic)
522 static int user_check_linger_file(User *u) {
526 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
529 r = access(p, F_OK) >= 0;
535 int user_check_gc(User *u, bool drop_not_started) {
540 if (drop_not_started && !u->started)
546 if (user_check_linger_file(u) > 0)
549 if (u->cgroup_path) {
550 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
561 void user_add_to_gc_queue(User *u) {
567 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
568 u->in_gc_queue = true;
571 UserState user_get_state(User *u) {
573 bool all_closing = true;
578 LIST_FOREACH(sessions_by_user, i, u->sessions) {
579 if (session_is_active(i))
581 if (session_get_state(i) != SESSION_CLOSING)
586 return all_closing ? USER_CLOSING : USER_ONLINE;
588 if (user_check_linger_file(u) > 0)
589 return USER_LINGERING;
594 int user_kill(User *u, int signo) {
603 pid_set = set_new(trivial_hash_func, trivial_compare_func);
607 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
609 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
618 static const char* const user_state_table[_USER_STATE_MAX] = {
619 [USER_OFFLINE] = "offline",
620 [USER_LINGERING] = "lingering",
621 [USER_ONLINE] = "online",
622 [USER_ACTIVE] = "active",
623 [USER_CLOSING] = "closing"
626 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);