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 r = hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
344 log_warning("Failed to create mapping between cgroup and user");
349 static int user_start_service(User *u) {
352 /* FIXME: Fill me in later ... */
357 int user_start(User *u) {
365 log_debug("New user %s logged in.", u->name);
367 /* Make XDG_RUNTIME_DIR */
368 r = user_mkdir_runtime_path(u);
373 r = user_create_cgroup(u);
377 /* Spawn user systemd */
378 r = user_start_service(u);
382 dual_timestamp_get(&u->timestamp);
386 /* Save new user data */
389 user_send_signal(u, true);
394 static int user_stop_service(User *u) {
403 static int user_shall_kill(User *u) {
406 if (!u->manager->kill_user_processes)
409 if (strv_contains(u->manager->kill_exclude_users, u->name))
412 if (strv_isempty(u->manager->kill_only_users))
415 return strv_contains(u->manager->kill_only_users, u->name);
418 static int user_terminate_cgroup(User *u) {
427 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
429 if (user_shall_kill(u)) {
431 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
433 log_error("Failed to kill user cgroup: %s", strerror(-r));
436 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
438 log_error("Failed to check user cgroup: %s", strerror(-r));
440 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
442 log_error("Failed to delete user cgroup: %s", strerror(-r));
447 STRV_FOREACH(k, u->manager->controllers)
448 cg_trim(*k, u->cgroup_path, true);
450 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
452 free(u->cgroup_path);
453 u->cgroup_path = NULL;
458 static int user_remove_runtime_path(User *u) {
463 if (!u->runtime_path)
466 r = rm_rf(u->runtime_path, false, true, false);
468 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
470 free(u->runtime_path);
471 u->runtime_path = NULL;
476 int user_stop(User *u) {
482 log_debug("User %s logged out.", u->name);
484 LIST_FOREACH(sessions_by_user, s, u->sessions) {
491 k = user_stop_service(u);
496 k = user_terminate_cgroup(u);
500 /* Kill XDG_RUNTIME_DIR */
501 k = user_remove_runtime_path(u);
505 unlink(u->state_file);
506 user_add_to_gc_queue(u);
509 user_send_signal(u, false);
516 int user_get_idle_hint(User *u, dual_timestamp *t) {
518 bool idle_hint = true;
519 dual_timestamp ts = { 0, 0 };
523 LIST_FOREACH(sessions_by_user, s, u->sessions) {
527 ih = session_get_idle_hint(s, &k);
533 if (k.monotonic < ts.monotonic)
539 } else if (idle_hint) {
541 if (k.monotonic > ts.monotonic)
552 static int user_check_linger_file(User *u) {
556 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
559 r = access(p, F_OK) >= 0;
565 int user_check_gc(User *u, bool drop_not_started) {
570 if (drop_not_started && !u->started)
576 if (user_check_linger_file(u) > 0)
579 if (u->cgroup_path) {
580 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
591 void user_add_to_gc_queue(User *u) {
597 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
598 u->in_gc_queue = true;
601 UserState user_get_state(User *u) {
603 bool all_closing = true;
608 LIST_FOREACH(sessions_by_user, i, u->sessions) {
609 if (session_is_active(i))
611 if (session_get_state(i) != SESSION_CLOSING)
616 return all_closing ? USER_CLOSING : USER_ONLINE;
618 if (user_check_linger_file(u) > 0)
619 return USER_LINGERING;
624 int user_kill(User *u, int signo) {
633 pid_set = set_new(trivial_hash_func, trivial_compare_func);
637 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
639 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
648 static const char* const user_state_table[_USER_STATE_MAX] = {
649 [USER_OFFLINE] = "offline",
650 [USER_LINGERING] = "lingering",
651 [USER_ONLINE] = "online",
652 [USER_ACTIVE] = "active",
653 [USER_CLOSING] = "closing"
656 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);