When running a user PulseAudio instance on top of the system instance, seat tracking will be useful for filtering out devices in the system instance that belong to some other user. --- src/modules/logind/logind.c | 107 ++++++++++++++++++++++++++++++++++++++++++++ src/modules/logind/logind.h | 13 +++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/modules/logind/logind.c b/src/modules/logind/logind.c index 1b6923f..99e6eff 100644 --- a/src/modules/logind/logind.c +++ b/src/modules/logind/logind.c @@ -30,9 +30,71 @@ #include <pulsecore/dynarray.h> #include <pulsecore/shared.h> +static void seat_new(pa_logind *logind, const char *id); +static void seat_free(pa_logind_seat *seat); + static void session_new(pa_logind *logind, const char *id); static void session_free(pa_logind_session *session); +static void get_seats(pa_logind *logind) { + int r; + char **seats; + pa_hashmap *old_seats; + pa_logind_seat *seat; + void *state; + pa_dynarray *new_ids; + char *id; + + pa_assert(logind); + + r = sd_uid_get_seats(getuid(), 0, &seats); + if (r < 0) { + pa_log("sd_uid_get_seats() failed: %s", pa_cstrerror(r)); + return; + } + + /* When we iterate over the new seats, we drop the encountered seats from + * old_seats. The seats that remain in old_seats in the end will be + * removed. */ + old_seats = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) seat_free); + PA_HASHMAP_FOREACH(seat, logind->seats, state) + pa_hashmap_put(old_seats, seat->id, seat); + + new_ids = pa_dynarray_new(NULL); + + if (seats) { + char **s; + + /* Note that the seats array is allocated with libc's malloc()/free() + * calls, hence do not use pa_xfree() to free this here. */ + + for (s = seats; *s; s++) { + seat = pa_hashmap_remove(old_seats, *s); + if (seat) + pa_hashmap_put(logind->seats, seat->id, seat); + else { + /* We don't create the seat yet, because creating the seat + * fires a hook, and we want to postpone firing any hooks until + * the seats hashmap is fully updated. */ + pa_dynarray_append(new_ids, pa_xstrdup(*s)); + } + + free(*s); + } + + free(seats); + } + + pa_hashmap_free(old_seats); + + while ((id = pa_dynarray_steal_last(new_ids))) { + seat_new(logind, id); + pa_xfree(id); + } + + pa_dynarray_free(new_ids); +} + static void get_sessions(pa_logind *logind) { int r; char **sessions; @@ -100,6 +162,7 @@ static void monitor_cb(pa_mainloop_api *api, pa_io_event* event, int fd, pa_io_e pa_assert(logind); sd_login_monitor_flush(logind->monitor); + get_seats(logind); get_sessions(logind); } @@ -115,6 +178,7 @@ static void set_up_monitor(pa_logind *logind) { pa_log("sd_login_monitor_new() failed: %s", pa_cstrerror(r)); return; } + logind->monitor = monitor; logind->monitor_event = logind->core->mainloop->io_new(logind->core->mainloop, sd_login_monitor_get_fd(monitor), PA_IO_EVENT_INPUT, monitor_cb, logind); @@ -142,6 +206,7 @@ static pa_logind *logind_new(pa_core *core) { logind = pa_xnew0(pa_logind, 1); logind->core = core; + logind->seats = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); logind->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); logind->refcnt = 1; @@ -153,6 +218,7 @@ static pa_logind *logind_new(pa_core *core) { goto finish; set_up_monitor(logind); + get_seats(logind); get_sessions(logind); finish: @@ -175,6 +241,13 @@ static void logind_free(pa_logind *logind) { session_free(session); } + if (logind->seats) { + pa_logind_seat *seat; + + while ((seat = pa_hashmap_first(logind->seats))) + seat_free(seat); + } + tear_down_monitor(logind); for (i = 0; i < PA_LOGIND_HOOK_MAX; i++) @@ -185,6 +258,11 @@ static void logind_free(pa_logind *logind) { pa_hashmap_free(logind->sessions); } + if (logind->seats) { + pa_assert(pa_hashmap_isempty(logind->seats)); + pa_hashmap_free(logind->seats); + } + pa_xfree(logind); } @@ -212,6 +290,35 @@ void pa_logind_unref(pa_logind *logind) { logind_free(logind); } +static void seat_new(pa_logind *logind, const char *id) { + pa_logind_seat *seat; + + pa_assert(logind); + pa_assert(id); + + seat = pa_xnew0(pa_logind_seat, 1); + seat->logind = logind; + seat->id = pa_xstrdup(id); + + pa_assert_se(pa_hashmap_put(logind->seats, seat->id, seat) >= 0); + + pa_log_debug("Created seat %s.", seat->id); + + pa_hook_fire(&logind->hooks[PA_LOGIND_HOOK_SEAT_ADDED], seat); +} + +static void seat_free(pa_logind_seat *seat) { + pa_assert(seat); + + pa_log_debug("Freeing seat %s.", seat->id); + + if (pa_hashmap_remove(seat->logind->seats, seat->id)) + pa_hook_fire(&seat->logind->hooks[PA_LOGIND_HOOK_SEAT_REMOVED], seat); + + pa_xfree(seat->id); + pa_xfree(seat); +} + static void session_new(pa_logind *logind, const char *id) { pa_logind_session *session; diff --git a/src/modules/logind/logind.h b/src/modules/logind/logind.h index 49ef9cf..a3f6f32 100644 --- a/src/modules/logind/logind.h +++ b/src/modules/logind/logind.h @@ -27,18 +27,22 @@ #include <systemd/sd-login.h> typedef struct pa_logind pa_logind; +typedef struct pa_logind_seat pa_logind_seat; typedef struct pa_logind_session pa_logind_session; enum { + PA_LOGIND_HOOK_SEAT_ADDED, + PA_LOGIND_HOOK_SEAT_REMOVED, PA_LOGIND_HOOK_SESSION_ADDED, PA_LOGIND_HOOK_SESSION_REMOVED, PA_LOGIND_HOOK_MAX, }; -/* Currently pa_logind doesn't track all sessions in the system, only those - * that belong to the current user. */ +/* Currently pa_logind doesn't track all seats and sessions in the system, only + * those that belong to the current user. */ struct pa_logind { pa_core *core; + pa_hashmap *seats; /* id -> pa_logind_seat */ pa_hashmap *sessions; /* id -> pa_logind_session */ pa_hook hooks[PA_LOGIND_HOOK_MAX]; @@ -50,6 +54,11 @@ struct pa_logind { pa_logind *pa_logind_get(pa_core *core); void pa_logind_unref(pa_logind *logind); +struct pa_logind_seat { + pa_logind *logind; + char *id; +}; + struct pa_logind_session { pa_logind *logind; char *id; -- 1.9.3