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)
192 fputs(i->seat->id, f);
199 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
201 unlink(u->state_file);
210 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
215 int user_load(User *u) {
217 char *display = NULL;
222 r = parse_env_file(u->state_file, NEWLINE,
223 "CGROUP", &u->cgroup_path,
224 "RUNTIME", &u->runtime_path,
225 "SERVICE", &u->service,
234 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
239 s = hashmap_get(u->manager->sessions, display);
243 if (s && s->display && display_is_local(s->display))
249 static int user_mkdir_runtime_path(User *u) {
255 r = mkdir_safe_label("/run/user", 0755, 0, 0);
257 log_error("Failed to create /run/user: %s", strerror(-r));
261 if (!u->runtime_path) {
262 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
267 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
269 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
271 u->runtime_path = NULL;
279 static int user_create_cgroup(User *u) {
286 if (!u->cgroup_path) {
287 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0)
292 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
294 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
296 u->cgroup_path = NULL;
302 STRV_FOREACH(k, u->manager->controllers) {
304 if (strv_contains(u->manager->reset_controllers, *k))
307 r = cg_create(*k, p);
309 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
312 hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
317 static int user_start_service(User *u) {
320 /* FIXME: Fill me in later ... */
325 int user_start(User *u) {
333 log_debug("New user %s logged in.", u->name);
335 /* Make XDG_RUNTIME_DIR */
336 r = user_mkdir_runtime_path(u);
341 r = user_create_cgroup(u);
345 /* Spawn user systemd */
346 r = user_start_service(u);
350 dual_timestamp_get(&u->timestamp);
354 /* Save new user data */
357 user_send_signal(u, true);
362 static int user_stop_service(User *u) {
371 static int user_shall_kill(User *u) {
374 if (!u->manager->kill_user_processes)
377 if (strv_contains(u->manager->kill_exclude_users, u->name))
380 if (strv_isempty(u->manager->kill_only_users))
383 return strv_contains(u->manager->kill_only_users, u->name);
386 static int user_terminate_cgroup(User *u) {
395 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
397 if (user_shall_kill(u)) {
399 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
401 log_error("Failed to kill user cgroup: %s", strerror(-r));
404 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
406 log_error("Failed to check user cgroup: %s", strerror(-r));
408 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
410 log_error("Failed to delete user cgroup: %s", strerror(-r));
415 STRV_FOREACH(k, u->manager->controllers)
416 cg_trim(*k, u->cgroup_path, true);
418 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
420 free(u->cgroup_path);
421 u->cgroup_path = NULL;
426 static int user_remove_runtime_path(User *u) {
431 if (!u->runtime_path)
434 r = rm_rf(u->runtime_path, false, true, false);
436 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
438 free(u->runtime_path);
439 u->runtime_path = NULL;
444 int user_stop(User *u) {
450 log_debug("User %s logged out.", u->name);
452 LIST_FOREACH(sessions_by_user, s, u->sessions) {
459 k = user_stop_service(u);
464 k = user_terminate_cgroup(u);
468 /* Kill XDG_RUNTIME_DIR */
469 k = user_remove_runtime_path(u);
473 unlink(u->state_file);
474 user_add_to_gc_queue(u);
477 user_send_signal(u, false);
484 int user_get_idle_hint(User *u, dual_timestamp *t) {
486 bool idle_hint = true;
487 dual_timestamp ts = { 0, 0 };
491 LIST_FOREACH(sessions_by_user, s, u->sessions) {
495 ih = session_get_idle_hint(s, &k);
501 if (k.monotonic < ts.monotonic)
507 } else if (idle_hint) {
509 if (k.monotonic > ts.monotonic)
520 static int user_check_linger_file(User *u) {
524 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
527 r = access(p, F_OK) >= 0;
533 int user_check_gc(User *u, bool drop_not_started) {
538 if (drop_not_started && !u->started)
544 if (user_check_linger_file(u) > 0)
547 if (u->cgroup_path) {
548 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
559 void user_add_to_gc_queue(User *u) {
565 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
566 u->in_gc_queue = true;
569 UserState user_get_state(User *u) {
574 LIST_FOREACH(sessions_by_user, i, u->sessions)
575 if (session_is_active(i))
581 if (user_check_linger_file(u) > 0)
582 return USER_LINGERING;
587 int user_kill(User *u, int signo) {
596 pid_set = set_new(trivial_hash_func, trivial_compare_func);
600 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
602 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
611 static const char* const user_state_table[_USER_STATE_MAX] = {
612 [USER_OFFLINE] = "offline",
613 [USER_LINGERING] = "lingering",
614 [USER_ONLINE] = "online",
615 [USER_ACTIVE] = "active",
616 [USER_CLOSING] = "closing"
619 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);