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>
31 #include "path-util.h"
33 #include "unit-name.h"
35 #include "bus-error.h"
36 #include "conf-parser.h"
37 #include "clean-ipc.h"
38 #include "logind-user.h"
39 #include "smack-util.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/"UID_FMT, uid) < 0)
58 if (hashmap_put(m->users, UID_TO_PTR(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);
84 free(u->runtime_path);
86 hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
93 int user_save(User *u) {
94 _cleanup_free_ char *temp_path = NULL;
95 _cleanup_fclose_ FILE *f = NULL;
99 assert(u->state_file);
104 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
108 r = fopen_temporary(u->state_file, &f, &temp_path);
112 fchmod(fileno(f), 0644);
115 "# This is private data. Do not parse.\n"
119 user_state_to_string(user_get_state(u)));
122 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
125 fprintf(f, "DISPLAY=%s\n", u->display->id);
127 if (dual_timestamp_is_set(&u->timestamp))
129 "REALTIME="USEC_FMT"\n"
130 "MONOTONIC="USEC_FMT"\n",
131 u->timestamp.realtime,
132 u->timestamp.monotonic);
138 fputs("SESSIONS=", f);
140 LIST_FOREACH(sessions_by_user, i, u->sessions) {
149 fputs("\nSEATS=", f);
151 LIST_FOREACH(sessions_by_user, i, u->sessions) {
160 fputs(i->seat->id, f);
163 fputs("\nACTIVE_SESSIONS=", f);
165 LIST_FOREACH(sessions_by_user, i, u->sessions) {
166 if (!session_is_active(i))
177 fputs("\nONLINE_SESSIONS=", f);
179 LIST_FOREACH(sessions_by_user, i, u->sessions) {
180 if (session_get_state(i) == SESSION_CLOSING)
191 fputs("\nACTIVE_SEATS=", f);
193 LIST_FOREACH(sessions_by_user, i, u->sessions) {
194 if (!session_is_active(i) || !i->seat)
202 fputs(i->seat->id, f);
205 fputs("\nONLINE_SEATS=", f);
207 LIST_FOREACH(sessions_by_user, i, u->sessions) {
208 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
216 fputs(i->seat->id, f);
223 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
225 unlink(u->state_file);
231 log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
236 int user_load(User *u) {
237 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
243 r = parse_env_file(u->state_file, NEWLINE,
244 "RUNTIME", &u->runtime_path,
246 "REALTIME", &realtime,
247 "MONOTONIC", &monotonic,
253 log_error_errno(r, "Failed to read %s: %m", u->state_file);
258 s = hashmap_get(u->manager->sessions, display);
260 if (s && s->display && display_is_local(s->display))
264 unsigned long long l;
265 if (sscanf(realtime, "%llu", &l) > 0)
266 u->timestamp.realtime = l;
270 unsigned long long l;
271 if (sscanf(monotonic, "%llu", &l) > 0)
272 u->timestamp.monotonic = l;
278 static int user_mkdir_runtime_path(User *u) {
284 r = mkdir_safe_label("/run/user", 0755, 0, 0);
286 return log_error_errno(r, "Failed to create /run/user: %m");
288 if (!u->runtime_path) {
289 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
294 if (path_is_mount_point(p, false) <= 0) {
295 _cleanup_free_ char *t = NULL;
297 (void) mkdir(p, 0700);
300 r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
302 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
308 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
310 if (errno != EPERM) {
311 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
315 /* Lacking permissions, maybe
316 * CAP_SYS_ADMIN-less container? In this case,
317 * just use a normal directory. */
319 r = chmod_and_chown(p, 0700, u->uid, u->gid);
321 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
332 /* Try to clean up, but ignore errors */
337 u->runtime_path = NULL;
341 int user_start(User *u) {
349 log_debug("New user %s logged in.", u->name);
351 /* Make XDG_RUNTIME_DIR */
352 r = user_mkdir_runtime_path(u);
356 if (!dual_timestamp_is_set(&u->timestamp))
357 dual_timestamp_get(&u->timestamp);
361 /* Save new user data */
364 user_send_signal(u, true);
369 static int user_remove_runtime_path(User *u) {
374 if (!u->runtime_path)
377 r = rm_rf(u->runtime_path, false, false, false);
379 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
381 /* Ignore cases where the directory isn't mounted, as that's
382 * quite possible, if we lacked the permissions to mount
384 r = umount2(u->runtime_path, MNT_DETACH);
385 if (r < 0 && errno != EINVAL && errno != ENOENT)
386 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
388 r = rm_rf(u->runtime_path, false, true, false);
390 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
392 free(u->runtime_path);
393 u->runtime_path = NULL;
398 int user_stop(User *u, bool force) {
403 /* Stop jobs have already been queued */
409 LIST_FOREACH(sessions_by_user, s, u->sessions) {
410 k = session_stop(s, force);
422 int user_finalize(User *u) {
429 log_debug("User %s logged out.", u->name);
431 LIST_FOREACH(sessions_by_user, s, u->sessions) {
432 k = session_finalize(s);
437 /* Kill XDG_RUNTIME_DIR */
438 k = user_remove_runtime_path(u);
442 /* Clean SysV + POSIX IPC objects */
443 if (u->manager->remove_ipc) {
444 k = clean_ipc(u->uid);
449 unlink(u->state_file);
450 user_add_to_gc_queue(u);
453 user_send_signal(u, false);
460 int user_get_idle_hint(User *u, dual_timestamp *t) {
462 bool idle_hint = true;
463 dual_timestamp ts = { 0, 0 };
467 LIST_FOREACH(sessions_by_user, s, u->sessions) {
471 ih = session_get_idle_hint(s, &k);
477 if (k.monotonic < ts.monotonic)
483 } else if (idle_hint) {
485 if (k.monotonic > ts.monotonic)
496 int user_check_linger_file(User *u) {
497 _cleanup_free_ char *cc = NULL;
500 cc = cescape(u->name);
504 p = strjoina("/var/lib/systemd/linger/", cc);
506 return access(p, F_OK) >= 0;
509 bool user_check_gc(User *u, bool drop_not_started) {
512 if (drop_not_started && !u->started)
518 if (user_check_linger_file(u) > 0)
524 void user_add_to_gc_queue(User *u) {
530 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
531 u->in_gc_queue = true;
534 UserState user_get_state(User *u) {
543 bool all_closing = true;
545 LIST_FOREACH(sessions_by_user, i, u->sessions) {
548 state = session_get_state(i);
549 if (state == SESSION_ACTIVE)
551 if (state != SESSION_CLOSING)
555 return all_closing ? USER_CLOSING : USER_ONLINE;
558 if (user_check_linger_file(u) > 0)
559 return USER_LINGERING;
564 int user_kill(User *u, int signo) {
570 LIST_FOREACH(sessions_by_user, s, u->sessions) {
571 int r = session_kill(s, KILL_ALL, signo);
572 if (res == 0 && r < 0)
579 void user_elect_display(User *u) {
580 Session *graphical = NULL, *text = NULL, *other = NULL, *s;
584 /* This elects a primary session for each user, which we call
585 * the "display". We try to keep the assignment stable, but we
586 * "upgrade" to better choices. */
588 LIST_FOREACH(sessions_by_user, s, u->sessions) {
590 if (s->class != SESSION_USER)
596 if (SESSION_TYPE_IS_GRAPHICAL(s->type))
598 else if (s->type == SESSION_TTY)
606 u->display->class != SESSION_USER ||
607 u->display->stopping ||
608 !SESSION_TYPE_IS_GRAPHICAL(u->display->type))) {
609 u->display = graphical;
615 u->display->class != SESSION_USER ||
616 u->display->stopping ||
617 u->display->type != SESSION_TTY)) {
624 u->display->class != SESSION_USER ||
625 u->display->stopping))
629 static const char* const user_state_table[_USER_STATE_MAX] = {
630 [USER_OFFLINE] = "offline",
631 [USER_OPENING] = "opening",
632 [USER_LINGERING] = "lingering",
633 [USER_ONLINE] = "online",
634 [USER_ACTIVE] = "active",
635 [USER_CLOSING] = "closing"
638 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
640 int config_parse_tmpfs_size(
642 const char *filename,
645 unsigned section_line,
661 e = endswith(rvalue, "%");
667 ul = strtoul(rvalue, &f, 10);
668 if (errno != 0 || f != e) {
669 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
673 if (ul <= 0 || ul >= 100) {
674 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
678 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
682 r = parse_size(rvalue, 1024, &o);
683 if (r < 0 || (off_t) (size_t) o != o) {
684 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
688 *sz = PAGE_ALIGN((size_t) o);