logind-user.c revision a9c67ea2a24d61640971fc1ca366a3e6c7d75266
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen This file is part of systemd.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Copyright 2011 Lennart Poettering
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen systemd is free software; you can redistribute it and/or modify it
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen under the terms of the GNU Lesser General Public License as published by
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen (at your option) any later version.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen systemd is distributed in the hope that it will be useful, but
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Lesser General Public License for more details.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen You should have received a copy of the GNU Lesser General Public License
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart PoetteringUser* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
21d73c87b09ec2b8642424bc714ce9af3da4fc40Lennart Poettering LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
a2a416f768e2aa7db5b975cd50eb19237cac9cceLennart Poettering hashmap_remove(u->manager->user_units, u->slice);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering hashmap_remove(u->manager->user_units, u->service);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = fopen_temporary(u->state_file, &f, &temp_path);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering "# This is private data. Do not parse.\n"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering user_state_to_string(user_get_state(u)));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering fprintf(f, "RUNTIME=%s\n", u->runtime_path);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (dual_timestamp_is_set(&u->timestamp))
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (session_get_state(i) == SESSION_CLOSING)
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen LIST_FOREACH(sessions_by_user, i, u->sessions) {
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen LIST_FOREACH(sessions_by_user, i, u->sessions) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (session_get_state(i) == SESSION_CLOSING || !i->seat)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (ferror(f) || rename(temp_path, u->state_file) < 0) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = parse_env_file(u->state_file, NEWLINE,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_error_errno(r, "Failed to read %s: %m", u->state_file);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering s = hashmap_get(u->manager->sessions, display);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (s && s->display && display_is_local(s->display))
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering unsigned long long l;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering unsigned long long l;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int user_mkdir_runtime_path(User *u) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = mkdir_safe_label("/run/user", 0755, 0, 0);
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering return log_error_errno(r, "Failed to create /run/user: %m");
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering if (path_is_mount_point(p, false) <= 0) {
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering /* Lacking permissions, maybe
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering * CAP_SYS_ADMIN-less container? In this case,
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering * just use a normal directory. */
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering r = chmod_and_chown(p, 0700, u->uid, u->gid);
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering /* Try to clean up, but ignore errors */
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering r = manager_start_unit(u->manager, slice, &error, &job);
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering log_error("Failed to start user slice: %s", bus_error_message(&error, r));
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering hashmap_put(u->manager->user_units, u->slice, u);
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
8d3d7072e609ef0e0fb37e1d19a29307d58146c3Michal Schmidt service = unit_name_build("user", lu, ".service");
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering r = manager_start_unit(u->manager, service, &error, &job);
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering log_error("Failed to start user service: %s", bus_error_message(&error, r));
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering hashmap_put(u->manager->user_units, u->service, u);
4d506d6bb757af3b99e0876234c465e6898c5ea4Lennart Poettering log_debug("New user %s logged in.", u->name);
4d506d6bb757af3b99e0876234c465e6898c5ea4Lennart Poettering /* Make XDG_RUNTIME_DIR */
4d506d6bb757af3b99e0876234c465e6898c5ea4Lennart Poettering /* Create cgroup */
4d506d6bb757af3b99e0876234c465e6898c5ea4Lennart Poettering /* Spawn user systemd */
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen /* Save new user data */
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen r = manager_stop_unit(u->manager, u->slice, &error, &job);
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = manager_stop_unit(u->manager, u->service, &error, &job);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_error("Failed to stop user service: %s", bus_error_message(&error, r));
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poetteringstatic int user_remove_runtime_path(User *u) {
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering r = rm_rf(u->runtime_path, false, false, false);
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Ignore cases where the directory isn't mounted, as that's
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen * quite possible, if we lacked the permissions to mount
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen * something */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = umount2(u->runtime_path, MNT_DETACH);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r < 0 && errno != EINVAL && errno != ENOENT)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
f0e1546763304aedc90e91d70dab9eeb7c966cf8Lennart Poettering r = rm_rf(u->runtime_path, false, true, false);
f0e1546763304aedc90e91d70dab9eeb7c966cf8Lennart Poettering log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering /* Stop jobs have already been queued */
4d506d6bb757af3b99e0876234c465e6898c5ea4Lennart Poettering LIST_FOREACH(sessions_by_user, s, u->sessions) {
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering /* Kill systemd */
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering /* Kill cgroup */
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering log_debug("User %s logged out.", u->name);
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering LIST_FOREACH(sessions_by_user, s, u->sessions) {
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering /* Kill XDG_RUNTIME_DIR */
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering /* Clean SysV + POSIX IPC objects */
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poetteringint user_get_idle_hint(User *u, dual_timestamp *t) {
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering LIST_FOREACH(sessions_by_user, s, u->sessions) {
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering p = strjoina("/var/lib/systemd/linger/", cc);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersenbool user_check_gc(User *u, bool drop_not_started) {
36a03ca2a8952ca1acb29fbe796210c27ff71affLennart Poettering if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering if (u->service_job && manager_job_is_active(u->manager, u->service_job))
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return false;
bda2c408f8a739c19161818bcc842107f60652a2Tom Gundersen LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen LIST_FOREACH(sessions_by_user, i, u->sessions) {
bda2c408f8a739c19161818bcc842107f60652a2Tom Gundersen return all_closing ? USER_CLOSING : USER_ONLINE;
bda2c408f8a739c19161818bcc842107f60652a2Tom Gundersen return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
4713135eae4f7b6b670a98476fe413edfa1d9f41Zbigniew Jędrzejewski-Szmek Session *graphical = NULL, *text = NULL, *other = NULL, *s;
4713135eae4f7b6b670a98476fe413edfa1d9f41Zbigniew Jędrzejewski-Szmek /* This elects a primary session for each user, which we call
4713135eae4f7b6b670a98476fe413edfa1d9f41Zbigniew Jędrzejewski-Szmek * the "display". We try to keep the assignment stable, but we
4713135eae4f7b6b670a98476fe413edfa1d9f41Zbigniew Jędrzejewski-Szmek * "upgrade" to better choices. */
822db23cfa98a9fbc48f41e11caafb6f1017e052Lennart Poettering LIST_FOREACH(sessions_by_user, s, u->sessions) {
4713135eae4f7b6b670a98476fe413edfa1d9f41Zbigniew Jędrzejewski-Szmek if (SESSION_TYPE_IS_GRAPHICAL(s->type))
4713135eae4f7b6b670a98476fe413edfa1d9f41Zbigniew Jędrzejewski-Szmek u->display->class != SESSION_USER ||
4713135eae4f7b6b670a98476fe413edfa1d9f41Zbigniew Jędrzejewski-Szmek !SESSION_TYPE_IS_GRAPHICAL(u->display->type))) {
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poetteringstatic const char* const user_state_table[_USER_STATE_MAX] = {
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart PoetteringDEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering const char *e;
bda2c408f8a739c19161818bcc842107f60652a2Tom Gundersen unsigned long ul;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (errno != 0 || f != e) {
822db23cfa98a9fbc48f41e11caafb6f1017e052Lennart Poettering log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);