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"
34 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
44 u->name = strdup(name);
50 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
56 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) {
70 void user_free(User *u) {
74 LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
77 session_free(u->sessions);
80 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
84 free(u->runtime_path);
86 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
93 int user_save(User *u) {
99 assert(u->state_file);
104 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
108 r = fopen_temporary(u->state_file, &f, &temp_path);
112 fchmod(fileno(f), 0644);
115 "# This is private data. Do not parse.\n"
119 user_state_to_string(user_get_state(u)));
145 fputs("SESSIONS=", f);
147 LIST_FOREACH(sessions_by_user, i, u->sessions) {
156 fputs("\nSEATS=", f);
158 LIST_FOREACH(sessions_by_user, i, u->sessions) {
167 fputs(i->seat->id, f);
170 fputs("\nACTIVE_SESSIONS=", f);
172 LIST_FOREACH(sessions_by_user, i, u->sessions) {
173 if (!session_is_active(i))
184 fputs("\nONLINE_SESSIONS=", f);
186 LIST_FOREACH(sessions_by_user, i, u->sessions) {
187 if (session_get_state(i) == SESSION_CLOSING)
198 fputs("\nACTIVE_SEATS=", f);
200 LIST_FOREACH(sessions_by_user, i, u->sessions) {
201 if (!session_is_active(i) || !i->seat)
209 fputs(i->seat->id, f);
212 fputs("\nONLINE_SEATS=", f);
214 LIST_FOREACH(sessions_by_user, i, u->sessions) {
215 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
223 fputs(i->seat->id, f);
230 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
232 unlink(u->state_file);
241 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
246 int user_load(User *u) {
248 char *display = NULL;
253 r = parse_env_file(u->state_file, NEWLINE,
254 "CGROUP", &u->cgroup_path,
255 "RUNTIME", &u->runtime_path,
256 "SERVICE", &u->service,
265 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
270 s = hashmap_get(u->manager->sessions, display);
274 if (s && s->display && display_is_local(s->display))
280 static int user_mkdir_runtime_path(User *u) {
286 r = mkdir_safe_label("/run/user", 0755, 0, 0);
288 log_error("Failed to create /run/user: %s", strerror(-r));
292 if (!u->runtime_path) {
293 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
298 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
300 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
302 u->runtime_path = NULL;
310 static int user_create_cgroup(User *u) {
317 if (!u->cgroup_path) {
318 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0)
323 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
325 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
327 u->cgroup_path = NULL;
333 STRV_FOREACH(k, u->manager->controllers) {
335 if (strv_contains(u->manager->reset_controllers, *k))
338 r = cg_create(*k, p);
340 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
343 r = hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
345 log_warning("Failed to create mapping between cgroup and user");
350 static int user_start_service(User *u) {
353 /* FIXME: Fill me in later ... */
358 int user_start(User *u) {
366 log_debug("New user %s logged in.", u->name);
368 /* Make XDG_RUNTIME_DIR */
369 r = user_mkdir_runtime_path(u);
374 r = user_create_cgroup(u);
378 /* Spawn user systemd */
379 r = user_start_service(u);
383 dual_timestamp_get(&u->timestamp);
387 /* Save new user data */
390 user_send_signal(u, true);
395 static int user_stop_service(User *u) {
404 static int user_shall_kill(User *u) {
407 if (!u->manager->kill_user_processes)
410 if (strv_contains(u->manager->kill_exclude_users, u->name))
413 if (strv_isempty(u->manager->kill_only_users))
416 return strv_contains(u->manager->kill_only_users, u->name);
419 static int user_terminate_cgroup(User *u) {
428 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
430 if (user_shall_kill(u)) {
432 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
434 log_error("Failed to kill user cgroup: %s", strerror(-r));
437 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
439 log_error("Failed to check user cgroup: %s", strerror(-r));
441 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
443 log_error("Failed to delete user cgroup: %s", strerror(-r));
448 STRV_FOREACH(k, u->manager->controllers)
449 cg_trim(*k, u->cgroup_path, true);
451 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
453 free(u->cgroup_path);
454 u->cgroup_path = NULL;
459 static int user_remove_runtime_path(User *u) {
464 if (!u->runtime_path)
467 r = rm_rf(u->runtime_path, false, true, false);
469 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
471 free(u->runtime_path);
472 u->runtime_path = NULL;
477 int user_stop(User *u) {
483 log_debug("User %s logged out.", u->name);
485 LIST_FOREACH(sessions_by_user, s, u->sessions) {
492 k = user_stop_service(u);
497 k = user_terminate_cgroup(u);
501 /* Kill XDG_RUNTIME_DIR */
502 k = user_remove_runtime_path(u);
506 unlink(u->state_file);
507 user_add_to_gc_queue(u);
510 user_send_signal(u, false);
517 int user_get_idle_hint(User *u, dual_timestamp *t) {
519 bool idle_hint = true;
520 dual_timestamp ts = { 0, 0 };
524 LIST_FOREACH(sessions_by_user, s, u->sessions) {
528 ih = session_get_idle_hint(s, &k);
534 if (k.monotonic < ts.monotonic)
540 } else if (idle_hint) {
542 if (k.monotonic > ts.monotonic)
553 static int user_check_linger_file(User *u) {
557 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
560 r = access(p, F_OK) >= 0;
566 int user_check_gc(User *u, bool drop_not_started) {
571 if (drop_not_started && !u->started)
577 if (user_check_linger_file(u) > 0)
580 if (u->cgroup_path) {
581 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
592 void user_add_to_gc_queue(User *u) {
598 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
599 u->in_gc_queue = true;
602 UserState user_get_state(User *u) {
604 bool all_closing = true;
609 LIST_FOREACH(sessions_by_user, i, u->sessions) {
610 if (session_is_active(i))
612 if (session_get_state(i) != SESSION_CLOSING)
617 return all_closing ? USER_CLOSING : USER_ONLINE;
619 if (user_check_linger_file(u) > 0)
620 return USER_LINGERING;
625 int user_kill(User *u, int signo) {
634 pid_set = set_new(trivial_hash_func, trivial_compare_func);
638 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
640 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
649 static const char* const user_state_table[_USER_STATE_MAX] = {
650 [USER_OFFLINE] = "offline",
651 [USER_LINGERING] = "lingering",
652 [USER_ONLINE] = "online",
653 [USER_ACTIVE] = "active",
654 [USER_CLOSING] = "closing"
657 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);