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 "smack-util.h"
40 #include "logind-user.h"
42 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
52 u->name = strdup(name);
56 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
59 if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
76 void user_free(User *u) {
80 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
83 session_free(u->sessions);
85 free(u->runtime_path);
87 hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
94 static int user_save_internal(User *u) {
95 _cleanup_free_ char *temp_path = NULL;
96 _cleanup_fclose_ FILE *f = NULL;
100 assert(u->state_file);
102 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
106 r = fopen_temporary(u->state_file, &f, &temp_path);
110 fchmod(fileno(f), 0644);
113 "# This is private data. Do not parse.\n"
117 user_state_to_string(user_get_state(u)));
120 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
123 fprintf(f, "DISPLAY=%s\n", u->display->id);
125 if (dual_timestamp_is_set(&u->timestamp))
127 "REALTIME="USEC_FMT"\n"
128 "MONOTONIC="USEC_FMT"\n",
129 u->timestamp.realtime,
130 u->timestamp.monotonic);
136 fputs("SESSIONS=", f);
138 LIST_FOREACH(sessions_by_user, i, u->sessions) {
147 fputs("\nSEATS=", f);
149 LIST_FOREACH(sessions_by_user, i, u->sessions) {
158 fputs(i->seat->id, f);
161 fputs("\nACTIVE_SESSIONS=", f);
163 LIST_FOREACH(sessions_by_user, i, u->sessions) {
164 if (!session_is_active(i))
175 fputs("\nONLINE_SESSIONS=", f);
177 LIST_FOREACH(sessions_by_user, i, u->sessions) {
178 if (session_get_state(i) == SESSION_CLOSING)
189 fputs("\nACTIVE_SEATS=", f);
191 LIST_FOREACH(sessions_by_user, i, u->sessions) {
192 if (!session_is_active(i) || !i->seat)
200 fputs(i->seat->id, f);
203 fputs("\nONLINE_SEATS=", f);
205 LIST_FOREACH(sessions_by_user, i, u->sessions) {
206 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
214 fputs(i->seat->id, f);
221 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
223 unlink(u->state_file);
229 log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
234 int user_save(User *u) {
240 return user_save_internal (u);
243 int user_load(User *u) {
244 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
250 r = parse_env_file(u->state_file, NEWLINE,
251 "RUNTIME", &u->runtime_path,
253 "REALTIME", &realtime,
254 "MONOTONIC", &monotonic,
260 log_error_errno(r, "Failed to read %s: %m", u->state_file);
265 s = hashmap_get(u->manager->sessions, display);
267 if (s && s->display && display_is_local(s->display))
271 unsigned long long l;
272 if (sscanf(realtime, "%llu", &l) > 0)
273 u->timestamp.realtime = l;
277 unsigned long long l;
278 if (sscanf(monotonic, "%llu", &l) > 0)
279 u->timestamp.monotonic = l;
285 static int user_mkdir_runtime_path(User *u) {
291 r = mkdir_safe_label("/run/user", 0755, 0, 0);
293 return log_error_errno(r, "Failed to create /run/user: %m");
295 if (!u->runtime_path) {
296 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
301 if (path_is_mount_point(p, 0) <= 0) {
302 _cleanup_free_ char *t = NULL;
304 (void) mkdir_label(p, 0700);
307 r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
309 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
315 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
317 if (errno != EPERM) {
318 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
322 /* Lacking permissions, maybe
323 * CAP_SYS_ADMIN-less container? In this case,
324 * just use a normal directory. */
326 r = chmod_and_chown(p, 0700, u->uid, u->gid);
328 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
333 r = label_fix(p, false, false);
335 log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
343 /* Try to clean up, but ignore errors */
348 u->runtime_path = NULL;
352 int user_start(User *u) {
360 log_debug("New user %s logged in.", u->name);
362 /* Make XDG_RUNTIME_DIR */
363 r = user_mkdir_runtime_path(u);
367 /* Save the user data so far, because pam_systemd will read the
368 * XDG_RUNTIME_DIR out of it while starting up systemd --user.
369 * We need to do user_save_internal() because we have not
370 * "officially" started yet. */
371 user_save_internal(u);
373 if (!dual_timestamp_is_set(&u->timestamp))
374 dual_timestamp_get(&u->timestamp);
378 /* Save new user data */
381 user_send_signal(u, true);
386 static int user_remove_runtime_path(User *u) {
391 if (!u->runtime_path)
394 r = rm_rf(u->runtime_path, false, false, false);
396 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
398 /* Ignore cases where the directory isn't mounted, as that's
399 * quite possible, if we lacked the permissions to mount
401 r = umount2(u->runtime_path, MNT_DETACH);
402 if (r < 0 && errno != EINVAL && errno != ENOENT)
403 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
405 r = rm_rf(u->runtime_path, false, true, false);
407 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
409 free(u->runtime_path);
410 u->runtime_path = NULL;
415 int user_stop(User *u, bool force) {
420 /* Stop jobs have already been queued */
426 LIST_FOREACH(sessions_by_user, s, u->sessions) {
427 k = session_stop(s, force);
439 int user_finalize(User *u) {
446 log_debug("User %s logged out.", u->name);
448 LIST_FOREACH(sessions_by_user, s, u->sessions) {
449 k = session_finalize(s);
454 /* Kill XDG_RUNTIME_DIR */
455 k = user_remove_runtime_path(u);
459 /* Clean SysV + POSIX IPC objects */
460 if (u->manager->remove_ipc) {
461 k = clean_ipc(u->uid);
466 unlink(u->state_file);
467 user_add_to_gc_queue(u);
470 user_send_signal(u, false);
477 int user_get_idle_hint(User *u, dual_timestamp *t) {
479 bool idle_hint = true;
480 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
484 LIST_FOREACH(sessions_by_user, s, u->sessions) {
488 ih = session_get_idle_hint(s, &k);
494 if (k.monotonic < ts.monotonic)
500 } else if (idle_hint) {
502 if (k.monotonic > ts.monotonic)
513 int user_check_linger_file(User *u) {
514 _cleanup_free_ char *cc = NULL;
517 cc = cescape(u->name);
521 p = strjoina("/var/lib/systemd/linger/", cc);
523 return access(p, F_OK) >= 0;
526 bool user_check_gc(User *u, bool drop_not_started) {
529 if (drop_not_started && !u->started)
535 if (user_check_linger_file(u) > 0)
541 void user_add_to_gc_queue(User *u) {
547 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
548 u->in_gc_queue = true;
551 UserState user_get_state(User *u) {
560 bool all_closing = true;
562 LIST_FOREACH(sessions_by_user, i, u->sessions) {
565 state = session_get_state(i);
566 if (state == SESSION_ACTIVE)
568 if (state != SESSION_CLOSING)
572 return all_closing ? USER_CLOSING : USER_ONLINE;
575 if (user_check_linger_file(u) > 0)
576 return USER_LINGERING;
581 int user_kill(User *u, int signo) {
587 LIST_FOREACH(sessions_by_user, s, u->sessions) {
588 int r = session_kill(s, KILL_ALL, signo);
589 if (res == 0 && r < 0)
596 static bool elect_display_filter(Session *s) {
597 /* Return true if the session is a candidate for the user’s ‘primary
598 * session’ or ‘display’. */
601 return (s->class == SESSION_USER && !s->stopping);
604 static int elect_display_compare(Session *s1, Session *s2) {
605 /* Indexed by SessionType. Lower numbers mean more preferred. */
606 const int type_ranks[_SESSION_TYPE_MAX] = {
607 [SESSION_UNSPECIFIED] = 0,
610 [SESSION_WAYLAND] = -3,
615 /* Calculate the partial order relationship between s1 and s2,
616 * returning < 0 if s1 is preferred as the user’s ‘primary session’,
617 * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
620 * s1 or s2 may be NULL. */
624 if ((s1 == NULL) != (s2 == NULL))
625 return (s1 == NULL) - (s2 == NULL);
627 if (s1->stopping != s2->stopping)
628 return s1->stopping - s2->stopping;
630 if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
631 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
633 if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
634 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
636 if (s1->type != s2->type)
637 return type_ranks[s1->type] - type_ranks[s2->type];
642 void user_elect_display(User *u) {
647 /* This elects a primary session for each user, which we call
648 * the "display". We try to keep the assignment stable, but we
649 * "upgrade" to better choices. */
650 log_debug("Electing new display for user %s", u->name);
652 LIST_FOREACH(sessions_by_user, s, u->sessions) {
653 if (!elect_display_filter(s)) {
654 log_debug("Ignoring session %s", s->id);
658 if (elect_display_compare(s, u->display) < 0) {
659 log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
665 static const char* const user_state_table[_USER_STATE_MAX] = {
666 [USER_OFFLINE] = "offline",
667 [USER_OPENING] = "opening",
668 [USER_LINGERING] = "lingering",
669 [USER_ONLINE] = "online",
670 [USER_ACTIVE] = "active",
671 [USER_CLOSING] = "closing"
674 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
676 int config_parse_tmpfs_size(
678 const char *filename,
681 unsigned section_line,
697 e = endswith(rvalue, "%");
703 ul = strtoul(rvalue, &f, 10);
704 if (errno != 0 || f != e) {
705 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
709 if (ul <= 0 || ul >= 100) {
710 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
714 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
718 r = parse_size(rvalue, 1024, &o);
719 if (r < 0 || (off_t) (size_t) o != o) {
720 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
724 *sz = PAGE_ALIGN((size_t) o);