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);
81 free(u->runtime_path);
83 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
90 int user_save(User *u) {
96 assert(u->state_file);
101 r = safe_mkdir("/run/systemd/users", 0755, 0, 0);
105 r = fopen_temporary(u->state_file, &f, &temp_path);
109 fchmod(fileno(f), 0644);
112 "# This is private data. Do not parse.\n"
116 user_state_to_string(user_get_state(u)));
142 fputs("SESSIONS=", f);
144 LIST_FOREACH(sessions_by_user, i, u->sessions) {
153 fputs("\nSEATS=", f);
155 LIST_FOREACH(sessions_by_user, i, u->sessions) {
164 fputs(i->seat->id, f);
167 fputs("\nACTIVE_SESSIONS=", f);
169 LIST_FOREACH(sessions_by_user, i, u->sessions) {
170 if (!session_is_active(i))
181 fputs("\nACTIVE_SEATS=", f);
183 LIST_FOREACH(sessions_by_user, i, u->sessions) {
184 if (!session_is_active(i) || !i->seat)
190 fputs(i->seat->id, f);
197 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
199 unlink(u->state_file);
208 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
213 int user_load(User *u) {
215 char *display = NULL;
220 r = parse_env_file(u->state_file, NEWLINE,
221 "CGROUP", &u->cgroup_path,
222 "RUNTIME", &u->runtime_path,
223 "SERVICE", &u->service,
232 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
237 s = hashmap_get(u->manager->sessions, display);
241 if (s && s->display && display_is_local(s->display))
247 static int user_mkdir_runtime_path(User *u) {
253 r = safe_mkdir("/run/user", 0755, 0, 0);
255 log_error("Failed to create /run/user: %s", strerror(-r));
259 if (!u->runtime_path) {
260 p = strappend("/run/user/", u->name);
263 log_error("Out of memory");
269 r = safe_mkdir(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) {
290 log_error("Out of memory");
296 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
298 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
300 u->cgroup_path = NULL;
306 STRV_FOREACH(k, u->manager->controllers) {
308 if (strv_contains(u->manager->reset_controllers, *k))
311 r = cg_create(*k, p);
313 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
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 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 int user_check_gc(User *u, bool drop_not_started) {
526 if (drop_not_started && !u->started)
532 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
535 r = access(p, F_OK) >= 0;
541 if (u->cgroup_path) {
542 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
553 void user_add_to_gc_queue(User *u) {
559 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
560 u->in_gc_queue = true;
563 UserState user_get_state(User *u) {
569 return USER_LINGERING;
571 LIST_FOREACH(sessions_by_user, i, u->sessions)
572 if (session_is_active(i))
578 int user_kill(User *u, int signo) {
587 pid_set = set_new(trivial_hash_func, trivial_compare_func);
591 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
593 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
602 static const char* const user_state_table[_USER_STATE_MAX] = {
603 [USER_OFFLINE] = "offline",
604 [USER_LINGERING] = "lingering",
605 [USER_ONLINE] = "online",
606 [USER_ACTIVE] = "active"
609 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);