diff options
Diffstat (limited to 'src/login/logind-session.c')
-rw-r--r-- | src/login/logind-session.c | 452 |
1 files changed, 253 insertions, 199 deletions
diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 3988e55..4713aa0 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -15,6 +15,7 @@ #include "audit-util.h" #include "bus-error.h" #include "bus-util.h" +#include "daemon-util.h" #include "devnum-util.h" #include "env-file.h" #include "escape.h" @@ -37,7 +38,7 @@ #include "strv.h" #include "terminal-util.h" #include "tmpfile-util.h" -#include "uid-alloc-range.h" +#include "uid-classification.h" #include "user-util.h" #define RELEASE_USEC (20*USEC_PER_SEC) @@ -45,13 +46,13 @@ static void session_remove_fifo(Session *s); static void session_restore_vt(Session *s); -int session_new(Session **ret, Manager *m, const char *id) { +int session_new(Manager *m, const char *id, Session **ret) { _cleanup_(session_freep) Session *s = NULL; int r; - assert(ret); assert(m); assert(id); + assert(ret); if (!session_id_valid(id)) return -EINVAL; @@ -62,19 +63,17 @@ int session_new(Session **ret, Manager *m, const char *id) { *s = (Session) { .manager = m, + .id = strdup(id), + .state_file = path_join("/run/systemd/sessions/", id), .fifo_fd = -EBADF, .vtfd = -EBADF, .audit_id = AUDIT_SESSION_INVALID, .tty_validity = _TTY_VALIDITY_INVALID, .leader = PIDREF_NULL, }; - - s->state_file = path_join("/run/systemd/sessions", id); - if (!s->state_file) + if (!s->id || !s->state_file) return -ENOMEM; - s->id = basename(s->state_file); - s->devices = hashmap_new(&devt_hash_ops); if (!s->devices) return -ENOMEM; @@ -87,12 +86,53 @@ int session_new(Session **ret, Manager *m, const char *id) { return 0; } -static void session_reset_leader(Session *s) { +static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + Session *s = ASSERT_PTR(userdata); + + assert(s->leader.fd == fd); + session_stop(s, /* force= */ false); + + return 1; +} + +static int session_watch_pidfd(Session *s) { + int r; + + assert(s); + assert(s->manager); + assert(pidref_is_set(&s->leader)); + + if (s->leader.fd < 0) + return 0; + + r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s); + if (r < 0) + return r; + + r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT); + if (r < 0) + return r; + + (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd"); + + return 0; +} + +static void session_reset_leader(Session *s, bool keep_fdstore) { assert(s); + if (!keep_fdstore) { + /* Clear fdstore if we're asked to, no matter if s->leader is set or not, so that when + * initially deserializing leader fd we clear the old fd too. */ + (void) notify_remove_fd_warnf("session-%s-leader-fd", s->id); + s->leader_fd_saved = false; + } + if (!pidref_is_set(&s->leader)) return; + s->leader_pidfd_event_source = sd_event_source_disable_unref(s->leader_pidfd_event_source); + (void) hashmap_remove_value(s->manager->sessions_by_leader, &s->leader, s); return pidref_done(&s->leader); @@ -104,10 +144,12 @@ Session* session_free(Session *s) { if (!s) return NULL; + sd_event_source_unref(s->stop_on_idle_event_source); + if (s->in_gc_queue) LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s); - s->timer_event_source = sd_event_source_unref(s->timer_event_source); + sd_event_source_unref(s->timer_event_source); session_drop_controller(s); @@ -142,9 +184,10 @@ Session* session_free(Session *s) { free(s->scope_job); - session_reset_leader(s); + session_reset_leader(s, /* keep_fdstore = */ true); sd_bus_message_unref(s->create_message); + sd_bus_message_unref(s->upgrade_message); free(s->tty); free(s->display); @@ -160,10 +203,9 @@ Session* session_free(Session *s) { /* Note that we remove neither the state file nor the fifo path here, since we want both to survive * daemon restarts */ - free(s->state_file); free(s->fifo_path); - - sd_event_source_unref(s->stop_on_idle_event_source); + free(s->state_file); + free(s->id); return mfree(s); } @@ -188,15 +230,27 @@ int session_set_leader_consume(Session *s, PidRef _leader) { if (pidref_equal(&s->leader, &pidref)) return 0; - session_reset_leader(s); + session_reset_leader(s, /* keep_fdstore = */ false); s->leader = TAKE_PIDREF(pidref); + r = session_watch_pidfd(s); + if (r < 0) + return log_error_errno(r, "Failed to watch leader pidfd for session '%s': %m", s->id); + r = hashmap_ensure_put(&s->manager->sessions_by_leader, &pidref_hash_ops, &s->leader, s); if (r < 0) return r; assert(r > 0); + if (s->leader.fd >= 0) { + r = notify_push_fdf(s->leader.fd, "session-%s-leader-fd", s->id); + if (r < 0) + log_warning_errno(r, "Failed to push leader pidfd for session '%s', ignoring: %m", s->id); + else + s->leader_fd_saved = true; + } + (void) audit_session_from_pid(s->leader.pid, &s->audit_id); return 1; @@ -240,16 +294,18 @@ int session_save(Session *s) { "# This is private data. Do not parse.\n" "UID="UID_FMT"\n" "USER=%s\n" - "ACTIVE=%i\n" - "IS_DISPLAY=%i\n" + "ACTIVE=%s\n" + "IS_DISPLAY=%s\n" "STATE=%s\n" - "REMOTE=%i\n", + "REMOTE=%s\n" + "LEADER_FD_SAVED=%s\n", s->user->user_record->uid, s->user->user_record->user_name, - session_is_active(s), - s->user->display == s, + one_zero(session_is_active(s)), + one_zero(s->user->display == s), session_state_to_string(session_get_state(s)), - s->remote); + one_zero(s->remote), + one_zero(s->leader_fd_saved)); if (s->type >= 0) fprintf(f, "TYPE=%s\n", session_type_to_string(s->type)); @@ -377,33 +433,27 @@ static int session_load_devices(Session *s, const char *devices) { for (const char *p = devices;;) { _cleanup_free_ char *word = NULL; - SessionDevice *sd; dev_t dev; int k; k = extract_first_word(&p, &word, NULL, 0); - if (k == 0) - break; - if (k < 0) { - r = k; + if (k <= 0) { + RET_GATHER(r, k); break; } k = parse_devnum(word, &dev); if (k < 0) { - r = k; + RET_GATHER(r, k); continue; } /* The file descriptors for loaded devices will be reattached later. */ - k = session_device_new(s, dev, false, &sd); - if (k < 0) - r = k; + RET_GATHER(r, session_device_new(s, dev, /* open_device = */ false, /* ret = */ NULL)); } if (r < 0) - log_error_errno(r, "Loading session devices for session %s failed: %m", s->id); - + log_error_errno(r, "Failed to load some session devices for session '%s': %m", s->id); return r; } @@ -414,7 +464,8 @@ int session_load(Session *s) { *vtnr = NULL, *state = NULL, *position = NULL, - *leader = NULL, + *leader_pid = NULL, + *leader_fd_saved = NULL, *type = NULL, *original_type = NULL, *class = NULL, @@ -431,32 +482,33 @@ int session_load(Session *s) { assert(s); r = parse_env_file(NULL, s->state_file, - "REMOTE", &remote, - "SCOPE", &s->scope, - "SCOPE_JOB", &s->scope_job, - "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, - "SERVICE", &s->service, - "DESKTOP", &s->desktop, - "VTNR", &vtnr, - "STATE", &state, - "POSITION", &position, - "LEADER", &leader, - "TYPE", &type, - "ORIGINAL_TYPE", &original_type, - "CLASS", &class, - "UID", &uid, - "REALTIME", &realtime, - "MONOTONIC", &monotonic, - "CONTROLLER", &controller, - "ACTIVE", &active, - "DEVICES", &devices, - "IS_DISPLAY", &is_display); + "REMOTE", &remote, + "SCOPE", &s->scope, + "SCOPE_JOB", &s->scope_job, + "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, + "SERVICE", &s->service, + "DESKTOP", &s->desktop, + "VTNR", &vtnr, + "STATE", &state, + "POSITION", &position, + "LEADER", &leader_pid, + "LEADER_FD_SAVED", &leader_fd_saved, + "TYPE", &type, + "ORIGINAL_TYPE", &original_type, + "CLASS", &class, + "UID", &uid, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + "CONTROLLER", &controller, + "ACTIVE", &active, + "DEVICES", &devices, + "IS_DISPLAY", &is_display); if (r < 0) return log_error_errno(r, "Failed to read %s: %m", s->state_file); @@ -523,19 +575,6 @@ int session_load(Session *s) { s->tty_validity = v; } - if (leader) { - _cleanup_(pidref_done) PidRef p = PIDREF_NULL; - - r = pidref_set_pidstr(&p, leader); - if (r < 0) - log_debug_errno(r, "Failed to parse leader PID of session: %s", leader); - else { - r = session_set_leader_consume(s, TAKE_PIDREF(p)); - if (r < 0) - log_warning_errno(r, "Failed to set session leader PID, ignoring: %m"); - } - } - if (type) { SessionType t; @@ -610,6 +649,33 @@ int session_load(Session *s) { session_restore_vt(s); } + if (leader_pid) { + assert(!pidref_is_set(&s->leader)); + + r = parse_pid(leader_pid, &s->deserialized_pid); + if (r < 0) + return log_error_errno(r, "Failed to parse LEADER=%s: %m", leader_pid); + + if (leader_fd_saved) { + r = parse_boolean(leader_fd_saved); + if (r < 0) + return log_error_errno(r, "Failed to parse LEADER_FD_SAVED=%s: %m", leader_fd_saved); + s->leader_fd_saved = r > 0; + + if (s->leader_fd_saved) + /* The leader fd will be acquired from fdstore later */ + return 0; + } + + _cleanup_(pidref_done) PidRef p = PIDREF_NULL; + + r = pidref_set_pid(&p, s->deserialized_pid); + if (r >= 0) + r = session_set_leader_consume(s, TAKE_PIDREF(p)); + if (r < 0) + log_warning_errno(r, "Failed to set leader PID for session '%s': %m", s->id); + } + return r; } @@ -651,60 +717,56 @@ int session_activate(Session *s) { } static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) { + _cleanup_free_ char *scope = NULL; + const char *description; int r; assert(s); assert(s->user); - if (!s->scope) { - _cleanup_strv_free_ char **after = 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(); - - description = strjoina("Session ", s->id, " of User ", s->user->user_record->user_name); - - /* We usually want to order session scopes after systemd-user-sessions.service since the - * latter unit is used as login session barrier for unprivileged users. However the barrier - * doesn't apply for root as sysadmin should always be able to log in (and without waiting - * for any timeout to expire) in case something goes wrong during the boot process. Since - * ordering after systemd-user-sessions.service and the user instance is optional we make use - * of STRV_IGNORE with strv_new() to skip these order constraints when needed. */ - after = strv_new("systemd-logind.service", - s->user->runtime_dir_service, - !uid_is_system(s->user->user_record->uid) ? "systemd-user-sessions.service" : STRV_IGNORE, - s->user->service); - if (!after) - return log_oom(); - - r = manager_start_scope( - s->manager, - scope, - &s->leader, - s->user->slice, - description, - /* These two have StopWhenUnneeded= set, hence add a dep towards them */ - STRV_MAKE(s->user->runtime_dir_service, - s->user->service), - after, - user_record_home_directory(s->user->user_record), - properties, - 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)); + if (!SESSION_CLASS_WANTS_SCOPE(s->class)) + return 0; - s->scope = TAKE_PTR(scope); - } + if (s->scope) + goto finish; - (void) hashmap_put(s->manager->session_units, s->scope, s); + s->scope_job = mfree(s->scope_job); + scope = strjoin("session-", s->id, ".scope"); + if (!scope) + return log_oom(); + + description = strjoina("Session ", s->id, " of User ", s->user->user_record->user_name); + + r = manager_start_scope( + s->manager, + scope, + &s->leader, + /* allow_pidfd = */ true, + s->user->slice, + description, + /* These should have been pulled in explicitly in user_start(). Just to be sure. */ + STRV_MAKE_CONST(s->user->runtime_dir_unit, + SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) ? s->user->service_manager_unit : NULL), + /* We usually want to order session scopes after systemd-user-sessions.service + * since the unit is used as login session barrier for unprivileged users. However + * the barrier doesn't apply for root as sysadmin should always be able to log in + * (and without waiting for any timeout to expire) in case something goes wrong + * during the boot process. */ + STRV_MAKE_CONST("systemd-logind.service", + SESSION_CLASS_IS_EARLY(s->class) ? NULL : "systemd-user-sessions.service"), + user_record_home_directory(s->user->user_record), + properties, + 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)); + + s->scope = TAKE_PTR(scope); + +finish: + (void) hashmap_put(s->manager->session_units, s->scope, s); return 0; } @@ -744,7 +806,7 @@ static int session_setup_stop_on_idle_timer(Session *s) { assert(s); - if (s->manager->stop_idle_session_usec == USEC_INFINITY) + if (s->manager->stop_idle_session_usec == USEC_INFINITY || !SESSION_CLASS_CAN_STOP_ON_IDLE(s->class)) return 0; r = sd_event_add_time_relative( @@ -804,17 +866,17 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) { user_elect_display(s->user); /* Save data */ - session_save(s); - user_save(s->user); + (void) session_save(s); + (void) user_save(s->user); if (s->seat) - seat_save(s->seat); + (void) seat_save(s->seat); /* Send signals */ session_send_signal(s, true); user_send_changed(s->user, "Display", NULL); if (s->seat && s->seat->active == s) - seat_send_changed(s->seat, "ActiveSession", NULL); + (void) seat_send_changed(s->seat, "ActiveSession", NULL); return 0; } @@ -900,8 +962,8 @@ int session_stop(Session *s, bool force) { user_elect_display(s->user); - session_save(s); - user_save(s->user); + (void) session_save(s); + (void) user_save(s->user); return r; } @@ -923,7 +985,6 @@ int session_finalize(Session *s) { LOG_MESSAGE("Removed session %s.", s->id)); s->timer_event_source = sd_event_source_unref(s->timer_event_source); - s->leader_pidfd_event_source = sd_event_source_unref(s->leader_pidfd_event_source); if (s->seat) seat_evict_position(s->seat, s); @@ -948,10 +1009,10 @@ int session_finalize(Session *s) { seat_save(s->seat); } - session_reset_leader(s); + session_reset_leader(s, /* keep_fdstore = */ false); - user_save(s->user); - user_send_changed(s->user, "Display", NULL); + (void) user_save(s->user); + (void) user_send_changed(s->user, "Display", NULL); return 0; } @@ -1042,19 +1103,21 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) { return s->idle_hint; } - /* For sessions with an explicitly configured tty, let's check its atime */ - if (s->tty) { - r = get_tty_atime(s->tty, &atime); - if (r >= 0) - goto found_atime; - } + if (s->type == SESSION_TTY) { + /* For sessions with an explicitly configured tty, let's check its atime */ + if (s->tty) { + r = get_tty_atime(s->tty, &atime); + if (r >= 0) + goto found_atime; + } - /* For sessions with a leader but no explicitly configured tty, let's check the controlling tty of - * the leader */ - if (pidref_is_set(&s->leader)) { - r = get_process_ctty_atime(s->leader.pid, &atime); - if (r >= 0) - goto found_atime; + /* For sessions with a leader but no explicitly configured tty, let's check the controlling tty of + * the leader */ + if (pidref_is_set(&s->leader)) { + r = get_process_ctty_atime(s->leader.pid, &atime); + if (r >= 0) + goto found_atime; + } } if (t) @@ -1081,7 +1144,9 @@ found_atime: int session_set_idle_hint(Session *s, bool b) { assert(s); - if (!SESSION_TYPE_IS_GRAPHICAL(s->type)) + if (!SESSION_CLASS_CAN_IDLE(s->class)) /* Only some session classes know the idle concept at all */ + return -ENOTTY; + if (!SESSION_TYPE_IS_GRAPHICAL(s->type)) /* And only graphical session types can set the field explicitly */ return -ENOTTY; if (s->idle_hint == b) @@ -1107,15 +1172,20 @@ int session_get_locked_hint(Session *s) { return s->locked_hint; } -void session_set_locked_hint(Session *s, bool b) { +int session_set_locked_hint(Session *s, bool b) { assert(s); + if (!SESSION_CLASS_CAN_LOCK(s->class)) + return -ENOTTY; + if (s->locked_hint == b) - return; + return 0; s->locked_hint = b; + (void) session_save(s); + (void) session_send_changed(s, "LockedHint", NULL); - session_send_changed(s, "LockedHint", NULL); + return 1; } void session_set_type(Session *s, SessionType t) { @@ -1125,9 +1195,22 @@ void session_set_type(Session *s, SessionType t) { return; s->type = t; - session_save(s); + (void) session_save(s); + (void) session_send_changed(s, "Type", NULL); +} + +void session_set_class(Session *s, SessionClass c) { + assert(s); + + if (s->class == c) + return; - session_send_changed(s, "Type", NULL); + s->class = c; + (void) session_save(s); + (void) session_send_changed(s, "Class", NULL); + + /* This class change might mean we need the per-user session manager now. Try to start it. */ + (void) user_start_service_manager(s->user); } int session_set_display(Session *s, const char *display) { @@ -1140,9 +1223,8 @@ int session_set_display(Session *s, const char *display) { if (r <= 0) /* 0 means the strings were equal */ return r; - session_save(s); - - session_send_changed(s, "Display", NULL); + (void) session_save(s); + (void) session_send_changed(s, "Display", NULL); return 1; } @@ -1157,9 +1239,8 @@ int session_set_tty(Session *s, const char *tty) { if (r <= 0) /* 0 means the strings were equal */ return r; - session_save(s); - - session_send_changed(s, "TTY", NULL); + (void) session_save(s); + (void) session_send_changed(s, "TTY", NULL); return 1; } @@ -1224,41 +1305,7 @@ static void session_remove_fifo(Session *s) { s->fifo_event_source = sd_event_source_unref(s->fifo_event_source); s->fifo_fd = safe_close(s->fifo_fd); - - if (s->fifo_path) { - (void) unlink(s->fifo_path); - s->fifo_path = mfree(s->fifo_path); - } -} - -static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - Session *s = ASSERT_PTR(userdata); - - assert(s->leader.fd == fd); - session_stop(s, /* force= */ false); - - return 1; -} - -int session_watch_pidfd(Session *s) { - int r; - - assert(s); - - if (s->leader.fd < 0) - return 0; - - r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s); - if (r < 0) - return r; - - r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT); - if (r < 0) - return r; - - (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd"); - - return 0; + s->fifo_path = unlink_and_free(s->fifo_path); } bool session_may_gc(Session *s, bool drop_not_started) { @@ -1273,15 +1320,17 @@ bool session_may_gc(Session *s, bool drop_not_started) { return true; r = pidref_is_alive(&s->leader); + if (r == -ESRCH) + /* Session has no leader. This is probably because the leader vanished before deserializing + * pidfd from FD store. */ + return true; if (r < 0) - log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not.", s->leader.pid); + log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not: %m", s->leader.pid); if (r > 0) return false; - if (s->fifo_fd >= 0) { - if (pipe_eof(s->fifo_fd) <= 0) - return false; - } + if (s->fifo_fd >= 0 && pipe_eof(s->fifo_fd) <= 0) + return false; if (s->scope_job) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1561,7 +1610,7 @@ int session_set_controller(Session *s, const char *sender, bool force, bool prep session_release_controller(s, true); s->controller = TAKE_PTR(name); - session_save(s); + (void) session_save(s); return 0; } @@ -1575,7 +1624,7 @@ void session_drop_controller(Session *s) { s->track = sd_bus_track_unref(s->track); session_set_type(s, s->original_type); session_release_controller(s, false); - session_save(s); + (void) session_save(s); session_restore_vt(s); } @@ -1600,10 +1649,15 @@ static const char* const session_type_table[_SESSION_TYPE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); static const char* const session_class_table[_SESSION_CLASS_MAX] = { - [SESSION_USER] = "user", - [SESSION_GREETER] = "greeter", - [SESSION_LOCK_SCREEN] = "lock-screen", - [SESSION_BACKGROUND] = "background", + [SESSION_USER] = "user", + [SESSION_USER_EARLY] = "user-early", + [SESSION_USER_INCOMPLETE] = "user-incomplete", + [SESSION_GREETER] = "greeter", + [SESSION_LOCK_SCREEN] = "lock-screen", + [SESSION_BACKGROUND] = "background", + [SESSION_BACKGROUND_LIGHT] = "background-light", + [SESSION_MANAGER] = "manager", + [SESSION_MANAGER_EARLY] = "manager-early", }; DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass); |