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 free(u->runtime_path);
594 u->runtime_path = NULL;
599 int user_stop(User *u, bool force) {
604 /* Stop jobs have already been queued */
610 LIST_FOREACH(sessions_by_user, s, u->sessions) {
611 k = session_stop(s, force);
617 /// elogind does not support service or slice jobs
619 k = user_stop_service(u);
624 k = user_stop_slice(u);
636 int user_finalize(User *u) {
643 log_debug("User %s logged out.", u->name);
645 LIST_FOREACH(sessions_by_user, s, u->sessions) {
646 k = session_finalize(s);
651 /* Kill XDG_RUNTIME_DIR */
652 k = user_remove_runtime_path(u);
656 /* Clean SysV + POSIX IPC objects */
657 if (u->manager->remove_ipc) {
658 k = clean_ipc(u->uid);
663 unlink(u->state_file);
664 user_add_to_gc_queue(u);
667 user_send_signal(u, false);
674 int user_get_idle_hint(User *u, dual_timestamp *t) {
676 bool idle_hint = true;
677 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
681 LIST_FOREACH(sessions_by_user, s, u->sessions) {
685 ih = session_get_idle_hint(s, &k);
691 if (k.monotonic < ts.monotonic)
697 } else if (idle_hint) {
699 if (k.monotonic > ts.monotonic)
710 int user_check_linger_file(User *u) {
711 _cleanup_free_ char *cc = NULL;
714 cc = cescape(u->name);
718 p = strjoina("/var/lib/systemd/linger/", cc);
720 return access(p, F_OK) >= 0;
723 bool user_check_gc(User *u, bool drop_not_started) {
726 if (drop_not_started && !u->started)
732 if (user_check_linger_file(u) > 0)
735 /// elogind does not support systemd services and slices
737 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
740 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
747 void user_add_to_gc_queue(User *u) {
753 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
754 u->in_gc_queue = true;
757 UserState user_get_state(User *u) {
765 /// elogind does not support slice and service jobs
767 if (!u->started || u->slice_job || u->service_job)
774 bool all_closing = true;
776 LIST_FOREACH(sessions_by_user, i, u->sessions) {
779 state = session_get_state(i);
780 if (state == SESSION_ACTIVE)
782 if (state != SESSION_CLOSING)
786 return all_closing ? USER_CLOSING : USER_ONLINE;
789 if (user_check_linger_file(u) > 0)
790 return USER_LINGERING;
795 int user_kill(User *u, int signo) {
796 /// Without systemd unit support, elogind has to rely on its session system
803 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
810 LIST_FOREACH(sessions_by_user, s, u->sessions) {
811 int r = session_kill(s, KILL_ALL, signo);
812 if (res == 0 && r < 0)
820 static bool elect_display_filter(Session *s) {
821 /* Return true if the session is a candidate for the user’s ‘primary
822 * session’ or ‘display’. */
825 return (s->class == SESSION_USER && !s->stopping);
828 static int elect_display_compare(Session *s1, Session *s2) {
829 /* Indexed by SessionType. Lower numbers mean more preferred. */
830 const int type_ranks[_SESSION_TYPE_MAX] = {
831 [SESSION_UNSPECIFIED] = 0,
834 [SESSION_WAYLAND] = -3,
839 /* Calculate the partial order relationship between s1 and s2,
840 * returning < 0 if s1 is preferred as the user’s ‘primary session’,
841 * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
844 * s1 or s2 may be NULL. */
848 if ((s1 == NULL) != (s2 == NULL))
849 return (s1 == NULL) - (s2 == NULL);
851 if (s1->stopping != s2->stopping)
852 return s1->stopping - s2->stopping;
854 if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
855 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
857 if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
858 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
860 if (s1->type != s2->type)
861 return type_ranks[s1->type] - type_ranks[s2->type];
866 void user_elect_display(User *u) {
871 /* This elects a primary session for each user, which we call
872 * the "display". We try to keep the assignment stable, but we
873 * "upgrade" to better choices. */
874 log_debug("Electing new display for user %s", u->name);
876 LIST_FOREACH(sessions_by_user, s, u->sessions) {
877 if (!elect_display_filter(s)) {
878 log_debug("Ignoring session %s", s->id);
882 if (elect_display_compare(s, u->display) < 0) {
883 log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
889 static const char* const user_state_table[_USER_STATE_MAX] = {
890 [USER_OFFLINE] = "offline",
891 [USER_OPENING] = "opening",
892 [USER_LINGERING] = "lingering",
893 [USER_ONLINE] = "online",
894 [USER_ACTIVE] = "active",
895 [USER_CLOSING] = "closing"
898 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
900 int config_parse_tmpfs_size(
902 const char *filename,
905 unsigned section_line,
921 e = endswith(rvalue, "%");
927 ul = strtoul(rvalue, &f, 10);
928 if (errno != 0 || f != e) {
929 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
933 if (ul <= 0 || ul >= 100) {
934 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
938 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
942 r = parse_size(rvalue, 1024, &o);
943 if (r < 0 || (off_t) (size_t) o != o) {
944 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
948 *sz = PAGE_ALIGN((size_t) o);