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"
33 // #include "special.h"
34 #include "unit-name.h"
36 #include "bus-error.h"
37 #include "conf-parser.h"
38 #include "clean-ipc.h"
39 #include "smack-util.h"
40 #include "formats-util.h"
42 #include "logind-user.h"
44 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
54 u->name = strdup(name);
58 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
61 if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
78 void user_free(User *u) {
82 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
85 session_free(u->sessions);
88 hashmap_remove(u->manager->user_units, u->slice);
93 hashmap_remove(u->manager->user_units, u->service);
97 /// elogind does not support slice and service jobs
100 free(u->service_job);
103 free(u->runtime_path);
105 hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
112 static int user_save_internal(User *u) {
113 _cleanup_free_ char *temp_path = NULL;
114 _cleanup_fclose_ FILE *f = NULL;
118 assert(u->state_file);
120 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
124 r = fopen_temporary(u->state_file, &f, &temp_path);
128 fchmod(fileno(f), 0644);
131 "# This is private data. Do not parse.\n"
135 user_state_to_string(user_get_state(u)));
138 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
141 fprintf(f, "SERVICE=%s\n", u->service);
142 /// elogind does not support service jobs
145 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
149 fprintf(f, "SLICE=%s\n", u->slice);
150 /// elogind does not support slice jobs
153 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
157 fprintf(f, "DISPLAY=%s\n", u->display->id);
159 if (dual_timestamp_is_set(&u->timestamp))
161 "REALTIME="USEC_FMT"\n"
162 "MONOTONIC="USEC_FMT"\n",
163 u->timestamp.realtime,
164 u->timestamp.monotonic);
170 fputs("SESSIONS=", f);
172 LIST_FOREACH(sessions_by_user, i, u->sessions) {
181 fputs("\nSEATS=", f);
183 LIST_FOREACH(sessions_by_user, i, u->sessions) {
192 fputs(i->seat->id, f);
195 fputs("\nACTIVE_SESSIONS=", f);
197 LIST_FOREACH(sessions_by_user, i, u->sessions) {
198 if (!session_is_active(i))
209 fputs("\nONLINE_SESSIONS=", f);
211 LIST_FOREACH(sessions_by_user, i, u->sessions) {
212 if (session_get_state(i) == SESSION_CLOSING)
223 fputs("\nACTIVE_SEATS=", f);
225 LIST_FOREACH(sessions_by_user, i, u->sessions) {
226 if (!session_is_active(i) || !i->seat)
234 fputs(i->seat->id, f);
237 fputs("\nONLINE_SEATS=", f);
239 LIST_FOREACH(sessions_by_user, i, u->sessions) {
240 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
248 fputs(i->seat->id, f);
253 r = fflush_and_check(f);
257 if (rename(temp_path, u->state_file) < 0) {
265 (void) unlink(u->state_file);
268 (void) unlink(temp_path);
270 return log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
273 int user_save(User *u) {
279 return user_save_internal (u);
282 int user_load(User *u) {
283 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
289 r = parse_env_file(u->state_file, NEWLINE,
290 "RUNTIME", &u->runtime_path,
291 "SERVICE", &u->service,
292 /// elogind does not support service jobs
294 "SERVICE_JOB", &u->service_job,
297 /// elogind does not support slice jobs
299 "SLICE_JOB", &u->slice_job,
302 "REALTIME", &realtime,
303 "MONOTONIC", &monotonic,
309 log_error_errno(r, "Failed to read %s: %m", u->state_file);
314 s = hashmap_get(u->manager->sessions, display);
316 if (s && s->display && display_is_local(s->display))
320 unsigned long long l;
321 if (sscanf(realtime, "%llu", &l) > 0)
322 u->timestamp.realtime = l;
326 unsigned long long l;
327 if (sscanf(monotonic, "%llu", &l) > 0)
328 u->timestamp.monotonic = l;
334 static int user_mkdir_runtime_path(User *u) {
340 r = mkdir_safe_label("/run/user", 0755, 0, 0);
342 return log_error_errno(r, "Failed to create /run/user: %m");
344 if (!u->runtime_path) {
345 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
350 if (path_is_mount_point(p, 0) <= 0) {
351 _cleanup_free_ char *t = NULL;
353 (void) mkdir_label(p, 0700);
356 r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
358 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
364 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
366 if (errno != EPERM) {
367 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
371 /* Lacking permissions, maybe
372 * CAP_SYS_ADMIN-less container? In this case,
373 * just use a normal directory. */
375 r = chmod_and_chown(p, 0700, u->uid, u->gid);
377 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
382 r = label_fix(p, false, false);
384 log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
392 /* Try to clean up, but ignore errors */
397 u->runtime_path = NULL;
401 static int user_start_slice(User *u) {
408 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
409 char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
410 sprintf(lu, UID_FMT, u->uid);
412 r = slice_build_subslice("user.slice", lu, &slice);
416 /// elogind : Do not try to use dbus to ask systemd
418 r = manager_start_unit(u->manager, slice, &error, &job);
420 log_error("Failed to start user slice: %s", bus_error_message(&error, r));
426 /// elogind does not support slice jobs
435 hashmap_put(u->manager->user_units, u->slice, u);
440 static int user_start_service(User *u) {
441 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
448 char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
449 sprintf(lu, UID_FMT, u->uid);
451 r = unit_name_build("user", lu, ".service", &service);
453 return log_error_errno(r, "Failed to build service name: %m");
455 /// elogind : Do not try to use dbus to ask systemd
457 r = manager_start_unit(u->manager, service, &error, &job);
460 log_error("Failed to start user service: %s", bus_error_message(&error, r));
463 u->service = service;
465 /// elogind does not support service jobs
467 free(u->service_job);
468 u->service_job = job;
474 hashmap_put(u->manager->user_units, u->service, u);
479 int user_start(User *u) {
487 log_debug("New user %s logged in.", u->name);
489 /* Make XDG_RUNTIME_DIR */
490 r = user_mkdir_runtime_path(u);
495 r = user_start_slice(u);
499 /* Save the user data so far, because pam_systemd will read the
500 * XDG_RUNTIME_DIR out of it while starting up systemd --user.
501 * We need to do user_save_internal() because we have not
502 * "officially" started yet. */
503 user_save_internal(u);
505 /* Spawn user systemd */
506 r = user_start_service(u);
510 if (!dual_timestamp_is_set(&u->timestamp))
511 dual_timestamp_get(&u->timestamp);
515 /* Save new user data */
518 user_send_signal(u, true);
523 /// UNNEEDED by elogind
525 static int user_stop_slice(User *u) {
526 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
535 r = manager_stop_unit(u->manager, u->slice, &error, &job);
537 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
547 static int user_stop_service(User *u) {
548 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
557 r = manager_stop_unit(u->manager, u->service, &error, &job);
559 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
563 free(u->service_job);
564 u->service_job = job;
570 static int user_remove_runtime_path(User *u) {
575 if (!u->runtime_path)
578 r = rm_rf(u->runtime_path, 0);
580 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
582 /* Ignore cases where the directory isn't mounted, as that's
583 * quite possible, if we lacked the permissions to mount
585 r = umount2(u->runtime_path, MNT_DETACH);
586 if (r < 0 && errno != EINVAL && errno != ENOENT)
587 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
589 r = rm_rf(u->runtime_path, REMOVE_ROOT);
591 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
593 u->runtime_path = mfree(u->runtime_path);
598 int user_stop(User *u, bool force) {
603 /* Stop jobs have already been queued */
609 LIST_FOREACH(sessions_by_user, s, u->sessions) {
610 k = session_stop(s, force);
616 /// elogind does not support service or slice jobs
618 k = user_stop_service(u);
623 k = user_stop_slice(u);
635 int user_finalize(User *u) {
642 log_debug("User %s logged out.", u->name);
644 LIST_FOREACH(sessions_by_user, s, u->sessions) {
645 k = session_finalize(s);
650 /* Kill XDG_RUNTIME_DIR */
651 k = user_remove_runtime_path(u);
655 /* Clean SysV + POSIX IPC objects */
656 if (u->manager->remove_ipc) {
657 k = clean_ipc(u->uid);
662 unlink(u->state_file);
663 user_add_to_gc_queue(u);
666 user_send_signal(u, false);
673 int user_get_idle_hint(User *u, dual_timestamp *t) {
675 bool idle_hint = true;
676 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
680 LIST_FOREACH(sessions_by_user, s, u->sessions) {
684 ih = session_get_idle_hint(s, &k);
690 if (k.monotonic < ts.monotonic)
696 } else if (idle_hint) {
698 if (k.monotonic > ts.monotonic)
709 int user_check_linger_file(User *u) {
710 _cleanup_free_ char *cc = NULL;
713 cc = cescape(u->name);
717 p = strjoina("/var/lib/systemd/linger/", cc);
719 return access(p, F_OK) >= 0;
722 bool user_check_gc(User *u, bool drop_not_started) {
725 if (drop_not_started && !u->started)
731 if (user_check_linger_file(u) > 0)
734 /// elogind does not support systemd services and slices
736 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
739 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
746 void user_add_to_gc_queue(User *u) {
752 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
753 u->in_gc_queue = true;
756 UserState user_get_state(User *u) {
764 /// elogind does not support slice and service jobs
766 if (!u->started || u->slice_job || u->service_job)
773 bool all_closing = true;
775 LIST_FOREACH(sessions_by_user, i, u->sessions) {
778 state = session_get_state(i);
779 if (state == SESSION_ACTIVE)
781 if (state != SESSION_CLOSING)
785 return all_closing ? USER_CLOSING : USER_ONLINE;
788 if (user_check_linger_file(u) > 0)
789 return USER_LINGERING;
794 int user_kill(User *u, int signo) {
795 /// Without systemd unit support, elogind has to rely on its session system
802 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
809 LIST_FOREACH(sessions_by_user, s, u->sessions) {
810 int r = session_kill(s, KILL_ALL, signo);
811 if (res == 0 && r < 0)
819 static bool elect_display_filter(Session *s) {
820 /* Return true if the session is a candidate for the user’s ‘primary
821 * session’ or ‘display’. */
824 return (s->class == SESSION_USER && !s->stopping);
827 static int elect_display_compare(Session *s1, Session *s2) {
828 /* Indexed by SessionType. Lower numbers mean more preferred. */
829 const int type_ranks[_SESSION_TYPE_MAX] = {
830 [SESSION_UNSPECIFIED] = 0,
833 [SESSION_WAYLAND] = -3,
838 /* Calculate the partial order relationship between s1 and s2,
839 * returning < 0 if s1 is preferred as the user’s ‘primary session’,
840 * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
843 * s1 or s2 may be NULL. */
847 if ((s1 == NULL) != (s2 == NULL))
848 return (s1 == NULL) - (s2 == NULL);
850 if (s1->stopping != s2->stopping)
851 return s1->stopping - s2->stopping;
853 if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
854 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
856 if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
857 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
859 if (s1->type != s2->type)
860 return type_ranks[s1->type] - type_ranks[s2->type];
865 void user_elect_display(User *u) {
870 /* This elects a primary session for each user, which we call
871 * the "display". We try to keep the assignment stable, but we
872 * "upgrade" to better choices. */
873 log_debug("Electing new display for user %s", u->name);
875 LIST_FOREACH(sessions_by_user, s, u->sessions) {
876 if (!elect_display_filter(s)) {
877 log_debug("Ignoring session %s", s->id);
881 if (elect_display_compare(s, u->display) < 0) {
882 log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
888 static const char* const user_state_table[_USER_STATE_MAX] = {
889 [USER_OFFLINE] = "offline",
890 [USER_OPENING] = "opening",
891 [USER_LINGERING] = "lingering",
892 [USER_ONLINE] = "online",
893 [USER_ACTIVE] = "active",
894 [USER_CLOSING] = "closing"
897 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
899 int config_parse_tmpfs_size(
901 const char *filename,
904 unsigned section_line,
920 e = endswith(rvalue, "%");
926 ul = strtoul(rvalue, &f, 10);
927 if (errno != 0 || f != e) {
928 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
932 if (ul <= 0 || ul >= 100) {
933 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
937 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
941 r = parse_size(rvalue, 1024, &o);
942 if (r < 0 || (off_t) (size_t) o != o) {
943 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
947 *sz = PAGE_ALIGN((size_t) o);