logind-user.c revision 90558f315844ec35e3fd4f1a19ac38c8721c9354
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2011 Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart PoetteringUser* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering hashmap_remove(u->manager->user_units, u->slice);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering hashmap_remove(u->manager->user_units, u->service);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering r = fopen_temporary(u->state_file, &f, &temp_path);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering "# This is private data. Do not parse.\n"
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering user_state_to_string(user_get_state(u)));
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering fprintf(f, "RUNTIME=%s\n", u->runtime_path);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering fprintf(f, "DISPLAY=%s\n", u->display->id);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering if (dual_timestamp_is_set(&u->timestamp))
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
8bf52d3d17d364438191077d0750b8b80b5dc53aLennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
8bf52d3d17d364438191077d0750b8b80b5dc53aLennart Poettering if (session_get_state(i) == SESSION_CLOSING)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering LIST_FOREACH(sessions_by_user, i, u->sessions) {
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering if (session_get_state(i) == SESSION_CLOSING || !i->seat)
9de3e3294065e8697ff10130b53f274319cdcf6fZbigniew Jędrzejewski-Szmek if (rename(temp_path, u->state_file) < 0) {
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering return log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
9de3e3294065e8697ff10130b53f274319cdcf6fZbigniew Jędrzejewski-Szmek _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering r = parse_env_file(u->state_file, NEWLINE,
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering log_error_errno(r, "Failed to read %s: %m", u->state_file);
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering s = hashmap_get(u->manager->sessions, display);
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering if (s && s->display && display_is_local(s->display))
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering unsigned long long l;
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering unsigned long long l;
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poetteringstatic int user_mkdir_runtime_path(User *u) {
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering r = mkdir_safe_label("/run/user", 0755, 0, 0);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering return log_error_errno(r, "Failed to create /run/user: %m");
fd0b4602f6332c3f1660eb208c8f5c719709a009Lennart Poettering if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
9de3e3294065e8697ff10130b53f274319cdcf6fZbigniew Jędrzejewski-Szmek r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek /* Lacking permissions, maybe
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek * CAP_SYS_ADMIN-less container? In this case,
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek * just use a normal directory. */
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek r = chmod_and_chown(p, 0700, u->uid, u->gid);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering r = label_fix(p, false, false);
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering /* Try to clean up, but ignore errors */
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmekstatic int user_start_slice(User *u) {
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice, *job;
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek u->slice_job = mfree(u->slice_job);
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice);
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering return log_error_errno(r, "Failed to build slice name: %m");
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering description = strjoina("User Slice of ", u->name);
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek "systemd-user-sessions.service",
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering if (sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS))
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek /* The slice already exists? If so, that's fine, let's just reuse it */
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", slice, bus_error_message(&error, r), error.name);
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek /* we don't fail due to this, let's try to continue */
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek (void) hashmap_put(u->manager->user_units, u->slice, u);
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmekstatic int user_start_service(User *u) {
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek r = unit_name_build("user", lu, ".service", &service);
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek return log_error_errno(r, "Failed to build service name: %m");
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r));
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek /* we don't fail due to this, let's try to continue */
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering (void) hashmap_put(u->manager->user_units, u->service, u);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering log_debug("New user %s logged in.", u->name);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering /* Make XDG_RUNTIME_DIR */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek /* Create cgroup */
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek /* Save the user data so far, because pam_systemd will read the
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek * XDG_RUNTIME_DIR out of it while starting up systemd --user.
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek * We need to do user_save_internal() because we have not
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek * "officially" started yet. */
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering /* Spawn user systemd */
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering if (!dual_timestamp_is_set(&u->timestamp))
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering /* Save new user data */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmekstatic int user_stop_slice(User *u) {
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering r = manager_stop_unit(u->manager, u->slice, &error, &job);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmekstatic int user_stop_service(User *u) {
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek r = manager_stop_unit(u->manager, u->service, &error, &job);
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek log_error("Failed to stop user service: %s", bus_error_message(&error, r));
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmekstatic int user_remove_runtime_path(User *u) {
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering /* Ignore cases where the directory isn't mounted, as that's
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering * quite possible, if we lacked the permissions to mount
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering * something */
ff3d6560bead6879a2fed1bf99bfe8273b3723f1Zbigniew Jędrzejewski-Szmek r = umount2(u->runtime_path, MNT_DETACH);
ff3d6560bead6879a2fed1bf99bfe8273b3723f1Zbigniew Jędrzejewski-Szmek if (r < 0 && errno != EINVAL && errno != ENOENT)
ff3d6560bead6879a2fed1bf99bfe8273b3723f1Zbigniew Jędrzejewski-Szmek log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
ff3d6560bead6879a2fed1bf99bfe8273b3723f1Zbigniew Jędrzejewski-Szmek r = rm_rf(u->runtime_path, REMOVE_ROOT);
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering u->runtime_path = mfree(u->runtime_path);
ff3d6560bead6879a2fed1bf99bfe8273b3723f1Zbigniew Jędrzejewski-Szmekint user_stop(User *u, bool force) {
ff3d6560bead6879a2fed1bf99bfe8273b3723f1Zbigniew Jędrzejewski-Szmek /* Stop jobs have already been queued */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek LIST_FOREACH(sessions_by_user, s, u->sessions) {
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering /* Kill systemd */
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering /* Kill cgroup */
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering log_debug("User %s logged out.", u->name);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering LIST_FOREACH(sessions_by_user, s, u->sessions) {
k = user_remove_runtime_path(u);
if (u->started) {
user_send_signal(u, false);
u->started = false;
Session *s;
bool idle_hint = true;
assert(u);
int ih;
if (ih < 0)
return ih;
if (!ih) {
if (!idle_hint) {
ts = k;
idle_hint = false;
ts = k;
} else if (idle_hint) {
ts = k;
*t = ts;
return idle_hint;
char *p = NULL;
if (!cc)
return -ENOMEM;
assert(u);
if (u->sessions)
if (user_check_linger_file(u) > 0)
assert(u);
if (u->in_gc_queue)
u->in_gc_queue = true;
Session *i;
assert(u);
if (u->stopping)
return USER_CLOSING;
return USER_OPENING;
if (u->sessions) {
bool all_closing = true;
return USER_ACTIVE;
all_closing = false;
if (user_check_linger_file(u) > 0)
return USER_LINGERING;
return USER_CLOSING;
assert(u);
if (!u->slice)
return -ESRCH;
assert(s);
[SESSION_UNSPECIFIED] = 0,
Session *s;
assert(u);
if (!elect_display_filter(s)) {
u->display = s;
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
unsigned long ul;
errno = 0;
if (errno != 0 || f != e) {
log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0, "Percentage value out of range, ignoring: %s", rvalue);
uint64_t k;