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 _cleanup_free_ char *name = NULL, *escaped = NULL;
320 if (asprintf(&name, "%lu.user", (unsigned long) u->uid) < 0)
323 escaped = cg_escape(name);
327 p = strjoin(u->manager->cgroup_path, "/", escaped, NULL);
333 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p, NULL);
335 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
337 u->cgroup_path = NULL;
343 STRV_FOREACH(k, u->manager->controllers) {
345 if (strv_contains(u->manager->reset_controllers, *k))
348 r = cg_create(*k, p, NULL);
350 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
353 r = hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
355 log_warning("Failed to create mapping between cgroup and user");
360 static int user_start_service(User *u) {
363 /* FIXME: Fill me in later ... */
368 int user_start(User *u) {
376 log_debug("New user %s logged in.", u->name);
378 /* Make XDG_RUNTIME_DIR */
379 r = user_mkdir_runtime_path(u);
384 r = user_create_cgroup(u);
388 /* Spawn user systemd */
389 r = user_start_service(u);
393 dual_timestamp_get(&u->timestamp);
397 /* Save new user data */
400 user_send_signal(u, true);
405 static int user_stop_service(User *u) {
414 static int user_shall_kill(User *u) {
417 if (!u->manager->kill_user_processes)
420 if (strv_contains(u->manager->kill_exclude_users, u->name))
423 if (strv_isempty(u->manager->kill_only_users))
426 return strv_contains(u->manager->kill_only_users, u->name);
429 static int user_terminate_cgroup(User *u) {
438 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
440 if (user_shall_kill(u)) {
442 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
444 log_error("Failed to kill user cgroup: %s", strerror(-r));
447 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
449 log_error("Failed to check user cgroup: %s", strerror(-r));
451 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
453 log_error("Failed to delete user cgroup: %s", strerror(-r));
458 STRV_FOREACH(k, u->manager->controllers)
459 cg_trim(*k, u->cgroup_path, true);
461 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
463 free(u->cgroup_path);
464 u->cgroup_path = NULL;
469 static int user_remove_runtime_path(User *u) {
474 if (!u->runtime_path)
477 r = rm_rf(u->runtime_path, false, true, false);
479 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
481 free(u->runtime_path);
482 u->runtime_path = NULL;
487 int user_stop(User *u) {
493 log_debug("User %s logged out.", u->name);
495 LIST_FOREACH(sessions_by_user, s, u->sessions) {
502 k = user_stop_service(u);
507 k = user_terminate_cgroup(u);
511 /* Kill XDG_RUNTIME_DIR */
512 k = user_remove_runtime_path(u);
516 unlink(u->state_file);
517 user_add_to_gc_queue(u);
520 user_send_signal(u, false);
527 int user_get_idle_hint(User *u, dual_timestamp *t) {
529 bool idle_hint = true;
530 dual_timestamp ts = { 0, 0 };
534 LIST_FOREACH(sessions_by_user, s, u->sessions) {
538 ih = session_get_idle_hint(s, &k);
544 if (k.monotonic < ts.monotonic)
550 } else if (idle_hint) {
552 if (k.monotonic > ts.monotonic)
563 static int user_check_linger_file(User *u) {
567 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
570 r = access(p, F_OK) >= 0;
576 int user_check_gc(User *u, bool drop_not_started) {
581 if (drop_not_started && !u->started)
587 if (user_check_linger_file(u) > 0)
590 if (u->cgroup_path) {
591 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
602 void user_add_to_gc_queue(User *u) {
608 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
609 u->in_gc_queue = true;
612 UserState user_get_state(User *u) {
614 bool all_closing = true;
619 LIST_FOREACH(sessions_by_user, i, u->sessions) {
620 if (session_is_active(i))
622 if (session_get_state(i) != SESSION_CLOSING)
627 return all_closing ? USER_CLOSING : USER_ONLINE;
629 if (user_check_linger_file(u) > 0)
630 return USER_LINGERING;
635 int user_kill(User *u, int signo) {
644 pid_set = set_new(trivial_hash_func, trivial_compare_func);
648 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
650 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
659 static const char* const user_state_table[_USER_STATE_MAX] = {
660 [USER_OFFLINE] = "offline",
661 [USER_LINGERING] = "lingering",
662 [USER_ONLINE] = "online",
663 [USER_ACTIVE] = "active",
664 [USER_CLOSING] = "closing"
667 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);