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"
40 #include "smack-util.h"
41 #include "formats-util.h"
43 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
53 u->name = strdup(name);
57 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
60 if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
77 void user_free(User *u) {
81 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
84 session_free(u->sessions);
86 free(u->runtime_path);
88 hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
95 int user_save(User *u) {
96 _cleanup_free_ char *temp_path = NULL;
97 _cleanup_fclose_ FILE *f = NULL;
101 assert(u->state_file);
106 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
110 r = fopen_temporary(u->state_file, &f, &temp_path);
114 fchmod(fileno(f), 0644);
117 "# This is private data. Do not parse.\n"
121 user_state_to_string(user_get_state(u)));
124 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
127 fprintf(f, "DISPLAY=%s\n", u->display->id);
129 if (dual_timestamp_is_set(&u->timestamp))
131 "REALTIME="USEC_FMT"\n"
132 "MONOTONIC="USEC_FMT"\n",
133 u->timestamp.realtime,
134 u->timestamp.monotonic);
140 fputs("SESSIONS=", f);
142 LIST_FOREACH(sessions_by_user, i, u->sessions) {
151 fputs("\nSEATS=", f);
153 LIST_FOREACH(sessions_by_user, i, u->sessions) {
162 fputs(i->seat->id, f);
165 fputs("\nACTIVE_SESSIONS=", f);
167 LIST_FOREACH(sessions_by_user, i, u->sessions) {
168 if (!session_is_active(i))
179 fputs("\nONLINE_SESSIONS=", f);
181 LIST_FOREACH(sessions_by_user, i, u->sessions) {
182 if (session_get_state(i) == SESSION_CLOSING)
193 fputs("\nACTIVE_SEATS=", f);
195 LIST_FOREACH(sessions_by_user, i, u->sessions) {
196 if (!session_is_active(i) || !i->seat)
204 fputs(i->seat->id, f);
207 fputs("\nONLINE_SEATS=", f);
209 LIST_FOREACH(sessions_by_user, i, u->sessions) {
210 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
218 fputs(i->seat->id, f);
225 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
227 unlink(u->state_file);
233 log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
238 int user_load(User *u) {
239 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
245 r = parse_env_file(u->state_file, NEWLINE,
246 "RUNTIME", &u->runtime_path,
248 "REALTIME", &realtime,
249 "MONOTONIC", &monotonic,
255 log_error_errno(r, "Failed to read %s: %m", u->state_file);
260 s = hashmap_get(u->manager->sessions, display);
262 if (s && s->display && display_is_local(s->display))
266 unsigned long long l;
267 if (sscanf(realtime, "%llu", &l) > 0)
268 u->timestamp.realtime = l;
272 unsigned long long l;
273 if (sscanf(monotonic, "%llu", &l) > 0)
274 u->timestamp.monotonic = l;
280 static int user_mkdir_runtime_path(User *u) {
286 r = mkdir_safe_label("/run/user", 0755, 0, 0);
288 return log_error_errno(r, "Failed to create /run/user: %m");
290 if (!u->runtime_path) {
291 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
296 if (path_is_mount_point(p, false) <= 0) {
297 _cleanup_free_ char *t = NULL;
299 (void) mkdir(p, 0700);
302 r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
304 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
310 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
312 if (errno != EPERM) {
313 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
317 /* Lacking permissions, maybe
318 * CAP_SYS_ADMIN-less container? In this case,
319 * just use a normal directory. */
321 r = chmod_and_chown(p, 0700, u->uid, u->gid);
323 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
334 /* Try to clean up, but ignore errors */
339 u->runtime_path = NULL;
343 int user_start(User *u) {
351 log_debug("New user %s logged in.", u->name);
353 /* Make XDG_RUNTIME_DIR */
354 r = user_mkdir_runtime_path(u);
358 if (!dual_timestamp_is_set(&u->timestamp))
359 dual_timestamp_get(&u->timestamp);
363 /* Save new user data */
366 user_send_signal(u, true);
371 static int user_remove_runtime_path(User *u) {
376 if (!u->runtime_path)
379 r = rm_rf(u->runtime_path, 0);
381 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
383 /* Ignore cases where the directory isn't mounted, as that's
384 * quite possible, if we lacked the permissions to mount
386 r = umount2(u->runtime_path, MNT_DETACH);
387 if (r < 0 && errno != EINVAL && errno != ENOENT)
388 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
390 r = rm_rf(u->runtime_path, REMOVE_ROOT);
392 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
394 free(u->runtime_path);
395 u->runtime_path = NULL;
400 int user_stop(User *u, bool force) {
405 /* Stop jobs have already been queued */
411 LIST_FOREACH(sessions_by_user, s, u->sessions) {
412 k = session_stop(s, force);
424 int user_finalize(User *u) {
431 log_debug("User %s logged out.", u->name);
433 LIST_FOREACH(sessions_by_user, s, u->sessions) {
434 k = session_finalize(s);
439 /* Kill XDG_RUNTIME_DIR */
440 k = user_remove_runtime_path(u);
444 /* Clean SysV + POSIX IPC objects */
445 if (u->manager->remove_ipc) {
446 k = clean_ipc(u->uid);
451 unlink(u->state_file);
452 user_add_to_gc_queue(u);
455 user_send_signal(u, false);
462 int user_get_idle_hint(User *u, dual_timestamp *t) {
464 bool idle_hint = true;
465 dual_timestamp ts = { 0, 0 };
469 LIST_FOREACH(sessions_by_user, s, u->sessions) {
473 ih = session_get_idle_hint(s, &k);
479 if (k.monotonic < ts.monotonic)
485 } else if (idle_hint) {
487 if (k.monotonic > ts.monotonic)
498 int user_check_linger_file(User *u) {
499 _cleanup_free_ char *cc = NULL;
502 cc = cescape(u->name);
506 p = strjoina("/var/lib/systemd/linger/", cc);
508 return access(p, F_OK) >= 0;
511 bool user_check_gc(User *u, bool drop_not_started) {
514 if (drop_not_started && !u->started)
520 if (user_check_linger_file(u) > 0)
526 void user_add_to_gc_queue(User *u) {
532 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
533 u->in_gc_queue = true;
536 UserState user_get_state(User *u) {
545 bool all_closing = true;
547 LIST_FOREACH(sessions_by_user, i, u->sessions) {
550 state = session_get_state(i);
551 if (state == SESSION_ACTIVE)
553 if (state != SESSION_CLOSING)
557 return all_closing ? USER_CLOSING : USER_ONLINE;
560 if (user_check_linger_file(u) > 0)
561 return USER_LINGERING;
566 int user_kill(User *u, int signo) {
572 LIST_FOREACH(sessions_by_user, s, u->sessions) {
573 int r = session_kill(s, KILL_ALL, signo);
574 if (res == 0 && r < 0)
581 void user_elect_display(User *u) {
582 Session *graphical = NULL, *text = NULL, *other = NULL, *s;
586 /* This elects a primary session for each user, which we call
587 * the "display". We try to keep the assignment stable, but we
588 * "upgrade" to better choices. */
590 LIST_FOREACH(sessions_by_user, s, u->sessions) {
592 if (s->class != SESSION_USER)
598 if (SESSION_TYPE_IS_GRAPHICAL(s->type))
600 else if (s->type == SESSION_TTY)
608 u->display->class != SESSION_USER ||
609 u->display->stopping ||
610 !SESSION_TYPE_IS_GRAPHICAL(u->display->type))) {
611 u->display = graphical;
617 u->display->class != SESSION_USER ||
618 u->display->stopping ||
619 u->display->type != SESSION_TTY)) {
626 u->display->class != SESSION_USER ||
627 u->display->stopping))
631 static const char* const user_state_table[_USER_STATE_MAX] = {
632 [USER_OFFLINE] = "offline",
633 [USER_OPENING] = "opening",
634 [USER_LINGERING] = "lingering",
635 [USER_ONLINE] = "online",
636 [USER_ACTIVE] = "active",
637 [USER_CLOSING] = "closing"
640 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
642 int config_parse_tmpfs_size(
644 const char *filename,
647 unsigned section_line,
663 e = endswith(rvalue, "%");
669 ul = strtoul(rvalue, &f, 10);
670 if (errno != 0 || f != e) {
671 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
675 if (ul <= 0 || ul >= 100) {
676 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
680 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
684 r = parse_size(rvalue, 1024, &o);
685 if (r < 0 || (off_t) (size_t) o != o) {
686 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
690 *sz = PAGE_ALIGN((size_t) o);