diff options
Diffstat (limited to 'src/login')
-rw-r--r-- | src/login/70-uaccess.rules.m4 (renamed from src/login/70-uaccess.rules) | 4 | ||||
-rw-r--r-- | src/login/elogind-dbus.c | 2 | ||||
-rw-r--r-- | src/login/elogind.c | 46 | ||||
-rw-r--r-- | src/login/elogind.h | 3 | ||||
-rw-r--r-- | src/login/logind-core.c | 216 | ||||
-rw-r--r-- | src/login/logind-dbus.c | 113 | ||||
-rw-r--r-- | src/login/logind-device.c | 4 | ||||
-rw-r--r-- | src/login/logind-gperf.gperf | 1 | ||||
-rw-r--r-- | src/login/logind-seat.c | 58 | ||||
-rw-r--r-- | src/login/logind-seat.h | 6 | ||||
-rw-r--r-- | src/login/logind-session-dbus.c | 23 | ||||
-rw-r--r-- | src/login/logind-session.c | 252 | ||||
-rw-r--r-- | src/login/logind-session.h | 25 | ||||
-rw-r--r-- | src/login/logind-user-dbus.c | 2 | ||||
-rw-r--r-- | src/login/logind-user.c | 395 | ||||
-rw-r--r-- | src/login/logind-user.h | 22 | ||||
-rw-r--r-- | src/login/logind-utmp.c | 6 | ||||
-rw-r--r-- | src/login/logind.c | 79 | ||||
-rw-r--r-- | src/login/logind.h | 20 | ||||
-rw-r--r-- | src/login/meson.build | 13 | ||||
-rw-r--r-- | src/login/org.freedesktop.login1.policy | 2 | ||||
-rw-r--r-- | src/login/pam_elogind.c | 124 | ||||
-rw-r--r-- | src/login/user-runtime-dir.c | 111 |
23 files changed, 1025 insertions, 502 deletions
diff --git a/src/login/70-uaccess.rules b/src/login/70-uaccess.rules.m4 index 3515d292a..d55e5bf5c 100644 --- a/src/login/70-uaccess.rules +++ b/src/login/70-uaccess.rules.m4 @@ -46,6 +46,10 @@ SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess" # DRI video devices SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess" +m4_ifdef(`DEV_KVM_UACCESS',`` +# KVM +SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess"'' +)m4_dnl # smart-card readers ENV{ID_SMARTCARD_READER}=="?*", TAG+="uaccess" diff --git a/src/login/elogind-dbus.c b/src/login/elogind-dbus.c index f359e1867..03ec05029 100644 --- a/src/login/elogind-dbus.c +++ b/src/login/elogind-dbus.c @@ -83,7 +83,7 @@ static int run_helper(const char *helper, const char *arg_verb) { arguments[0] = NULL; arguments[1] = (char*)arg_verb; arguments[2] = NULL; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL); r = safe_fork_full(helper, NULL, 0, FORK_RESET_SIGNALS|FORK_REOPEN_LOG, NULL); diff --git a/src/login/elogind.c b/src/login/elogind.c index 9fe52a94c..77622483a 100644 --- a/src/login/elogind.c +++ b/src/login/elogind.c @@ -156,7 +156,7 @@ static int elogind_daemonize(void) { /// Simple tool to see, if elogind is already running static pid_t elogind_is_already_running(bool need_pid_file) { - _cleanup_free_ char *s = NULL; + _cleanup_free_ char *s = NULL, *comm = NULL; pid_t pid; int r; @@ -170,8 +170,17 @@ static pid_t elogind_is_already_running(bool need_pid_file) { if (r < 0) goto we_are_alone; - if ( (pid != getpid_cached()) && pid_is_alive(pid)) - return pid; + if ( (pid != getpid_cached()) && pid_is_alive(pid)) { + /* If the old elogind process currently running was forked into + * background, its name will be "elogind-daemon", while this + * process will be "elogind". + * Therefore check comm with startswith(). + */ + get_process_comm(pid, &comm); + if (NULL == startswith(strna(comm), program_invocation_short_name)) + goto we_are_alone; + } + return pid; we_are_alone: @@ -454,27 +463,28 @@ void elogind_manager_reset_config(Manager* m) { #endif // ENABLE_DEBUG_ELOGIND } - /// Add-On for manager_startup() -int elogind_manager_startup(Manager *m) { - int r; - - assert(m); +int elogind_manager_startup(Manager* m) { + int r, e = 0; - assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, -1) >= 0); + /* Install our signal handler */ r = sd_event_add_signal(m->event, NULL, SIGINT, elogind_signal_handler, m); - if (r < 0) - return log_error_errno(r, "Failed to register SIGINT handler: %m"); + if (r < 0) { + if (e == 0) e = r; + log_error_errno(r, "Failed to register SIGINT handler: %m"); + } - assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGQUIT, -1) >= 0); r = sd_event_add_signal(m->event, NULL, SIGQUIT, elogind_signal_handler, m); - if (r < 0) - return log_error_errno(r, "Failed to register SIGQUIT handler: %m"); + if (r < 0) { + if (e == 0) e = r; + log_error_errno(r, "Failed to register SIGQUIT handler: %m"); + } - assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGTERM, -1) >= 0); r = sd_event_add_signal(m->event, NULL, SIGTERM, elogind_signal_handler, m); - if (r < 0) - return log_error_errno(r, "Failed to register SIGTERM handler: %m"); + if (r < 0) { + if (e == 0) e = r; + log_error_errno(r, "Failed to register SIGTERM handler: %m"); + } - return 0; + return e; } diff --git a/src/login/elogind.h b/src/login/elogind.h index 60b9f80dd..37bcb1e40 100644 --- a/src/login/elogind.h +++ b/src/login/elogind.h @@ -42,7 +42,6 @@ int elogind_manager_new(Manager* m); void elogind_manager_reset_config(Manager* m); /// Add-On for manager_startup() -int elogind_manager_startup(Manager *m); - +int elogind_manager_startup(Manager* m); #endif // ELOGIND_SRC_LOGIN_ELOGIN_H_INCLUDED diff --git a/src/login/logind-core.c b/src/login/logind-core.c index db0df19e2..e7517794c 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -5,6 +5,9 @@ #include <sys/ioctl.h> #include <sys/types.h> #include <linux/vt.h> +#if ENABLE_UTMP +#include <utmpx.h> +#endif #include "alloc-util.h" #include "bus-error.h" @@ -14,6 +17,7 @@ #include "fd-util.h" #include "logind.h" #include "parse-util.h" +#include "path-util.h" #include "process-util.h" #include "strv.h" #include "terminal-util.h" @@ -29,6 +33,8 @@ void manager_reset_config(Manager *m) { #endif // 0 m->remove_ipc = true; m->inhibit_delay_max = 5 * USEC_PER_SEC; + m->user_stop_delay = 10 * USEC_PER_SEC; + m->handle_power_key = HANDLE_POWEROFF; m->handle_suspend_key = HANDLE_SUSPEND; m->handle_hibernate_key = HANDLE_HIBERNATE; @@ -102,15 +108,16 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev int manager_add_seat(Manager *m, const char *id, Seat **_seat) { Seat *s; + int r; assert(m); assert(id); s = hashmap_get(m->seats, id); if (!s) { - s = seat_new(m, id); - if (!s) - return -ENOMEM; + r = seat_new(&s, m, id); + if (r < 0) + return r; } if (_seat) @@ -121,15 +128,16 @@ int manager_add_seat(Manager *m, const char *id, Seat **_seat) { int manager_add_session(Manager *m, const char *id, Session **_session) { Session *s; + int r; assert(m); assert(id); s = hashmap_get(m->sessions, id); if (!s) { - s = session_new(m, id); - if (!s) - return -ENOMEM; + r = session_new(&s, m, id); + if (r < 0) + return r; } if (_session) @@ -138,7 +146,14 @@ int manager_add_session(Manager *m, const char *id, Session **_session) { return 0; } -int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { +int manager_add_user( + Manager *m, + uid_t uid, + gid_t gid, + const char *name, + const char *home, + User **_user) { + User *u; int r; @@ -147,7 +162,7 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User ** u = hashmap_get(m->users, UID_TO_PTR(uid)); if (!u) { - r = user_new(&u, m, uid, gid, name); + r = user_new(&u, m, uid, gid, name, home); if (r < 0) return r; } @@ -158,7 +173,12 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User ** return 0; } -int manager_add_user_by_name(Manager *m, const char *name, User **_user) { +int manager_add_user_by_name( + Manager *m, + const char *name, + User **_user) { + + const char *home = NULL; uid_t uid; gid_t gid; int r; @@ -166,11 +186,11 @@ int manager_add_user_by_name(Manager *m, const char *name, User **_user) { assert(m); assert(name); - r = get_user_creds(&name, &uid, &gid, NULL, NULL); + r = get_user_creds(&name, &uid, &gid, &home, NULL); if (r < 0) return r; - return manager_add_user(m, uid, gid, name, _user); + return manager_add_user(m, uid, gid, name, home, _user); } int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { @@ -183,7 +203,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { if (!p) return errno > 0 ? -errno : -ENOENT; - return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); + return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user); } int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) { @@ -337,26 +357,29 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **ret) { if (!pid_is_valid(pid)) return -EINVAL; + s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(pid)); + if (!s) { #if 0 /// elogind does not support systemd units, but its own session system - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - goto not_found; + r = cg_pid_get_unit(pid, &unit); + if (r < 0) + goto not_found; - s = hashmap_get(m->session_units, unit); - if (!s) - goto not_found; + s = hashmap_get(m->session_units, unit); + if (!s) + goto not_found; #else - log_debug_elogind("Searching session for PID %u", pid); - r = cg_pid_get_session(pid, &session_name); - if (r < 0) - goto not_found; + log_debug_elogind("Searching session for PID %u", pid); + r = cg_pid_get_session(pid, &session_name); + if (r < 0) + goto not_found; - s = hashmap_get(m->sessions, session_name); - log_debug_elogind("Session Name \"%s\" -> Session \"%s\"", - session_name, s && s->id ? s->id : "NULL"); - if (NULL == s) - goto not_found; + s = hashmap_get(m->sessions, session_name); + log_debug_elogind("Session Name \"%s\" -> Session \"%s\"", + session_name, s && s->id ? s->id : "NULL"); + if (NULL == s) + goto not_found; #endif // 0 + } if (ret) *ret = s; @@ -722,3 +745,142 @@ bool manager_all_buttons_ignored(Manager *m) { return true; } + +int manager_read_utmp(Manager *m) { +#if ENABLE_UTMP + int r; + + assert(m); + + if (utmpxname(_PATH_UTMPX) < 0) + return log_error_errno(errno, "Failed to set utmp path to " _PATH_UTMPX ": %m"); + + setutxent(); + + for (;;) { + _cleanup_free_ char *t = NULL; + struct utmpx *u; + const char *c; + Session *s; + + errno = 0; + u = getutxent(); + if (!u) { + if (errno != 0) + log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m"); + r = 0; + break; + } + + if (u->ut_type != USER_PROCESS) + continue; + + if (!pid_is_valid(u->ut_pid)) + continue; + + t = strndup(u->ut_line, sizeof(u->ut_line)); + if (!t) { + r = log_oom(); + break; + } + + c = path_startswith(t, "/dev/"); + if (c) { + r = free_and_strdup(&t, c); + if (r < 0) { + log_oom(); + break; + } + } + + if (isempty(t)) + continue; + + s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(u->ut_pid)); + if (!s) + continue; + + if (s->tty_validity == TTY_FROM_UTMP && !streq_ptr(s->tty, t)) { + /* This may happen on multiplexed SSH connection (i.e. 'SSH connection sharing'). In + * this case PAM and utmp sessions don't match. In such a case let's invalidate the TTY + * information and never acquire it again. */ + + s->tty = mfree(s->tty); + s->tty_validity = TTY_UTMP_INCONSISTENT; + log_debug("Session '%s' has inconsistent TTY information, dropping TTY information.", s->id); + continue; + } + + /* Never override what we figured out once */ + if (s->tty || s->tty_validity >= 0) + continue; + + s->tty = TAKE_PTR(t); + s->tty_validity = TTY_FROM_UTMP; + log_debug("Acquired TTY information '%s' from utmp for session '%s'.", s->tty, s->id); + } + + endutxent(); + return r; +#else + return 0 +#endif +} + +#if ENABLE_UTMP +static int manager_dispatch_utmp(sd_event_source *s, const struct inotify_event *event, void *userdata) { + Manager *m = userdata; + + assert(m); + + /* If there's indication the file itself might have been removed or became otherwise unavailable, then let's + * reestablish the watch on whatever there's now. */ + if ((event->mask & (IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF|IN_Q_OVERFLOW|IN_UNMOUNT)) != 0) + manager_connect_utmp(m); + + (void) manager_read_utmp(m); + return 0; +} +#endif + +void manager_connect_utmp(Manager *m) { +#if ENABLE_UTMP + sd_event_source *s = NULL; + int r; + + assert(m); + + /* Watch utmp for changes via inotify. We do this to deal with tools such as ssh, which will register the PAM + * session early, and acquire a TTY only much later for the connection. Thus during PAM the TTY won't be known + * yet. ssh will register itself with utmp when it finally acquired the TTY. Hence, let's make use of this, and + * watch utmp for the TTY asynchronously. We use the PAM session's leader PID as key, to find the right entry. + * + * Yes, relying on utmp is pretty ugly, but it's good enough for informational purposes, as well as idle + * detection (which, for tty sessions, relies on the TTY used) */ + + r = sd_event_add_inotify(m->event, &s, _PATH_UTMPX, IN_MODIFY|IN_MOVE_SELF|IN_DELETE_SELF|IN_ATTRIB, manager_dispatch_utmp, m); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG: LOG_WARNING, r, "Failed to create inotify watch on " _PATH_UTMPX ", ignoring: %m"); + else { + r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_IDLE); + if (r < 0) + log_warning_errno(r, "Failed to adjust utmp event source priority, ignoring: %m"); + + (void) sd_event_source_set_description(s, "utmp"); + } + + sd_event_source_unref(m->utmp_event_source); + m->utmp_event_source = s; +#endif +} + +void manager_reconnect_utmp(Manager *m) { +#if ENABLE_UTMP + assert(m); + + if (m->utmp_event_source) + return; + + manager_connect_utmp(m); +#endif +} diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index acf50e62e..44f08c583 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -775,6 +775,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus } while (hashmap_get(m->sessions, id)); } + /* If we are not watching utmp aleady, try again */ + manager_reconnect_utmp(m); + r = manager_add_user_by_uid(m, uid, &user); if (r < 0) goto fail; @@ -784,9 +787,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus goto fail; session_set_user(session, user); + session_set_leader(session, leader); - session->leader = leader; - session->audit_id = audit_id; session->type = t; session->class = c; session->remote = remote; @@ -798,6 +800,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus r = -ENOMEM; goto fail; } + + session->tty_validity = TTY_FROM_PAM; } if (!isempty(display)) { @@ -848,9 +852,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus r = sd_bus_message_enter_container(message, 'a', "(sv)"); if (r < 0) - return r; + goto fail; - r = session_start(session, message); + r = session_start(session, message, error); if (r < 0) goto fail; @@ -1842,8 +1846,9 @@ static int method_do_shutdown_or_sleep( #if 0 /// Within elogind the manager m must be provided, too r = can_sleep(sleep_verb); if (r == -ENOSPC) - return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, - "Not enough swap space for hibernation"); + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not enough swap space for hibernation"); + if (r == -EADV) + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Resume not configured, can't hibernate"); if (r == 0) return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb \"%s\" not supported", sleep_verb); @@ -2285,6 +2290,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd if (cancelled && m->enable_wall_messages) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + _cleanup_free_ char *username = NULL; const char *tty = NULL; uid_t uid = 0; int r; @@ -2295,9 +2301,10 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd (void) sd_bus_creds_get_tty(creds, &tty); } + username = uid_to_name(uid); #if 0 /// elogind wants to allow extra cancellation messages utmp_wall("The system shutdown has been cancelled", - uid_to_name(uid), tty, logind_wall_tty_filter, m); + username, tty, logind_wall_tty_filter, m); #else r = asprintf(&l, "%s%sThe system shutdown has been cancelled!", strempty(m->wall_message), @@ -2345,7 +2352,7 @@ static int method_can_shutdown_or_sleep( #else r = can_sleep(m, sleep_verb); #endif // 0 - if (IN_SET(r, 0, -ENOSPC)) + if (IN_SET(r, 0, -ENOSPC, -EADV)) return sd_bus_reply_method_return(message, "s", "na"); if (r < 0) return r; @@ -2367,7 +2374,6 @@ static int method_can_shutdown_or_sleep( blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); handle = handle_action_from_string(sleep_verb); - if (handle >= 0) { #if 0 /// elogind uses its own variant, which can use the handle directly. const char *target; @@ -2385,7 +2391,6 @@ static int method_can_shutdown_or_sleep( goto finish; } } - } #else log_debug_elogind("CanShutDownOrSleep: %s [%d] %s blocked", sleep_verb, handle, blocked ? "is" : "not"); @@ -2819,6 +2824,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST), @@ -2900,24 +2906,20 @@ const sd_bus_vtable manager_vtable[] = { #if 0 /// UNNEEDED by elogind static int session_jobs_reply(Session *s, const char *unit, const char *result) { - int r = 0; - assert(s); assert(unit); if (!s->started) - return r; + return 0; - if (streq(result, "done")) - r = session_send_create_reply(s, NULL); - else { + if (result && !streq(result, "done")) { _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; - sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); - r = session_send_create_reply(s, &e); + sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit '%s' failed with '%s'", unit, result); + return session_send_create_reply(s, &e); } - return r; + return session_send_create_reply(s, NULL); } int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -2950,30 +2952,29 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err } session = hashmap_get(m->session_units, unit); - if (session && streq_ptr(path, session->scope_job)) { - session->scope_job = mfree(session->scope_job); - session_jobs_reply(session, unit, result); + if (session) { + if (streq_ptr(path, session->scope_job)) { + session->scope_job = mfree(session->scope_job); + (void) session_jobs_reply(session, unit, result); + + session_save(session); + user_save(session->user); + } - session_save(session); - user_save(session->user); session_add_to_gc_queue(session); } user = hashmap_get(m->user_units, unit); - if (user && - (streq_ptr(path, user->service_job) || - streq_ptr(path, user->slice_job))) { - - if (streq_ptr(path, user->service_job)) + if (user) { + if (streq_ptr(path, user->service_job)) { user->service_job = mfree(user->service_job); - if (streq_ptr(path, user->slice_job)) - user->slice_job = mfree(user->slice_job); + LIST_FOREACH(sessions_by_user, session, user->sessions) + (void) session_jobs_reply(session, unit, NULL /* don't propagate user service failures to the client */); - LIST_FOREACH(sessions_by_user, session, user->sessions) - session_jobs_reply(session, unit, result); + user_save(user); + } - user_save(user); user_add_to_gc_queue(user); } @@ -3107,13 +3108,15 @@ int manager_start_scope( pid_t pid, const char *slice, const char *description, - const char *after, - const char *after2, + char **wants, + char **after, + const char *requires_mounts_for, sd_bus_message *more_properties, sd_bus_error *error, char **job) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + char **i; int r; assert(manager); @@ -3151,14 +3154,20 @@ int manager_start_scope( return r; } - if (!isempty(after)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); + STRV_FOREACH(i, wants) { + r = sd_bus_message_append(m, "(sv)", "Wants", "as", 1, *i); if (r < 0) return r; } - if (!isempty(after2)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); + STRV_FOREACH(i, after) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i); + if (r < 0) + return r; + } + + if (!empty_or_root(requires_mounts_for)) { + r = sd_bus_message_append(m, "(sv)", "RequiresMountsFor", "as", 1, requires_mounts_for); if (r < 0) return r; } @@ -3255,7 +3264,8 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c return strdup_job(reply, job); } -int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) { +int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *ret_error) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *path = NULL; int r; @@ -3272,17 +3282,16 @@ int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *err path, "org.freedesktop.systemd1.Scope", "Abandon", - error, + &error, NULL, NULL); if (r < 0) { - if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED) || - sd_bus_error_has_name(error, BUS_ERROR_SCOPE_NOT_RUNNING)) { - sd_bus_error_free(error); + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED) || + sd_bus_error_has_name(&error, BUS_ERROR_SCOPE_NOT_RUNNING)) return 0; - } + sd_bus_error_move(ret_error, &error); return r; } @@ -3304,7 +3313,7 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo "ssi", unit, who == KILL_LEADER ? "main" : "all", signo); } -int manager_unit_is_active(Manager *manager, const char *unit) { +int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *ret_error) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *path = NULL; @@ -3340,17 +3349,18 @@ int manager_unit_is_active(Manager *manager, const char *unit) { sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) return false; + sd_bus_error_move(ret_error, &error); return r; } r = sd_bus_message_read(reply, "s", &state); if (r < 0) - return -EINVAL; + return r; - return !streq(state, "inactive") && !streq(state, "failed"); + return !STR_IN_SET(state, "inactive", "failed"); } -int manager_job_is_active(Manager *manager, const char *path) { +int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *ret_error) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; int r; @@ -3375,6 +3385,7 @@ int manager_job_is_active(Manager *manager, const char *path) { if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT)) return false; + sd_bus_error_move(ret_error, &error); return r; } diff --git a/src/login/logind-device.c b/src/login/logind-device.c index 9b5b3e879..e724365b1 100644 --- a/src/login/logind-device.c +++ b/src/login/logind-device.c @@ -99,6 +99,8 @@ void device_attach(Device *d, Seat *s) { } } - if (!had_master && d->master) + if (!had_master && d->master && s->started) { + seat_save(s); seat_send_changed(s, "CanGraphical", NULL); + } } diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index c8523fbdf..fe593d5b4 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -34,6 +34,7 @@ Login.KillUserProcesses, config_parse_bool, 0, offse Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users) Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users) Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) +Login.UserStopDelaySec, config_parse_sec, 0, offsetof(Manager, user_stop_delay) Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key) Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key) diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index 0ee5f70f5..c8fc4bb8e 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -21,33 +21,42 @@ #include "terminal-util.h" #include "util.h" -Seat *seat_new(Manager *m, const char *id) { - Seat *s; +int seat_new(Seat** ret, Manager *m, const char *id) { + _cleanup_(seat_freep) Seat *s = NULL; + int r; + assert(ret); assert(m); assert(id); - s = new0(Seat, 1); + if (!seat_name_is_valid(id)) + return -EINVAL; + + s = new(Seat, 1); if (!s) - return NULL; + return -ENOMEM; + + *s = (Seat) { + .manager = m, + }; s->state_file = strappend("/run/systemd/seats/", id); if (!s->state_file) - return mfree(s); + return -ENOMEM; s->id = basename(s->state_file); - s->manager = m; - if (hashmap_put(m->seats, s->id, s) < 0) { - free(s->state_file); - return mfree(s); - } + r = hashmap_put(m->seats, s->id, s); + if (r < 0) + return r; - return s; + *ret = TAKE_PTR(s); + return 0; } -void seat_free(Seat *s) { - assert(s); +Seat* seat_free(Seat *s) { + if (!s) + return NULL; if (s->in_gc_queue) LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s); @@ -64,7 +73,8 @@ void seat_free(Seat *s) { free(s->positions); free(s->state_file); - free(s); + + return mfree(s); } int seat_save(Seat *s) { @@ -166,7 +176,7 @@ static int vt_allocate(unsigned int vtnr) { xsprintf(p, "/dev/tty%u", vtnr); fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) - return -errno; + return fd; return 0; } @@ -190,10 +200,8 @@ int seat_preallocate_vts(Seat *s) { int q; q = vt_allocate(i); - if (q < 0) { - log_error_errno(q, "Failed to preallocate VT %u: %m", i); - r = q; - } + if (q < 0) + r = log_error_errno(q, "Failed to preallocate VT %u: %m", i); } return r; @@ -212,9 +220,9 @@ int seat_apply_acls(Seat *s, Session *old_active) { !!s->active, s->active ? s->active->user->uid : 0); if (r < 0) - log_error_errno(r, "Failed to apply ACLs: %m"); + return log_error_errno(r, "Failed to apply ACLs: %m"); - return r; + return 0; } int seat_set_active(Seat *s, Session *session) { @@ -234,7 +242,7 @@ int seat_set_active(Seat *s, Session *session) { session_send_changed(old_active, "Active", NULL); } - seat_apply_acls(s, old_active); + (void) seat_apply_acls(s, old_active); if (session && session->started) { session_send_changed(session, "Active", NULL); @@ -427,7 +435,7 @@ int seat_start(Seat *s) { } int seat_stop(Seat *s, bool force) { - int r = 0; + int r; assert(s); @@ -437,9 +445,9 @@ int seat_stop(Seat *s, bool force) { "SEAT_ID=%s", s->id, LOG_MESSAGE("Removed seat %s.", s->id)); - seat_stop_sessions(s, force); + r = seat_stop_sessions(s, force); - unlink(s->state_file); + (void) unlink(s->state_file); seat_add_to_gc_queue(s); if (s->started) diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h index 6d4506257..0446384ce 100644 --- a/src/login/logind-seat.h +++ b/src/login/logind-seat.h @@ -27,8 +27,10 @@ struct Seat { LIST_FIELDS(Seat, gc_queue); }; -Seat *seat_new(Manager *m, const char *id); -void seat_free(Seat *s); +int seat_new(Seat **ret, Manager *m, const char *id); +Seat* seat_free(Seat *s); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Seat *, seat_free); int seat_save(Seat *s); int seat_load(Seat *s); diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 8a95fc7c7..e7f64fd61 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -689,6 +689,17 @@ int session_send_lock_all(Manager *m, bool lock) { return r; } +#if 0 /// elogind does not support scope and service jobs +static bool session_ready(Session *s) { + assert(s); + + /* Returns true when the session is ready, i.e. all jobs we enqueued for it are done (regardless if successful or not) */ + + return !s->scope_job && + !s->user->service_job; +} +#endif // 0 + int session_send_create_reply(Session *s, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; _cleanup_close_ int fifo_fd = -1; @@ -696,21 +707,18 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { assert(s); - /* This is called after the session scope and the user service - * were successfully created, and finishes where + /* This is called after the session scope and the user service were successfully created, and finishes where * bus_manager_create_session() left off. */ if (!s->create_message) return 0; #if 0 /// elogind does not support scope and service jobs - if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job)) + if (!sd_bus_error_is_set(error) && !session_ready(s)) return 0; #endif // 0 - c = s->create_message; - s->create_message = NULL; - + c = TAKE_PTR(s->create_message); if (error) return sd_bus_reply_method_error(c, error); @@ -718,8 +726,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { if (fifo_fd < 0) return fifo_fd; - /* Update the session state file before we notify the client - * about the result. */ + /* Update the session state file before we notify the client about the result. */ session_save(s); #if 1 /// Additionally elogind saves the user state file diff --git a/src/login/logind-session.c b/src/login/logind-session.c index fedf58358..dce0f7377 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -5,6 +5,7 @@ #include <linux/kd.h> #include <linux/vt.h> #include <signal.h> +#include <stdio_ext.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> @@ -24,7 +25,9 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +//#include "process-util.h" #include "string-table.h" +//#include "strv.h" #include "terminal-util.h" #include "user-util.h" #include "util.h" @@ -37,47 +40,52 @@ static void session_remove_fifo(Session *s); -Session* session_new(Manager *m, const char *id) { - Session *s; +int session_new(Session **ret, Manager *m, const char *id) { + _cleanup_(session_freep) Session *s = NULL; + int r; + assert(ret); assert(m); assert(id); - assert(session_id_valid(id)); - s = new0(Session, 1); + if (!session_id_valid(id)) + return -EINVAL; + + s = new(Session, 1); if (!s) - return NULL; + return -ENOMEM; + + *s = (Session) { + .manager = m, + .fifo_fd = -1, + .vtfd = -1, + .audit_id = AUDIT_SESSION_INVALID, + .tty_validity = _TTY_VALIDITY_INVALID, + }; s->state_file = strappend("/run/systemd/sessions/", id); if (!s->state_file) - return mfree(s); - - s->devices = hashmap_new(&devt_hash_ops); - if (!s->devices) { - free(s->state_file); - return mfree(s); - } + return -ENOMEM; s->id = basename(s->state_file); - if (hashmap_put(m->sessions, s->id, s) < 0) { - hashmap_free(s->devices); - free(s->state_file); - return mfree(s); - } + s->devices = hashmap_new(&devt_hash_ops); + if (!s->devices) + return -ENOMEM; - s->manager = m; - s->fifo_fd = -1; - s->vtfd = -1; - s->audit_id = AUDIT_SESSION_INVALID; + r = hashmap_put(m->sessions, s->id, s); + if (r < 0) + return r; - return s; + *ret = TAKE_PTR(s); + return 0; } -void session_free(Session *s) { +Session* session_free(Session *s) { SessionDevice *sd; - assert(s); + if (!s) + return NULL; if (s->in_gc_queue) LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s); @@ -98,6 +106,8 @@ void session_free(Session *s) { if (s->user->display == s) s->user->display = NULL; + + user_update_last_session_timer(s->user); } if (s->seat) { @@ -110,10 +120,15 @@ void session_free(Session *s) { LIST_REMOVE(sessions_by_seat, s->seat->sessions, s); } +#if 0 /// elogind does not support systemd units and scope_jobs if (s->scope) { hashmap_remove(s->manager->session_units, s->scope); free(s->scope); } +#endif // 0 + + if (pid_is_valid(s->leader)) + (void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s); #if 0 /// elogind does not support systemd scope_jobs free(s->scope_job); @@ -131,7 +146,8 @@ void session_free(Session *s) { hashmap_remove(s->manager->sessions, s->id); free(s->state_file); - free(s); + + return mfree(s); } void session_set_user(Session *s, User *u) { @@ -140,6 +156,32 @@ void session_set_user(Session *s, User *u) { s->user = u; LIST_PREPEND(sessions_by_user, u->sessions, s); + + user_update_last_session_timer(u); +} + +int session_set_leader(Session *s, pid_t pid) { + int r; + + assert(s); + + if (!pid_is_valid(pid)) + return -EINVAL; + + if (s->leader == pid) + return 0; + + r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pid), s); + if (r < 0) + return r; + + if (pid_is_valid(s->leader)) + (void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s); + + s->leader = pid; + (void) audit_session_from_pid(pid, &s->audit_id); + + return 1; } static void session_save_devices(Session *s, FILE *f) { @@ -175,20 +217,21 @@ int session_save(Session *s) { if (r < 0) goto fail; - assert(s->user); - - fchmod(fileno(f), 0644); + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + (void) fchmod(fileno(f), 0644); fprintf(f, "# This is private data. Do not parse.\n" "UID="UID_FMT"\n" "USER=%s\n" "ACTIVE=%i\n" + "IS_DISPLAY=%i\n" "STATE=%s\n" "REMOTE=%i\n", s->user->uid, s->user->name, session_is_active(s), + s->user->display == s, session_state_to_string(session_get_state(s)), s->remote); @@ -214,6 +257,9 @@ int session_save(Session *s) { if (s->tty) fprintf(f, "TTY=%s\n", s->tty); + if (s->tty_validity >= 0) + fprintf(f, "TTY_VALIDITY=%s\n", tty_validity_to_string(s->tty_validity)); + if (s->display) fprintf(f, "DISPLAY=%s\n", s->display); @@ -350,6 +396,7 @@ static int session_load_devices(Session *s, const char *devices) { int session_load(Session *s) { _cleanup_free_ char *remote = NULL, *seat = NULL, + *tty_validity = NULL, *vtnr = NULL, *state = NULL, *position = NULL, @@ -361,7 +408,8 @@ int session_load(Session *s) { *monotonic = NULL, *controller = NULL, *active = NULL, - *devices = NULL; + *devices = NULL, + *is_display = NULL; int k, r; @@ -376,6 +424,7 @@ int session_load(Session *s) { "FIFO", &s->fifo_path, "SEAT", &seat, "TTY", &s->tty, + "TTY_VALIDITY", &tty_validity, "DISPLAY", &s->display, "REMOTE_HOST", &s->remote_host, "REMOTE_USER", &s->remote_user, @@ -393,6 +442,7 @@ int session_load(Session *s) { "CONTROLLER", &controller, "ACTIVE", &active, "DEVICES", &devices, + "IS_DISPLAY", &is_display, NULL); if (r < 0) @@ -451,9 +501,27 @@ int session_load(Session *s) { seat_claim_position(s->seat, s, npos); } + if (tty_validity) { + TTYValidity v; + + v = tty_validity_from_string(tty_validity); + if (v < 0) + log_debug("Failed to parse TTY validity: %s", tty_validity); + else + s->tty_validity = v; + } + if (leader) { - if (parse_pid(leader, &s->leader) >= 0) - (void) audit_session_from_pid(s->leader, &s->audit_id); + pid_t pid; + + r = parse_pid(leader, &pid); + if (r < 0) + log_debug_errno(r, "Failed to parse leader PID of session: %s", leader); + else { + r = session_set_leader(s, pid); + if (r < 0) + log_warning_errno(r, "Failed to set session leader PID, ignoring: %m"); + } } if (type) { @@ -500,6 +568,18 @@ int session_load(Session *s) { s->was_active = k; } + if (is_display) { + /* Note that when enumerating users are loaded before sessions, hence the display session to use is + * something we have to store along with the session and not the user, as in that case we couldn't + * apply it at the time we load the user. */ + + k = parse_boolean(is_display); + if (k < 0) + log_warning_errno(k, "Failed to parse IS_DISPLAY session property: %m"); + else if (k > 0) + s->user->display = s; + } + if (controller) { if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) { session_set_controller(s, controller, false, false); @@ -525,7 +605,7 @@ int session_activate(Session *s) { /* on seats with VTs, we let VTs manage session-switching */ if (seat_has_vts(s->seat)) { - if (!s->vtnr) + if (s->vtnr == 0) return -EOPNOTSUPP; return chvt(s->vtnr); @@ -549,17 +629,18 @@ int session_activate(Session *s) { } #if 0 /// UNNEEDED by elogind -static int session_start_scope(Session *s, sd_bus_message *properties) { +static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) { int r; assert(s); assert(s->user); if (!s->scope) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *scope, *job = NULL; + _cleanup_free_ char *scope = NULL; const char *description; + s->scope_job = mfree(s->scope_job); + scope = strjoin("session-", s->id, ".scope"); if (!scope) return log_oom(); @@ -572,21 +653,16 @@ static int session_start_scope(Session *s, sd_bus_message *properties) { s->leader, s->user->slice, description, - "systemd-logind.service", - "systemd-user-sessions.service", + STRV_MAKE(s->user->runtime_dir_service, s->user->service), /* These two have StopWhenUnneeded= set, hence add a dep towards them */ + STRV_MAKE("systemd-logind.service", "systemd-user-sessions.service", s->user->runtime_dir_service, s->user->service), /* And order us after some more */ + s->user->home, properties, - &error, - &job); - if (r < 0) { - log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r)); - free(scope); - return r; - } else { - s->scope = scope; + error, + &s->scope_job); + if (r < 0) + return log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(error, r)); - free(s->scope_job); - s->scope_job = job; - } + s->scope = TAKE_PTR(scope); } if (s->scope) @@ -615,7 +691,7 @@ static int session_start_cgroup(Session *s) { } #endif // 0 -int session_start(Session *s, sd_bus_message *properties) { +int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) { int r; assert(s); @@ -623,6 +699,9 @@ int session_start(Session *s, sd_bus_message *properties) { if (!s->user) return -ESTALE; + if (s->stopping) + return -EINVAL; + if (s->started) return 0; @@ -630,9 +709,8 @@ int session_start(Session *s, sd_bus_message *properties) { if (r < 0) return r; - /* Create cgroup */ #if 0 /// elogind does its own session management - r = session_start_scope(s, properties); + r = session_start_scope(s, properties, error); #else r = session_start_cgroup(s); #endif // 0 @@ -687,21 +765,24 @@ static int session_stop_scope(Session *s, bool force) { * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log * when killing any processes left after this point. */ r = manager_abandon_scope(s->manager, s->scope, &error); - if (r < 0) + if (r < 0) { log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r)); + sd_bus_error_free(&error); + } + + s->scope_job = mfree(s->scope_job); /* Optionally, let's kill everything that's left now. */ if (force || manager_shall_kill(s->manager, s->user->name)) { - char *job = NULL; - r = manager_stop_unit(s->manager, s->scope, &error, &job); - if (r < 0) - return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r)); + r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job); + if (r < 0) { + if (force) + return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r)); - free(s->scope_job); - s->scope_job = job; + log_warning_errno(r, "Failed to stop session scope, ignoring: %s", bus_error_message(&error, r)); + } } else { - s->scope_job = mfree(s->scope_job); /* With no killing, this session is allowed to persist in "closing" state indefinitely. * Therefore session stop and session removal may be two distinct events. @@ -739,8 +820,17 @@ int session_stop(Session *s, bool force) { assert(s); + /* This is called whenever we begin with tearing down a session record. It's called in four cases: explicit API + * request via the bus (either directly for the session object or for the seat or user object this session + * belongs to; 'force' is true), or due to automatic GC (i.e. scope vanished; 'force' is false), or because the + * session FIFO saw an EOF ('force' is false), or because the release timer hit ('force' is false). */ + if (!s->user) return -ESTALE; + if (!s->started) + return 0; + if (s->stopping) + return 0; s->timer_event_source = sd_event_source_unref(s->timer_event_source); @@ -839,7 +929,7 @@ int session_release(Session *s) { return sd_event_add_time(s->manager->event, &s->timer_event_source, CLOCK_MONOTONIC, - now(CLOCK_MONOTONIC) + RELEASE_USEC, 0, + usec_add(now(CLOCK_MONOTONIC), RELEASE_USEC), 0, release_timeout_callback, s); } @@ -918,7 +1008,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) { /* For sessions with a leader but no explicitly configured * tty, let's check the controlling tty of the leader */ - if (s->leader > 0) { + if (pid_is_valid(s->leader)) { r = get_process_ctty_atime(s->leader, &atime); if (r >= 0) goto found_atime; @@ -1002,7 +1092,8 @@ int session_create_fifo(Session *s) { if (r < 0) return r; - if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0) + s->fifo_path = strjoin("/run/systemd/sessions/", s->id, ".ref"); + if (!s->fifo_path) return -ENOMEM; if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST) @@ -1014,7 +1105,6 @@ int session_create_fifo(Session *s) { s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (s->fifo_fd < 0) return -errno; - } if (!s->fifo_event_source) { @@ -1044,12 +1134,16 @@ static void session_remove_fifo(Session *s) { s->fifo_fd = safe_close(s->fifo_fd); if (s->fifo_path) { - unlink(s->fifo_path); + (void) unlink(s->fifo_path); s->fifo_path = mfree(s->fifo_path); } } bool session_may_gc(Session *s, bool drop_not_started) { +#if 0 /// UNNEEDED by elogind + int r; +#endif // 0 + assert(s); if (drop_not_started && !s->started) @@ -1064,11 +1158,25 @@ bool session_may_gc(Session *s, bool drop_not_started) { } #if 0 /// elogind supports neither scopes nor jobs - if (s->scope_job && manager_job_is_active(s->manager, s->scope_job)) - return false; + if (s->scope_job) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - if (s->scope && manager_unit_is_active(s->manager, s->scope)) - return false; + r = manager_job_is_active(s->manager, s->scope_job, &error); + if (r < 0) + log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", s->scope_job, bus_error_message(&error, r)); + if (r != 0) + return false; + } + + if (s->scope) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + r = manager_unit_is_active(s->manager, s->scope, &error); + if (r < 0) + log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", s->scope, bus_error_message(&error, r)); + if (r != 0) + return false; + } #endif // 0 return true; @@ -1395,3 +1503,11 @@ static const char* const kill_who_table[_KILL_WHO_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); + +static const char* const tty_validity_table[_TTY_VALIDITY_MAX] = { + [TTY_FROM_PAM] = "from-pam", + [TTY_FROM_UTMP] = "from-utmp", + [TTY_UTMP_INCONSISTENT] = "utmp-inconsistent", +}; + +DEFINE_STRING_TABLE_LOOKUP(tty_validity, TTYValidity); diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 117f4683b..0bca7683a 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -46,6 +46,14 @@ enum KillWho { _KILL_WHO_INVALID = -1 }; +typedef enum TTYValidity { + TTY_FROM_PAM, + TTY_FROM_UTMP, + TTY_UTMP_INCONSISTENT, /* may happen on ssh sessions with multiplexed TTYs */ + _TTY_VALIDITY_MAX, + _TTY_VALIDITY_INVALID = -1, +} TTYValidity; + struct Session { Manager *manager; @@ -60,8 +68,9 @@ struct Session { dual_timestamp timestamp; - char *tty; char *display; + char *tty; + TTYValidity tty_validity; bool remote; char *remote_user; @@ -99,6 +108,7 @@ struct Session { sd_bus_message *create_message; + /* Set up when a client requested to release the session via the bus */ sd_event_source *timer_event_source; char *controller; @@ -111,9 +121,13 @@ struct Session { LIST_FIELDS(Session, gc_queue); }; -Session *session_new(Manager *m, const char *id); -void session_free(Session *s); +int session_new(Session **ret, Manager *m, const char *id); +Session* session_free(Session *s); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Session *, session_free); + void session_set_user(Session *s, User *u); +int session_set_leader(Session *s, pid_t pid); bool session_may_gc(Session *s, bool drop_not_started); void session_add_to_gc_queue(Session *s); int session_activate(Session *s); @@ -123,7 +137,7 @@ void session_set_idle_hint(Session *s, bool b); int session_get_locked_hint(Session *s); void session_set_locked_hint(Session *s, bool b); int session_create_fifo(Session *s); -int session_start(Session *s, sd_bus_message *properties); +int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error); int session_stop(Session *s, bool force); int session_finalize(Session *s); int session_release(Session *s); @@ -157,6 +171,9 @@ SessionClass session_class_from_string(const char *s) _pure_; const char *kill_who_to_string(KillWho k) _const_; KillWho kill_who_from_string(const char *s) _pure_; +const char* tty_validity_to_string(TTYValidity t) _const_; +TTYValidity tty_validity_from_string(const char *s) _pure_; + int session_prepare_vt(Session *s); void session_restore_vt(Session *s); void session_leave_vt(Session *s); diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index c662a26b9..9620fb0cf 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -109,7 +109,7 @@ static int property_get_idle_since_hint( assert(reply); assert(u); - user_get_idle_hint(u, &t); + (void) user_get_idle_hint(u, &t); k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; return sd_bus_message_append(reply, "t", k); diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 38a15b224..78822c921 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -26,40 +26,54 @@ #include "special.h" #include "stdio-util.h" #include "string-table.h" +//#include "strv.h" #include "unit-name.h" #include "user-util.h" //#include "util.h" /// Additional includes needed by elogind #include "user-runtime-dir.h" -int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { +int user_new(User **ret, + Manager *m, + uid_t uid, + gid_t gid, + const char *name, + const char *home) { + _cleanup_(user_freep) User *u = NULL; char lu[DECIMAL_STR_MAX(uid_t) + 1]; int r; - assert(out); + assert(ret); assert(m); assert(name); - u = new0(User, 1); + u = new(User, 1); if (!u) return -ENOMEM; - u->manager = m; - u->uid = uid; - u->gid = gid; - xsprintf(lu, UID_FMT, uid); + *u = (User) { + .manager = m, + .uid = uid, + .gid = gid, + .last_session_timestamp = USEC_INFINITY, + }; u->name = strdup(name); if (!u->name) return -ENOMEM; + u->home = strdup(home); + if (!u->home) + return -ENOMEM; + if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0) return -ENOMEM; if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0) return -ENOMEM; + xsprintf(lu, UID_FMT, uid); r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice); if (r < 0) return r; @@ -68,10 +82,15 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { if (r < 0) return r; + r = unit_name_build("user-runtime-dir", lu, ".service", &u->runtime_dir_service); + if (r < 0) + return r; + r = hashmap_put(m->users, UID_TO_PTR(uid), u); if (r < 0) return r; +#if 0 /// elogind does not support systemd units r = hashmap_put(m->user_units, u->slice, u); if (r < 0) return r; @@ -80,8 +99,12 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { if (r < 0) return r; - *out = TAKE_PTR(u); + r = hashmap_put(m->user_units, u->runtime_dir_service, u); + if (r < 0) + return r; +#endif // 0 + *ret = TAKE_PTR(u); return 0; } @@ -95,24 +118,32 @@ User *user_free(User *u) { while (u->sessions) session_free(u->sessions); +#if 0 /// elogind does not support systemd units if (u->service) hashmap_remove_value(u->manager->user_units, u->service, u); + if (u->runtime_dir_service) + hashmap_remove_value(u->manager->user_units, u->runtime_dir_service, u); + if (u->slice) hashmap_remove_value(u->manager->user_units, u->slice, u); +#endif // 0 hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u); + (void) sd_event_source_unref(u->timer_event_source); + #if 0 /// elogind neither supports slice nor service jobs. - u->slice_job = mfree(u->slice_job); u->service_job = mfree(u->service_job); #endif // 0 u->service = mfree(u->service); + u->runtime_dir_service = mfree(u->runtime_dir_service); u->slice = mfree(u->slice); u->runtime_path = mfree(u->runtime_path); u->state_file = mfree(u->state_file); u->name = mfree(u->name); + u->home = mfree(u->home); return mfree(u); } @@ -139,9 +170,11 @@ static int user_save_internal(User *u) { fprintf(f, "# This is private data. Do not parse.\n" "NAME=%s\n" - "STATE=%s\n", + "STATE=%s\n" /* friendly user-facing state */ + "STOPPING=%s\n", /* low-level state */ u->name, - user_state_to_string(user_get_state(u))); + user_state_to_string(user_get_state(u)), + yes_no(u->stopping)); /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */ if (u->runtime_path) @@ -151,10 +184,7 @@ static int user_save_internal(User *u) { if (u->service_job) fprintf(f, "SERVICE_JOB=%s\n", u->service_job); - if (u->slice_job) - fprintf(f, "SLICE_JOB=%s\n", u->slice_job); #endif // 0 - if (u->display) fprintf(f, "DISPLAY=%s\n", u->display->id); @@ -165,6 +195,10 @@ static int user_save_internal(User *u) { u->timestamp.realtime, u->timestamp.monotonic); + if (u->last_session_timestamp != USEC_INFINITY) + fprintf(f, "LAST_SESSION_TIMESTAMP=" USEC_FMT "\n", + u->last_session_timestamp); + if (u->sessions) { Session *i; bool first; @@ -278,120 +312,96 @@ int user_save(User *u) { if (!u->started) return 0; - return user_save_internal (u); + return user_save_internal(u); } int user_load(User *u) { - _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL; - Session *s = NULL; + _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL, *last_session_timestamp = NULL; int r; assert(u); r = parse_env_file(NULL, u->state_file, NEWLINE, #if 0 /// elogind neither supports service nor slice jobs - "SERVICE_JOB", &u->service_job, - "SLICE_JOB", &u->slice_job, + "SERVICE_JOB", &u->service_job, #endif // 0 - "DISPLAY", &display, - "REALTIME", &realtime, - "MONOTONIC", &monotonic, + "STOPPING", &stopping, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + "LAST_SESSION_TIMESTAMP", &last_session_timestamp, NULL); - if (r < 0) { - if (r == -ENOENT) - return 0; - + if (r == -ENOENT) + return 0; + if (r < 0) return log_error_errno(r, "Failed to read %s: %m", u->state_file); - } - - if (display) - s = hashmap_get(u->manager->sessions, display); - if (s && s->display && display_is_local(s->display)) - u->display = s; + if (stopping) { + r = parse_boolean(stopping); + if (r < 0) + log_debug_errno(r, "Failed to parse 'STOPPING' boolean: %s", stopping); + else + u->stopping = r; + } if (realtime) - timestamp_deserialize(realtime, &u->timestamp.realtime); + (void) timestamp_deserialize(realtime, &u->timestamp.realtime); if (monotonic) - timestamp_deserialize(monotonic, &u->timestamp.monotonic); + (void) timestamp_deserialize(monotonic, &u->timestamp.monotonic); + if (last_session_timestamp) + (void) timestamp_deserialize(last_session_timestamp, &u->last_session_timestamp); - return r; + return 0; } -static int user_start_service(User *u) { -#if 0 /// elogind can not ask systemd via dbus to start user services +#if 0 /// elogind neither spawns systemd --user nor suports systemd units and services. +static void user_start_service(User *u) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; int r; assert(u); + /* Start the service containing the "systemd --user" instance (user@.service). Note that we don't explicitly + * start the per-user slice or the systemd-runtime-dir@.service instance, as those are pulled in both by + * user@.service and the session scopes as dependencies. */ + u->service_job = mfree(u->service_job); - r = manager_start_unit( - u->manager, - u->service, - &error, - &job); + r = manager_start_unit(u->manager, u->service, &error, &u->service_job); if (r < 0) - /* we don't fail due to this, let's try to continue */ - log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); - else - u->service_job = job; -#else - assert(u); - - hashmap_put(u->manager->user_units, u->service, u); -#endif // 0 - - return 0; + log_warning_errno(r, "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r)); } +#endif // 0 int user_start(User *u) { - int r; - assert(u); if (u->started && !u->stopping) return 0; - /* - * If u->stopping is set, the user is marked for removal and the slice - * and service stop-jobs are queued. We have to clear that flag before - * queing the start-jobs again. If they succeed, the user object can be - * re-used just fine (pid1 takes care of job-ordering and proper - * restart), but if they fail, we want to force another user_stop() so - * possibly pending units are stopped. - * Note that we don't clear u->started, as we have no clue what state - * the user is in on failure here. Hence, we pretend the user is - * running so it will be properly taken down by GC. However, we clearly - * return an error from user_start() in that case, so no further - * reference to the user is taken. - */ + /* If u->stopping is set, the user is marked for removal and service stop-jobs are queued. We have to clear + * that flag before queing the start-jobs again. If they succeed, the user object can be re-used just fine + * (pid1 takes care of job-ordering and proper restart), but if they fail, we want to force another user_stop() + * so possibly pending units are stopped. */ u->stopping = false; -#if 0 /// elogind has to prepare the XDG_RUNTIME_DIR by itself if (!u->started) log_debug("Starting services for new user %s.", u->name); -#else - if (!u->started) { - log_debug("Starting services for new user %s.", u->name); - r = user_runtime_dir("start", u); - if (r < 0) - return r; - } + +#if 1 /// elogind has to prepare the XDG_RUNTIME_DIR by itself + int r; + r = user_runtime_dir("start", u); + if (r < 0) + return r; #endif // 1 - /* Save the user data so far, because pam_systemd will read the - * XDG_RUNTIME_DIR out of it while starting up systemd --user. - * We need to do user_save_internal() because we have not - * "officially" started yet. */ + /* Save the user data so far, because pam_systemd will read the XDG_RUNTIME_DIR out of it while starting up + * systemd --user. We need to do user_save_internal() because we have not "officially" started yet. */ user_save_internal(u); - /* Spawn user systemd */ - r = user_start_service(u); - if (r < 0) - return r; +#if 0 /// elogind does not spawn user instances of systemd + /* Start user@UID.service */ + user_start_service(u); +#endif // 0 if (!u->started) { if (!dual_timestamp_is_set(&u->timestamp)) @@ -406,84 +416,59 @@ int user_start(User *u) { return 0; } -#if 0 /// UNNEEDED by elogind -static int user_stop_slice(User *u) { +#if 0 /// elogind does not support user services and systemd units +static void user_stop_service(User *u) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; int r; assert(u); + assert(u->service); - r = manager_stop_unit(u->manager, u->slice, &error, &job); - if (r < 0) { - log_error("Failed to stop user slice: %s", bus_error_message(&error, r)); - return r; - } - - free(u->slice_job); - u->slice_job = job; - - return r; -} - -static int user_stop_service(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; - int r; + /* The reverse of user_start_service(). Note that we only stop user@UID.service here, and let StopWhenUnneeded= + * deal with the slice and the user-runtime-dir@.service instance. */ - assert(u); - - r = manager_stop_unit(u->manager, u->service, &error, &job); - if (r < 0) { - log_error("Failed to stop user service: %s", bus_error_message(&error, r)); - return r; - } + u->service_job = mfree(u->service_job); - free_and_replace(u->service_job, job); - return r; + r = manager_stop_unit(u->manager, u->service, &error, &u->service_job); + if (r < 0) + log_warning_errno(r, "Failed to stop user service '%s', ignoring: %s", u->service, bus_error_message(&error, r)); } #endif // 0 int user_stop(User *u, bool force) { Session *s; - int r = 0, k; + int r = 0; assert(u); - /* Stop jobs have already been queued */ - if (u->stopping) { + /* This is called whenever we begin with tearing down a user record. It's called in two cases: explicit API + * request to do so via the bus (in which case 'force' is true) and automatically due to GC, if there's no + * session left pinning it (in which case 'force' is false). Note that this just initiates tearing down of the + * user, the User object will remain in memory until user_finalize() is called, see below. */ + + if (!u->started) + return 0; + + if (u->stopping) { /* Stop jobs have already been queued */ user_save(u); -#if 1 /// elogind must queue this user again - user_add_to_gc_queue(u); -#endif // 1 - return r; + return 0; } LIST_FOREACH(sessions_by_user, s, u->sessions) { + int k; + k = session_stop(s, force); if (k < 0) r = k; } - /* Kill systemd */ #if 0 /// elogind does not support service or slice jobs - k = user_stop_service(u); - if (k < 0) - r = k; - - /* Kill cgroup */ - k = user_stop_slice(u); - if (k < 0) - r = k; + user_stop_service(u); #endif // 0 u->stopping = true; user_save(u); -#if 1 /// elogind must queue this user again - user_add_to_gc_queue(u); -#endif // 1 - return r; } @@ -493,6 +478,9 @@ int user_finalize(User *u) { assert(u); + /* Called when the user is really ready to be freed, i.e. when all unit stop jobs and suchlike for it are + * done. This is called as a result of an earlier user_done() when all jobs are completed. */ + if (u->started) log_debug("User %s logged out.", u->name); @@ -508,7 +496,6 @@ int user_finalize(User *u) { if (k < 0) r = k; #endif // 1 - /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to * system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such @@ -521,7 +508,7 @@ int user_finalize(User *u) { r = k; } - unlink(u->state_file); + (void) unlink(u->state_file); user_add_to_gc_queue(u); if (u->started) { @@ -577,11 +564,44 @@ int user_check_linger_file(User *u) { return -ENOMEM; p = strjoina("/var/lib/elogind/linger/", cc); + if (access(p, F_OK) < 0) { + if (errno != ENOENT) + return -errno; + + return false; + } + + return true; +} + +#if 0 /// elogind does not support systemd units +static bool user_unit_active(User *u) { + const char *i; + int r; + + assert(u->service); + assert(u->runtime_dir_service); + assert(u->slice); + + FOREACH_STRING(i, u->service, u->runtime_dir_service, u->slice) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + r = manager_unit_is_active(u->manager, i, &error); + if (r < 0) + log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", u->service, bus_error_message(&error, r)); + if (r != 0) + return true; + } - return access(p, F_OK) >= 0; + return false; } +#endif // 0 bool user_may_gc(User *u, bool drop_not_started) { +#if 0 /// UNNEEDED by elogind + int r; +#endif // 0 + assert(u); if (drop_not_started && !u->started) @@ -590,15 +610,39 @@ bool user_may_gc(User *u, bool drop_not_started) { if (u->sessions) return false; + if (u->last_session_timestamp != USEC_INFINITY) { + /* All sessions have been closed. Let's see if we shall leave the user record around for a bit */ + + if (u->manager->user_stop_delay == USEC_INFINITY) + return false; /* Leave it around forever! */ + if (u->manager->user_stop_delay > 0 && + now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, u->manager->user_stop_delay)) + return false; /* Leave it around for a bit longer. */ + } + + /* Is this a user that shall stay around forever ("linger")? Before we say "no" to GC'ing for lingering users, let's check + * if any of the three units that we maintain for this user is still around. If none of them is, + * there's no need to keep this user around even if lingering is enabled. */ +#if 0 /// elogind does not support systemd units + if (user_check_linger_file(u) > 0 && user_unit_active(u)) +#else if (user_check_linger_file(u) > 0) +#endif // 0 return false; #if 0 /// elogind neither supports service nor slice jobs - if (u->slice_job && manager_job_is_active(u->manager, u->slice_job)) - return false; + /* Check if our job is still pending */ + if (u->service_job) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - if (u->service_job && manager_job_is_active(u->manager, u->service_job)) - return false; + r = manager_job_is_active(u->manager, u->service_job, &error); + if (r < 0) + log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", u->service_job, bus_error_message(&error, r)); + if (r != 0) + return false; + } + /* Note that we don't care if the three units we manage for each user object are up or not, as we are managing + * their state rather than tracking it. */ #endif // 0 return true; @@ -623,7 +667,7 @@ UserState user_get_state(User *u) { return USER_CLOSING; #if 0 /// elogind neither supports service nor slice jobs. - if (!u->started || u->slice_job || u->service_job) + if (!u->started || u->service_job) #else if (!u->started) #endif // 0 @@ -645,7 +689,11 @@ UserState user_get_state(User *u) { return all_closing ? USER_CLOSING : USER_ONLINE; } +#if 0 /// elogind does not support systemd units + if (user_check_linger_file(u) > 0 && user_unit_active(u)) +#else if (user_check_linger_file(u) > 0) +#endif // 0 return USER_LINGERING; return USER_CLOSING; @@ -673,11 +721,10 @@ int user_kill(User *u, int signo) { } static bool elect_display_filter(Session *s) { - /* Return true if the session is a candidate for the user’s ‘primary - * session’ or ‘display’. */ + /* Return true if the session is a candidate for the user’s ‘primary session’ or ‘display’. */ assert(s); - return (s->class == SESSION_USER && !s->stopping); + return s->class == SESSION_USER && s->started && !s->stopping; } static int elect_display_compare(Session *s1, Session *s2) { @@ -723,9 +770,8 @@ void user_elect_display(User *u) { assert(u); - /* This elects a primary session for each user, which we call - * the "display". We try to keep the assignment stable, but we - * "upgrade" to better choices. */ + /* This elects a primary session for each user, which we call the "display". We try to keep the assignment + * stable, but we "upgrade" to better choices. */ log_debug("Electing new display for user %s", u->name); LIST_FOREACH(sessions_by_user, s, u->sessions) { @@ -741,6 +787,59 @@ void user_elect_display(User *u) { } } +static int user_stop_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) { + User *u = userdata; + + assert(u); + user_add_to_gc_queue(u); + + return 0; +} + +void user_update_last_session_timer(User *u) { + int r; + + assert(u); + + if (u->sessions) { + /* There are sessions, turn off the timer */ + u->last_session_timestamp = USEC_INFINITY; + u->timer_event_source = sd_event_source_unref(u->timer_event_source); + return; + } + + if (u->last_session_timestamp != USEC_INFINITY) + return; /* Timer already started */ + + u->last_session_timestamp = now(CLOCK_MONOTONIC); + + assert(!u->timer_event_source); + + if (u->manager->user_stop_delay == 0 || u->manager->user_stop_delay == USEC_INFINITY) + return; + + if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) { + log_debug("Not allocating user stop timeout, since we are already exiting."); + return; + } + + r = sd_event_add_time(u->manager->event, + &u->timer_event_source, + CLOCK_MONOTONIC, + usec_add(u->last_session_timestamp, u->manager->user_stop_delay), 0, + user_stop_timeout_callback, u); + if (r < 0) + log_warning_errno(r, "Failed to enqueue user stop event source, ignoring: %m"); + + if (DEBUG_LOGGING) { + char s[FORMAT_TIMESPAN_MAX]; + + log_debug("Last session of user '%s' logged out, terminating user context in %s.", + u->name, + format_timespan(s, sizeof(s), u->manager->user_stop_delay, USEC_PER_MSEC)); + } +} + static const char* const user_state_table[_USER_STATE_MAX] = { [USER_OFFLINE] = "offline", [USER_OPENING] = "opening", @@ -813,7 +912,7 @@ int config_parse_compat_user_tasks_max( log_syntax(unit, LOG_NOTICE, filename, line, 0, "Support for option %s= has been removed.", lvalue); - log_info("Hint: try creating /etc/elogind/system/user-.slice/50-limits.conf with:\n" + log_info("Hint: try creating /etc/elogind/system/user-.slice.d/50-limits.conf with:\n" " [Slice]\n" " TasksMax=%s", rvalue); diff --git a/src/login/logind-user.h b/src/login/logind-user.h index afb6f4b1a..ca404f31b 100644 --- a/src/login/logind-user.h +++ b/src/login/logind-user.h @@ -23,29 +23,36 @@ struct User { uid_t uid; gid_t gid; char *name; + char *home; char *state_file; char *runtime_path; - char *slice; - char *service; + + char *slice; /* user-UID.slice */ + char *service; /* user@UID.service */ + char *runtime_dir_service; /* user-runtime-dir@UID.service */ #if 0 /// UNNEEDED by elogind char *service_job; - char *slice_job; #endif // 0 Session *display; - dual_timestamp timestamp; + dual_timestamp timestamp; /* When this User object was 'started' the first time */ + usec_t last_session_timestamp; /* When the number of sessions of this user went from 1 to 0 the last time */ + + /* Set up when the last session of the user logs out */ + sd_event_source *timer_event_source; bool in_gc_queue:1; - bool started:1; - bool stopping:1; + + bool started:1; /* Whenever the user being started, has been started or is being stopped again. */ + bool stopping:1; /* Whenever the user is being stopped or has been stopped. */ LIST_HEAD(Session, sessions); LIST_FIELDS(User, gc_queue); }; -int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name); +int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name, const char *home); User *user_free(User *u); DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free); @@ -62,6 +69,7 @@ int user_load(User *u); int user_kill(User *u, int signo); int user_check_linger_file(User *u); void user_elect_display(User *u); +void user_update_last_session_timer(User *u); extern const sd_bus_vtable user_vtable[]; int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c index f18147688..d3489f5f0 100644 --- a/src/login/logind-utmp.c +++ b/src/login/logind-utmp.c @@ -61,7 +61,7 @@ bool logind_wall_tty_filter(const char *tty, void *userdata) { static int warn_wall(Manager *m, usec_t n) { char date[FORMAT_TIMESTAMP_MAX] = {}; - _cleanup_free_ char *l = NULL; + _cleanup_free_ char *l = NULL, *username = NULL; usec_t left; int r; @@ -83,8 +83,8 @@ static int warn_wall(Manager *m, usec_t n) { return 0; } - utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid), - m->scheduled_shutdown_tty, logind_wall_tty_filter, m); + username = uid_to_name(m->scheduled_shutdown_uid); + utmp_wall(l, username, m->scheduled_shutdown_tty, logind_wall_tty_filter, m); return 1; } diff --git a/src/login/logind.c b/src/login/logind.c index c7acb05cb..558733b30 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -45,28 +45,35 @@ static int manager_new(Manager **ret) { assert(ret); - m = new0(Manager, 1); + m = new(Manager, 1); if (!m) return -ENOMEM; - m->console_active_fd = -1; -#if 0 /// UNNEEDED by elogind - m->reserve_vt_fd = -1; + *m = (Manager) { + .console_active_fd = -1, +#if 0 /// elogind does not support autospawning of vts + .reserve_vt_fd = -1, #endif // 0 + }; m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); m->devices = hashmap_new(&string_hash_ops); m->seats = hashmap_new(&string_hash_ops); m->sessions = hashmap_new(&string_hash_ops); + m->sessions_by_leader = hashmap_new(NULL); m->users = hashmap_new(NULL); m->inhibitors = hashmap_new(&string_hash_ops); m->buttons = hashmap_new(&string_hash_ops); +#if 0 /// elogind does not support units m->user_units = hashmap_new(&string_hash_ops); m->session_units = hashmap_new(&string_hash_ops); - if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units) + if (!m->devices || !m->seats || !m->sessions || !m->sessions_by_leader || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units) +#else + if (!m->devices || !m->seats || !m->sessions || !m->sessions_by_leader || !m->users || !m->inhibitors || !m->buttons) +#endif // 0 return -ENOMEM; #if 1 /// elogind needs some more data @@ -82,6 +89,7 @@ static int manager_new(Manager **ret) { if (r < 0) return r; +#if 0 /// elogind uses its own signal handler, installed at elogind_manager_startup() r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); if (r < 0) return r; @@ -89,6 +97,7 @@ static int manager_new(Manager **ret) { r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); if (r < 0) return r; +#endif // 0 (void) sd_event_set_watchdog(m->event, true); @@ -130,12 +139,15 @@ static Manager* manager_unref(Manager *m) { hashmap_free(m->devices); hashmap_free(m->seats); hashmap_free(m->sessions); + hashmap_free(m->sessions_by_leader); hashmap_free(m->users); hashmap_free(m->inhibitors); hashmap_free(m->buttons); +#if 0 /// elogind does not support systemd units. hashmap_free(m->user_units); hashmap_free(m->session_units); +#endif // 0 sd_event_source_unref(m->idle_action_event_source); sd_event_source_unref(m->inhibit_timeout_source); @@ -150,6 +162,10 @@ static Manager* manager_unref(Manager *m) { sd_event_source_unref(m->udev_button_event_source); sd_event_source_unref(m->lid_switch_ignore_event_source); +#if ENABLE_UTMP + sd_event_source_unref(m->utmp_event_source); +#endif + safe_close(m->console_active_fd); udev_monitor_unref(m->udev_seat_monitor); @@ -852,28 +868,28 @@ static int manager_connect_console(Manager *m) { assert(m); assert(m->console_active_fd < 0); - /* On certain architectures (S390 and Xen, and containers), - /dev/tty0 does not exist, so don't fail if we can't open - it. */ + /* On certain systems (such as S390, Xen, and containers) /dev/tty0 does not exist (as there is no VC), so + * don't fail if we can't open it. */ + if (access("/dev/tty0", F_OK) < 0) return 0; m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC); if (m->console_active_fd < 0) { - /* On some systems the device node /dev/tty0 may exist - * even though /sys/class/tty/tty0 does not. */ - if (errno == ENOENT) + /* On some systems /dev/tty0 may exist even though /sys/class/tty/tty0 does not. These are broken, but + * common. Let's complain but continue anyway. */ + if (errno == ENOENT) { + log_warning_errno(errno, "System has /dev/tty0 but not /sys/class/tty/tty0/active which is broken, ignoring: %m"); return 0; + } return log_error_errno(errno, "Failed to open /sys/class/tty/tty0/active: %m"); } r = sd_event_add_io(m->event, &m->console_active_event_source, m->console_active_fd, 0, manager_dispatch_console, m); - if (r < 0) { - log_error("Failed to watch foreground console"); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to watch foreground console: %m"); /* * SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used @@ -892,7 +908,7 @@ static int manager_connect_console(Manager *m) { r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m); if (r < 0) - return r; + return log_error_errno(r, "Failed to subscribe to signal: %m"); return 0; } @@ -1018,13 +1034,13 @@ static void manager_gc(Manager *m, bool drop_not_started) { /* First, if we are not closing yet, initiate stopping */ if (session_may_gc(session, drop_not_started) && session_get_state(session) != SESSION_CLOSING) - session_stop(session, false); + (void) session_stop(session, false); /* Normally, this should make the session referenced * again, if it doesn't then let's get rid of it * immediately */ if (session_may_gc(session, drop_not_started)) { - session_finalize(session); + (void) session_finalize(session); session_free(session); } } @@ -1035,11 +1051,11 @@ static void manager_gc(Manager *m, bool drop_not_started) { /* First step: queue stop jobs */ if (user_may_gc(user, drop_not_started)) - user_stop(user, false); + (void) user_stop(user, false); /* Second step: finalize user */ if (user_may_gc(user, drop_not_started)) { - user_finalize(user); + (void) user_finalize(user); user_free(user); } } @@ -1137,9 +1153,15 @@ static int manager_startup(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register SIGHUP handler: %m"); -#if 1 /// elogind needs some extra preparations before connecting... - elogind_manager_startup(m); +#if 1 /// install elogind specific signal handlers + r = elogind_manager_startup(m); + if (r < 0) + return log_error_errno(r, "Failed to register elogind signal handlers: %m"); #endif // 1 + + /* Connect to utmp */ + manager_connect_utmp(m); + /* Connect to console */ r = manager_connect_console(m); if (r < 0) @@ -1197,15 +1219,18 @@ static int manager_startup(Manager *m) { manager_reserve_vt(m); #endif // 0 + /* Read in utmp if it exists */ + manager_read_utmp(m); + /* And start everything */ HASHMAP_FOREACH(seat, m->seats, i) - seat_start(seat); + (void) seat_start(seat); HASHMAP_FOREACH(user, m->users, i) - user_start(user); + (void) user_start(user); HASHMAP_FOREACH(session, m->sessions, i) - session_start(session, NULL); + (void) session_start(session, NULL, NULL); HASHMAP_FOREACH(inhibitor, m->inhibitors, i) inhibitor_start(inhibitor); @@ -1307,7 +1332,11 @@ int main(int argc, char *argv[]) { return log_error_errno(r, "Failed to create /run/systemd/machines : %m"); #endif // 0 +#if 0 /// elogind also blocks SIGQUIT, and installs a signal handler for it assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGHUP, SIGTERM, SIGINT, -1) >= 0); +#else + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGHUP, SIGTERM, SIGINT, SIGQUIT, -1) >= 0); +#endif // 0 r = manager_new(&m); if (r < 0) { diff --git a/src/login/logind.h b/src/login/logind.h index 3f17d5d6d..6db015e82 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -37,6 +37,7 @@ struct Manager { Hashmap *devices; Hashmap *seats; Hashmap *sessions; + Hashmap *sessions_by_leader; Hashmap *users; Hashmap *inhibitors; Hashmap *buttons; @@ -54,6 +55,10 @@ struct Manager { sd_event_source *udev_vcsa_event_source; sd_event_source *udev_button_event_source; +#if ENABLE_UTMP + sd_event_source *utmp_event_source; +#endif + #if 0 /// elogind does not support autospawning of vts int console_active_fd; @@ -89,10 +94,13 @@ struct Manager { unsigned long session_counter; unsigned long inhibit_counter; +#if 0 /// elogind does not support units Hashmap *session_units; Hashmap *user_units; +#endif // 0 usec_t inhibit_delay_max; + usec_t user_stop_delay; /* If an action is currently being executed or is delayed, * this is != 0 and encodes what is being done */ @@ -171,7 +179,7 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev int manager_add_button(Manager *m, const char *name, Button **_button); int manager_add_seat(Manager *m, const char *id, Seat **_seat); int manager_add_session(Manager *m, const char *id, Session **_session); -int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user); +int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **_user); int manager_add_user_by_name(Manager *m, const char *name, User **_user); int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user); int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor); @@ -194,6 +202,10 @@ bool manager_is_docked_or_external_displays(Manager *m); bool manager_is_on_external_power(void); bool manager_all_buttons_ignored(Manager *m); +int manager_read_utmp(Manager *m); +void manager_connect_utmp(Manager *m); +void manager_reconnect_utmp(Manager *m); + extern const sd_bus_vtable manager_vtable[]; #if 0 /// UNNEEDED by elogind @@ -212,13 +224,13 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, HandleAction action, int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; #if 0 /// UNNEEDED by elogind -int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_message *more_properties, sd_bus_error *error, char **job); +int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, const char *requires_mounts_for, sd_bus_message *more_properties, sd_bus_error *error, char **job); int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error); -int manager_unit_is_active(Manager *manager, const char *unit); -int manager_job_is_active(Manager *manager, const char *path); +int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error); +int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error); #endif // 0 /* gperf lookup function */ diff --git a/src/login/meson.build b/src/login/meson.build index 0eac0d3cc..81f39e1ea 100644 --- a/src/login/meson.build +++ b/src/login/meson.build @@ -83,7 +83,6 @@ loginctl_sources = files(''' #if 0 /// UNNEEDED by elogind # user_runtime_dir_sources = files(''' # user-runtime-dir.c -# logind.h # '''.split()) #endif // 0 @@ -125,10 +124,6 @@ loginctl_sources += files(''' install_data('70-power-switch.rules', install_dir : udevrulesdir) - if conf.get('HAVE_ACL') == 1 - install_data('70-uaccess.rules', install_dir : udevrulesdir) - endif - seat_rules = configure_file( input : '71-seat.rules.in', output : '71-seat.rules', @@ -142,6 +137,14 @@ loginctl_sources += files(''' output : '73-seat-late.rules.m4', configuration : substs) #endif // 1 + custom_target( + '70-uaccess.rules', + input : '70-uaccess.rules.m4', + output: '70-uaccess.rules', + command : [meson_apply_m4, config_h, '@INPUT@'], + capture : true, + install : conf.get('HAVE_ACL') == 1, + install_dir : udevrulesdir) custom_target( '73-seat-late.rules', diff --git a/src/login/org.freedesktop.login1.policy b/src/login/org.freedesktop.login1.policy index f1d1f956d..78bee24b0 100644 --- a/src/login/org.freedesktop.login1.policy +++ b/src/login/org.freedesktop.login1.policy @@ -343,7 +343,7 @@ <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> - <allow_active>auth_admin_keep</allow_active> + <allow_active>yes</allow_active> </defaults> </action> diff --git a/src/login/pam_elogind.c b/src/login/pam_elogind.c index 88c5705ef..c4550d35b 100644 --- a/src/login/pam_elogind.c +++ b/src/login/pam_elogind.c @@ -130,7 +130,7 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ r = socket_from_display(display, &p); if (r < 0) return r; - strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1); + strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)); fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); if (fd < 0) @@ -160,40 +160,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ return 0; } -static int export_legacy_dbus_address( - pam_handle_t *handle, - uid_t uid, - const char *runtime) { - - _cleanup_free_ char *s = NULL; - int r = PAM_BUF_ERR; - - /* FIXME: We *really* should move the access() check into the - * daemons that spawn dbus-daemon, instead of forcing - * DBUS_SESSION_BUS_ADDRESS= here. */ - - s = strjoin(runtime, "/bus"); - if (!s) - goto error; - - if (access(s, F_OK) < 0) - return PAM_SUCCESS; - - s = mfree(s); - if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0) - goto error; - - r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0); - if (r != PAM_SUCCESS) - goto error; - - return PAM_SUCCESS; - -error: - pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); - return r; -} - static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) { uint64_t val; int r; @@ -274,6 +240,36 @@ static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, con return 0; } +static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) { + struct stat st; + + assert(path); + + /* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set + * up properly for us. */ + + if (lstat(path, &st) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror(errno)); + goto fail; + } + + if (!S_ISDIR(st.st_mode)) { + pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path); + goto fail; + } + + if (st.st_uid != uid) { + pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid); + goto fail; + } + + return true; + +fail: + pam_syslog(handle, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order."); + return false; +} + _public_ PAM_EXTERN int pam_sm_open_session( pam_handle_t *handle, int flags, @@ -334,16 +330,14 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0) return PAM_BUF_ERR; - r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); - return r; + if (validate_runtime_directory(handle, rt, pw->pw_uid)) { + r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); + return r; + } } - r = export_legacy_dbus_address(handle, pw->pw_uid, rt); - if (r != PAM_SUCCESS) - return r; - return PAM_SUCCESS; } @@ -381,28 +375,32 @@ _public_ PAM_EXTERN int pam_sm_open_session( tty = strempty(tty); if (strchr(tty, ':')) { - /* A tty with a colon is usually an X11 display, - * placed there to show up in utmp. We rearrange - * things and don't pretend that an X display was a - * tty. */ - + /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things + * and don't pretend that an X display was a tty. */ if (isempty(display)) display = tty; tty = NULL; + } else if (streq(tty, "cron")) { - /* cron has been setting PAM_TTY to "cron" for a very - * long time and it probably shouldn't stop doing that - * for compatibility reasons. */ + /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but + * probably because it wants to set it to something as pam_time/pam_access/… require PAM_TTY to be set + * (as they otherwise even try to update it!) — but cron doesn't actually allocate a TTY for its forked + * off processes.) */ type = "unspecified"; class = "background"; tty = NULL; + } else if (streq(tty, "ssh")) { - /* ssh has been setting PAM_TTY to "ssh" for a very - * long time and probably shouldn't stop doing that - * for compatibility reasons. */ + /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further + * details look for "PAM_TTY_KLUDGE" in the openssh sources). */ type ="tty"; class = "user"; - tty = NULL; + tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though usually + * associated with a pty — won't be tracked by their tty in logind. This is because ssh + * does the PAM session registration early for new connections, and registers a pty only + * much later (this is because it doesn't know yet if it needs one at all, as whether to + * register a pty or not is negotiated much later in the protocol). */ + } else /* Chop off leading /dev prefix that some clients specify, but others do not. */ tty = skip_dev_prefix(tty); @@ -472,7 +470,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( r = sd_bus_message_append(m, "uusssssussbss", (uint32_t) pw->pw_uid, - (uint32_t) getpid_cached(), + 0, service, type, class, @@ -561,15 +559,13 @@ _public_ PAM_EXTERN int pam_sm_open_session( * in privileged apps clobbering the runtime directory * unnecessarily. */ - r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); - return r; + if (validate_runtime_directory(handle, runtime_path, pw->pw_uid)) { + r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); + return r; + } } - - r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path); - if (r != PAM_SUCCESS) - return r; } if (!isempty(seat)) { diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c index 3cd5afee3..aff039bc7 100644 --- a/src/login/user-runtime-dir.c +++ b/src/login/user-runtime-dir.c @@ -3,13 +3,16 @@ #include <stdint.h> #include <sys/mount.h> +//#include "sd-bus.h" + +//#include "bus-error.h" #include "fs-util.h" #include "label.h" -//#include "logind.h" #include "mkdir.h" #include "mount-util.h" #include "path-util.h" #include "rm-rf.h" +//#include "selinux-util.h" #include "smack-util.h" #include "stdio-util.h" #include "string-util.h" @@ -19,22 +22,29 @@ #include "user-runtime-dir.h" #if 0 /// UNNEEDED by elogind -static int gather_configuration(size_t *runtime_dir_size) { - Manager m = {}; +static int acquire_runtime_dir_size(uint64_t *ret) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; int r; - manager_reset_config(&m); + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); - r = manager_parse_config_file(&m); + r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', ret); if (r < 0) - log_warning_errno(r, "Failed to parse logind.conf: %m"); + return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r)); - *runtime_dir_size = m.runtime_dir_size; return 0; } #endif // 0 -static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gid, size_t runtime_dir_size) { +static int user_mkdir_runtime_path( + const char *runtime_path, + uid_t uid, + gid_t gid, + uint64_t runtime_dir_size) { + int r; assert(runtime_path); @@ -52,10 +62,10 @@ static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gi char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*") + DECIMAL_STR_MAX(uid_t) + DECIMAL_STR_MAX(gid_t) - + DECIMAL_STR_MAX(size_t)]; + + DECIMAL_STR_MAX(uint64_t)]; xsprintf(options, - "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s", + "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 "%s", uid, gid, runtime_dir_size, mac_smack_use() ? ",smackfsroot=*" : ""); @@ -99,27 +109,42 @@ static int user_remove_runtime_path(const char *runtime_path) { r = rm_rf(runtime_path, 0); if (r < 0) - log_error_errno(r, "Failed to remove runtime directory %s (before unmounting): %m", runtime_path); + log_debug_errno(r, "Failed to remove runtime directory %s (before unmounting), ignoring: %m", runtime_path); - /* Ignore cases where the directory isn't mounted, as that's - * quite possible, if we lacked the permissions to mount - * something */ + /* Ignore cases where the directory isn't mounted, as that's quite possible, if we lacked the permissions to + * mount something */ r = umount2(runtime_path, MNT_DETACH); if (r < 0 && !IN_SET(errno, EINVAL, ENOENT)) - log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", runtime_path); + log_debug_errno(errno, "Failed to unmount user runtime directory %s, ignoring: %m", runtime_path); r = rm_rf(runtime_path, REMOVE_ROOT); - if (r < 0) - log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path); - return r; + return 0; } #if 0 /// having a User instance, elogind can ask its manager directly. -static int do_mount(const char *runtime_path, uid_t uid, gid_t gid) { - size_t runtime_dir_size; +static int do_mount(const char *user) { + char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)]; + uint64_t runtime_dir_size; + uid_t uid; + gid_t gid; + int r; - assert_se(gather_configuration(&runtime_dir_size) == 0); + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) + return log_error_errno(r, + r == -ESRCH ? "No such user \"%s\"" : + r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group" + : "Failed to look up user \"%s\": %m", + user); + + r = acquire_runtime_dir_size(&runtime_dir_size); + if (r < 0) + return r; + + xsprintf(runtime_path, "/run/user/" UID_FMT, uid); #else static int do_mount(const char *runtime_path, size_t runtime_dir_size, uid_t uid, gid_t gid) { #endif // 0 @@ -128,17 +153,35 @@ static int do_mount(const char *runtime_path, size_t runtime_dir_size, uid_t uid return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size); } +#if 0 /// elogind already has the runtime path +static int do_umount(const char *user) { + char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)]; + uid_t uid; + int r; + + /* The user may be already removed. So, first try to parse the string by parse_uid(), + * and if it fails, fallback to get_user_creds().*/ + if (parse_uid(user, &uid) < 0) { + r = get_user_creds(&user, &uid, NULL, NULL, NULL); + if (r < 0) + return log_error_errno(r, + r == -ESRCH ? "No such user \"%s\"" : + r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group" + : "Failed to look up user \"%s\": %m", + user); + } + + xsprintf(runtime_path, "/run/user/" UID_FMT, uid); +#else static int do_umount(const char *runtime_path) { +#endif // 0 + log_debug("Will remove %s", runtime_path); return user_remove_runtime_path(runtime_path); } #if 0 /// elogind does this internally as we have no unit chain being init. int main(int argc, char *argv[]) { - const char *user; - uid_t uid; - gid_t gid; - char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)]; int r; log_parse_environment(); @@ -153,24 +196,18 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - umask(0022); - - user = argv[2]; - r = get_user_creds(&user, &uid, &gid, NULL, NULL); + r = mac_selinux_init(); if (r < 0) { - log_error_errno(r, - r == -ESRCH ? "No such user \"%s\"" : - r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group" - : "Failed to look up user \"%s\": %m", - user); + log_error_errno(r, "Could not initialize labelling: %m\n"); return EXIT_FAILURE; } - xsprintf(runtime_path, "/run/user/" UID_FMT, uid); + + umask(0022); if (streq(argv[1], "start")) - r = do_mount(runtime_path, uid, gid); + r = do_mount(argv[2]); else if (streq(argv[1], "stop")) - r = do_umount(runtime_path); + r = do_umount(argv[2]); else assert_not_reached("Unknown verb!"); |