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"
35 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
45 u->name = strdup(name);
49 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
52 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);
84 free(u->runtime_path);
86 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
94 int user_save(User *u) {
95 _cleanup_free_ char *temp_path = NULL;
96 _cleanup_fclose_ FILE *f = NULL;
100 assert(u->state_file);
105 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
109 r = fopen_temporary(u->state_file, &f, &temp_path);
113 fchmod(fileno(f), 0644);
116 "# This is private data. Do not parse.\n"
120 user_state_to_string(user_get_state(u)));
123 fprintf(f, "CGROUP=%s\n", u->cgroup_path);
126 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
129 fprintf(f, "SERVICE=%s\n", u->service);
132 fprintf(f, "SLICE=%s\n", u->slice);
135 fprintf(f, "DISPLAY=%s\n", u->display->id);
137 if (dual_timestamp_is_set(&u->timestamp))
141 (unsigned long long) u->timestamp.realtime,
142 (unsigned long long) u->timestamp.monotonic);
148 fputs("SESSIONS=", f);
150 LIST_FOREACH(sessions_by_user, i, u->sessions) {
159 fputs("\nSEATS=", f);
161 LIST_FOREACH(sessions_by_user, i, u->sessions) {
170 fputs(i->seat->id, f);
173 fputs("\nACTIVE_SESSIONS=", f);
175 LIST_FOREACH(sessions_by_user, i, u->sessions) {
176 if (!session_is_active(i))
187 fputs("\nONLINE_SESSIONS=", f);
189 LIST_FOREACH(sessions_by_user, i, u->sessions) {
190 if (session_get_state(i) == SESSION_CLOSING)
201 fputs("\nACTIVE_SEATS=", f);
203 LIST_FOREACH(sessions_by_user, i, u->sessions) {
204 if (!session_is_active(i) || !i->seat)
212 fputs(i->seat->id, f);
215 fputs("\nONLINE_SEATS=", f);
217 LIST_FOREACH(sessions_by_user, i, u->sessions) {
218 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
226 fputs(i->seat->id, f);
233 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
235 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) {
247 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
253 r = parse_env_file(u->state_file, NEWLINE,
254 "CGROUP", &u->cgroup_path,
255 "RUNTIME", &u->runtime_path,
256 "SERVICE", &u->service,
259 "REALTIME", &realtime,
260 "MONOTONIC", &monotonic,
266 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
271 s = hashmap_get(u->manager->sessions, display);
273 if (s && s->display && display_is_local(s->display))
277 unsigned long long l;
278 if (sscanf(realtime, "%llu", &l) > 0)
279 u->timestamp.realtime = l;
283 unsigned long long l;
284 if (sscanf(monotonic, "%llu", &l) > 0)
285 u->timestamp.monotonic = l;
291 static int user_mkdir_runtime_path(User *u) {
297 r = mkdir_safe_label("/run/user", 0755, 0, 0);
299 log_error("Failed to create /run/user: %s", strerror(-r));
303 if (!u->runtime_path) {
304 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
309 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
311 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
313 u->runtime_path = NULL;
321 static int user_create_cgroup(User *u) {
328 u->slice = strdup(SPECIAL_USER_SLICE);
333 if (!u->cgroup_path) {
334 _cleanup_free_ char *name = NULL, *escaped = NULL, *slice = NULL;
336 if (asprintf(&name, "%lu.user", (unsigned long) u->uid) < 0)
339 escaped = cg_escape(name);
343 r = cg_slice_to_path(u->slice, &slice);
347 u->cgroup_path = strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
352 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
354 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", u->cgroup_path, strerror(-r));
358 STRV_FOREACH(k, u->manager->controllers) {
360 if (strv_contains(u->manager->reset_controllers, *k))
363 r = cg_create(*k, u->cgroup_path);
365 log_warning("Failed to create cgroup %s:%s: %s", *k, u->cgroup_path, strerror(-r));
368 r = hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
370 log_warning("Failed to create mapping between cgroup and user");
375 static int user_start_service(User *u) {
378 /* FIXME: Fill me in later ... */
383 int user_start(User *u) {
391 log_debug("New user %s logged in.", u->name);
393 /* Make XDG_RUNTIME_DIR */
394 r = user_mkdir_runtime_path(u);
399 r = user_create_cgroup(u);
403 /* Spawn user systemd */
404 r = user_start_service(u);
408 if (!dual_timestamp_is_set(&u->timestamp))
409 dual_timestamp_get(&u->timestamp);
413 /* Save new user data */
416 user_send_signal(u, true);
421 static int user_stop_service(User *u) {
430 static int user_shall_kill(User *u) {
433 if (!u->manager->kill_user_processes)
436 if (strv_contains(u->manager->kill_exclude_users, u->name))
439 if (strv_isempty(u->manager->kill_only_users))
442 return strv_contains(u->manager->kill_only_users, u->name);
445 static int user_terminate_cgroup(User *u) {
454 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
456 if (user_shall_kill(u)) {
458 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
460 log_error("Failed to kill user cgroup: %s", strerror(-r));
463 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
465 log_error("Failed to check user cgroup: %s", strerror(-r));
467 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
469 log_error("Failed to delete user cgroup: %s", strerror(-r));
474 STRV_FOREACH(k, u->manager->controllers)
475 cg_trim(*k, u->cgroup_path, true);
477 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
479 free(u->cgroup_path);
480 u->cgroup_path = NULL;
485 static int user_remove_runtime_path(User *u) {
490 if (!u->runtime_path)
493 r = rm_rf(u->runtime_path, false, true, false);
495 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
497 free(u->runtime_path);
498 u->runtime_path = NULL;
503 int user_stop(User *u) {
509 log_debug("User %s logged out.", u->name);
511 LIST_FOREACH(sessions_by_user, s, u->sessions) {
518 k = user_stop_service(u);
523 k = user_terminate_cgroup(u);
527 /* Kill XDG_RUNTIME_DIR */
528 k = user_remove_runtime_path(u);
532 unlink(u->state_file);
533 user_add_to_gc_queue(u);
536 user_send_signal(u, false);
543 int user_get_idle_hint(User *u, dual_timestamp *t) {
545 bool idle_hint = true;
546 dual_timestamp ts = { 0, 0 };
550 LIST_FOREACH(sessions_by_user, s, u->sessions) {
554 ih = session_get_idle_hint(s, &k);
560 if (k.monotonic < ts.monotonic)
566 } else if (idle_hint) {
568 if (k.monotonic > ts.monotonic)
579 static int user_check_linger_file(User *u) {
583 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
586 r = access(p, F_OK) >= 0;
592 int user_check_gc(User *u, bool drop_not_started) {
597 if (drop_not_started && !u->started)
603 if (user_check_linger_file(u) > 0)
606 if (u->cgroup_path) {
607 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
618 void user_add_to_gc_queue(User *u) {
624 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
625 u->in_gc_queue = true;
628 UserState user_get_state(User *u) {
630 bool all_closing = true;
635 LIST_FOREACH(sessions_by_user, i, u->sessions) {
636 if (session_is_active(i))
638 if (session_get_state(i) != SESSION_CLOSING)
643 return all_closing ? USER_CLOSING : USER_ONLINE;
645 if (user_check_linger_file(u) > 0)
646 return USER_LINGERING;
651 int user_kill(User *u, int signo) {
652 _cleanup_set_free_ Set *pid_set = NULL;
660 pid_set = set_new(trivial_hash_func, trivial_compare_func);
664 r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
665 if (r < 0 && (r != -EAGAIN && r != -ESRCH && r != -ENOENT))
671 static const char* const user_state_table[_USER_STATE_MAX] = {
672 [USER_OFFLINE] = "offline",
673 [USER_LINGERING] = "lingering",
674 [USER_ONLINE] = "online",
675 [USER_ACTIVE] = "active",
676 [USER_CLOSING] = "closing"
679 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);