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/>.
22 #include <sys/mount.h>
32 #include "path-util.h"
34 #include "unit-name.h"
36 #include "bus-error.h"
37 #include "conf-parser.h"
38 #include "clean-ipc.h"
39 #include "logind-user.h"
41 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
51 u->name = strdup(name);
55 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
58 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0)
75 void user_free(User *u) {
79 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
82 session_free(u->sessions);
85 hashmap_remove(u->manager->user_units, u->slice);
90 hashmap_remove(u->manager->user_units, u->service);
97 free(u->runtime_path);
99 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
106 int user_save(User *u) {
107 _cleanup_free_ char *temp_path = NULL;
108 _cleanup_fclose_ FILE *f = NULL;
112 assert(u->state_file);
117 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
121 r = fopen_temporary(u->state_file, &f, &temp_path);
125 fchmod(fileno(f), 0644);
128 "# This is private data. Do not parse.\n"
132 user_state_to_string(user_get_state(u)));
135 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
138 fprintf(f, "SERVICE=%s\n", u->service);
140 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
143 fprintf(f, "SLICE=%s\n", u->slice);
145 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
148 fprintf(f, "DISPLAY=%s\n", u->display->id);
150 if (dual_timestamp_is_set(&u->timestamp))
152 "REALTIME="USEC_FMT"\n"
153 "MONOTONIC="USEC_FMT"\n",
154 u->timestamp.realtime,
155 u->timestamp.monotonic);
161 fputs("SESSIONS=", f);
163 LIST_FOREACH(sessions_by_user, i, u->sessions) {
172 fputs("\nSEATS=", f);
174 LIST_FOREACH(sessions_by_user, i, u->sessions) {
183 fputs(i->seat->id, f);
186 fputs("\nACTIVE_SESSIONS=", f);
188 LIST_FOREACH(sessions_by_user, i, u->sessions) {
189 if (!session_is_active(i))
200 fputs("\nONLINE_SESSIONS=", f);
202 LIST_FOREACH(sessions_by_user, i, u->sessions) {
203 if (session_get_state(i) == SESSION_CLOSING)
214 fputs("\nACTIVE_SEATS=", f);
216 LIST_FOREACH(sessions_by_user, i, u->sessions) {
217 if (!session_is_active(i) || !i->seat)
225 fputs(i->seat->id, f);
228 fputs("\nONLINE_SEATS=", f);
230 LIST_FOREACH(sessions_by_user, i, u->sessions) {
231 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
239 fputs(i->seat->id, f);
246 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
248 unlink(u->state_file);
254 log_error("Failed to save user data %s: %s", u->state_file, strerror(-r));
259 int user_load(User *u) {
260 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
266 r = parse_env_file(u->state_file, NEWLINE,
267 "RUNTIME", &u->runtime_path,
268 "SERVICE", &u->service,
269 "SERVICE_JOB", &u->service_job,
271 "SLICE_JOB", &u->slice_job,
273 "REALTIME", &realtime,
274 "MONOTONIC", &monotonic,
280 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
285 s = hashmap_get(u->manager->sessions, display);
287 if (s && s->display && display_is_local(s->display))
291 unsigned long long l;
292 if (sscanf(realtime, "%llu", &l) > 0)
293 u->timestamp.realtime = l;
297 unsigned long long l;
298 if (sscanf(monotonic, "%llu", &l) > 0)
299 u->timestamp.monotonic = l;
305 static int user_mkdir_runtime_path(User *u) {
311 r = mkdir_safe_label("/run/user", 0755, 0, 0);
313 log_error("Failed to create /run/user: %s", strerror(-r));
317 if (!u->runtime_path) {
318 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
323 if (path_is_mount_point(p, false) <= 0) {
324 _cleanup_free_ char *t = NULL;
328 if (asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size) < 0) {
333 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
335 log_error("Failed to mount per-user tmpfs directory %s: %s", p, strerror(-r));
345 u->runtime_path = NULL;
349 static int user_start_slice(User *u) {
356 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
357 char lu[DECIMAL_STR_MAX(unsigned long) + 1], *slice;
358 sprintf(lu, "%lu", (unsigned long) u->uid);
360 r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
364 r = manager_start_unit(u->manager, slice, &error, &job);
366 log_error("Failed to start user slice: %s", bus_error_message(&error, r));
377 hashmap_put(u->manager->user_units, u->slice, u);
382 static int user_start_service(User *u) {
383 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
390 char lu[DECIMAL_STR_MAX(unsigned long) + 1], *service;
391 sprintf(lu, "%lu", (unsigned long) u->uid);
393 service = unit_name_build("user", lu, ".service");
397 r = manager_start_unit(u->manager, service, &error, &job);
399 log_error("Failed to start user service: %s", bus_error_message(&error, r));
402 u->service = service;
404 free(u->service_job);
405 u->service_job = job;
410 hashmap_put(u->manager->user_units, u->service, u);
415 int user_start(User *u) {
423 log_debug("New user %s logged in.", u->name);
425 /* Make XDG_RUNTIME_DIR */
426 r = user_mkdir_runtime_path(u);
431 r = user_start_slice(u);
435 /* Spawn user systemd */
436 r = user_start_service(u);
440 if (!dual_timestamp_is_set(&u->timestamp))
441 dual_timestamp_get(&u->timestamp);
445 /* Save new user data */
448 user_send_signal(u, true);
453 static int user_stop_slice(User *u) {
454 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
463 r = manager_stop_unit(u->manager, u->slice, &error, &job);
465 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
475 static int user_stop_service(User *u) {
476 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
485 r = manager_stop_unit(u->manager, u->service, &error, &job);
487 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
491 free(u->service_job);
492 u->service_job = job;
497 static int user_remove_runtime_path(User *u) {
502 if (!u->runtime_path)
505 r = rm_rf(u->runtime_path, false, false, false);
507 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
509 if (umount2(u->runtime_path, MNT_DETACH) < 0)
510 log_error("Failed to unmount user runtime directory %s: %m", u->runtime_path);
512 r = rm_rf(u->runtime_path, false, true, false);
514 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
516 free(u->runtime_path);
517 u->runtime_path = NULL;
522 int user_stop(User *u, bool force) {
527 /* Stop jobs have already been queued */
533 LIST_FOREACH(sessions_by_user, s, u->sessions) {
534 k = session_stop(s, force);
540 k = user_stop_service(u);
545 k = user_stop_slice(u);
556 int user_finalize(User *u) {
563 log_debug("User %s logged out.", u->name);
565 LIST_FOREACH(sessions_by_user, s, u->sessions) {
566 k = session_finalize(s);
571 /* Kill XDG_RUNTIME_DIR */
572 k = user_remove_runtime_path(u);
576 /* Clean SysV + POSIX IPC objects */
577 if (u->manager->remove_ipc) {
578 k = clean_ipc(u->uid);
583 unlink(u->state_file);
584 user_add_to_gc_queue(u);
587 user_send_signal(u, false);
594 int user_get_idle_hint(User *u, dual_timestamp *t) {
596 bool idle_hint = true;
597 dual_timestamp ts = { 0, 0 };
601 LIST_FOREACH(sessions_by_user, s, u->sessions) {
605 ih = session_get_idle_hint(s, &k);
611 if (k.monotonic < ts.monotonic)
617 } else if (idle_hint) {
619 if (k.monotonic > ts.monotonic)
630 int user_check_linger_file(User *u) {
631 _cleanup_free_ char *cc = NULL;
634 cc = cescape(u->name);
638 p = strappenda("/var/lib/systemd/linger/", cc);
640 return access(p, F_OK) >= 0;
643 bool user_check_gc(User *u, bool drop_not_started) {
646 if (drop_not_started && !u->started)
652 if (user_check_linger_file(u) > 0)
655 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
658 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
664 void user_add_to_gc_queue(User *u) {
670 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
671 u->in_gc_queue = true;
674 UserState user_get_state(User *u) {
682 if (u->slice_job || u->service_job)
686 bool all_closing = true;
688 LIST_FOREACH(sessions_by_user, i, u->sessions) {
691 state = session_get_state(i);
692 if (state == SESSION_ACTIVE)
694 if (state != SESSION_CLOSING)
698 return all_closing ? USER_CLOSING : USER_ONLINE;
701 if (user_check_linger_file(u) > 0)
702 return USER_LINGERING;
707 int user_kill(User *u, int signo) {
713 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
716 static const char* const user_state_table[_USER_STATE_MAX] = {
717 [USER_OFFLINE] = "offline",
718 [USER_OPENING] = "opening",
719 [USER_LINGERING] = "lingering",
720 [USER_ONLINE] = "online",
721 [USER_ACTIVE] = "active",
722 [USER_CLOSING] = "closing"
725 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
727 int config_parse_tmpfs_size(
729 const char *filename,
732 unsigned section_line,
748 e = endswith(rvalue, "%");
754 ul = strtoul(rvalue, &f, 10);
755 if (errno != 0 || f != e) {
756 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
760 if (ul <= 0 || ul >= 100) {
761 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
765 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
769 r = parse_size(rvalue, 1024, &o);
770 if (r < 0 || (off_t) (size_t) o != o) {
771 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
775 *sz = PAGE_ALIGN((size_t) o);