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)));
141 fputs("SESSIONS=", f);
142 LIST_FOREACH(sessions_by_user, i, u->sessions) {
146 i->sessions_by_user_next ? ' ' : '\n');
150 LIST_FOREACH(sessions_by_user, i, u->sessions) {
155 i->sessions_by_user_next ? ' ' : '\n');
158 fputs("ACTIVE_SESSIONS=", f);
159 LIST_FOREACH(sessions_by_user, i, u->sessions)
160 if (session_is_active(i))
163 (unsigned long) i->user->uid,
164 i->sessions_by_user_next ? ' ' : '\n');
166 fputs("ACTIVE_SEATS=", f);
167 LIST_FOREACH(sessions_by_user, i, u->sessions) {
168 if (session_is_active(i) && i->seat)
172 i->sessions_by_user_next ? ' ' : '\n');
178 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
180 unlink(u->state_file);
189 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
194 int user_load(User *u) {
196 char *display = NULL;
201 r = parse_env_file(u->state_file, NEWLINE,
202 "CGROUP", &u->cgroup_path,
203 "RUNTIME", &u->runtime_path,
204 "SERVICE", &u->service,
213 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
218 s = hashmap_get(u->manager->sessions, display);
222 if (s && s->display && display_is_local(s->display))
228 static int user_mkdir_runtime_path(User *u) {
234 r = safe_mkdir("/run/user", 0755, 0, 0);
236 log_error("Failed to create /run/user: %s", strerror(-r));
240 if (!u->runtime_path) {
241 p = strappend("/run/user/", u->name);
244 log_error("Out of memory");
250 r = safe_mkdir(p, 0700, u->uid, u->gid);
252 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
254 u->runtime_path = NULL;
262 static int user_create_cgroup(User *u) {
269 if (!u->cgroup_path) {
270 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0) {
271 log_error("Out of memory");
277 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
279 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
281 u->cgroup_path = NULL;
287 STRV_FOREACH(k, u->manager->controllers) {
289 if (strv_contains(u->manager->reset_controllers, *k))
292 r = cg_create(*k, p);
294 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
300 static int user_start_service(User *u) {
303 /* FIXME: Fill me in later ... */
308 int user_start(User *u) {
316 log_debug("New user %s logged in.", u->name);
318 /* Make XDG_RUNTIME_DIR */
319 r = user_mkdir_runtime_path(u);
324 r = user_create_cgroup(u);
328 /* Spawn user systemd */
329 r = user_start_service(u);
333 dual_timestamp_get(&u->timestamp);
337 /* Save new user data */
340 user_send_signal(u, true);
345 static int user_stop_service(User *u) {
354 static int user_shall_kill(User *u) {
357 if (!u->manager->kill_user_processes)
360 if (strv_contains(u->manager->kill_exclude_users, u->name))
363 if (strv_isempty(u->manager->kill_only_users))
366 return strv_contains(u->manager->kill_only_users, u->name);
369 static int user_terminate_cgroup(User *u) {
378 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
380 if (user_shall_kill(u)) {
382 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
384 log_error("Failed to kill user cgroup: %s", strerror(-r));
387 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
389 log_error("Failed to check user cgroup: %s", strerror(-r));
391 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
393 log_error("Failed to delete user cgroup: %s", strerror(-r));
398 STRV_FOREACH(k, u->manager->controllers)
399 cg_trim(*k, u->cgroup_path, true);
401 free(u->cgroup_path);
402 u->cgroup_path = NULL;
407 static int user_remove_runtime_path(User *u) {
412 if (!u->runtime_path)
415 r = rm_rf(u->runtime_path, false, true, false);
417 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
419 free(u->runtime_path);
420 u->runtime_path = NULL;
425 int user_stop(User *u) {
431 log_debug("User %s logged out.", u->name);
433 LIST_FOREACH(sessions_by_user, s, u->sessions) {
440 k = user_stop_service(u);
445 k = user_terminate_cgroup(u);
449 /* Kill XDG_RUNTIME_DIR */
450 k = user_remove_runtime_path(u);
454 unlink(u->state_file);
455 user_add_to_gc_queue(u);
458 user_send_signal(u, false);
465 int user_get_idle_hint(User *u, dual_timestamp *t) {
467 bool idle_hint = true;
468 dual_timestamp ts = { 0, 0 };
472 LIST_FOREACH(sessions_by_user, s, u->sessions) {
476 ih = session_get_idle_hint(s, &k);
482 if (k.monotonic < ts.monotonic)
488 } else if (idle_hint) {
490 if (k.monotonic > ts.monotonic)
501 int user_check_gc(User *u, bool drop_not_started) {
507 if (drop_not_started && !u->started)
513 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
516 r = access(p, F_OK) >= 0;
522 if (u->cgroup_path) {
523 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
534 void user_add_to_gc_queue(User *u) {
540 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
541 u->in_gc_queue = true;
544 UserState user_get_state(User *u) {
550 return USER_LINGERING;
552 LIST_FOREACH(sessions_by_user, i, u->sessions)
553 if (session_is_active(i))
559 int user_kill(User *u, int signo) {
568 pid_set = set_new(trivial_hash_func, trivial_compare_func);
572 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
574 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
583 static const char* const user_state_table[_USER_STATE_MAX] = {
584 [USER_OFFLINE] = "offline",
585 [USER_LINGERING] = "lingering",
586 [USER_ONLINE] = "online",
587 [USER_ACTIVE] = "active"
590 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);