diff options
author | Mark Hindley <mark@hindley.org.uk> | 2018-11-12 09:22:09 +0000 |
---|---|---|
committer | Mark Hindley <mark@hindley.org.uk> | 2018-11-12 09:22:09 +0000 |
commit | a7bee29ae34de50a86bf64d4ea9f69bdbc3b27e4 (patch) | |
tree | ac6c9abfefdc4286c4f52167738c4e4d35c96cd4 /src/login/logind-session.c | |
parent | 98f0f1042d219289b880436fad42823433648d18 (diff) | |
parent | 2cc17d30309a1db16cccbf376a59ae40e47b6959 (diff) |
Merge branch 'merge_upstream' into debian_WIP
Diffstat (limited to 'src/login/logind-session.c')
-rw-r--r-- | src/login/logind-session.c | 252 |
1 files changed, 184 insertions, 68 deletions
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); |