diff options
Diffstat (limited to 'src/modules/module-systemd-login.c')
-rw-r--r-- | src/modules/module-systemd-login.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/modules/module-systemd-login.c b/src/modules/module-systemd-login.c new file mode 100644 index 0000000..5140157 --- /dev/null +++ b/src/modules/module-systemd-login.c @@ -0,0 +1,243 @@ +/*** + This file is part of PulseAudio. + + Copyright 2012 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <systemd/sd-login.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/module.h> +#include <pulsecore/log.h> +#include <pulsecore/hashmap.h> +#include <pulsecore/idxset.h> +#include <pulsecore/modargs.h> + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Create a client for each login session of this user"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(true); + +static const char* const valid_modargs[] = { + NULL +}; + +struct session { + char *id; + pa_client *client; +}; + +struct userdata { + pa_module *module; + pa_core *core; + pa_hashmap *sessions, *previous_sessions; + sd_login_monitor *monitor; + pa_io_event *io; +}; + +static int add_session(struct userdata *u, const char *id) { + struct session *session; + pa_client_new_data data; + + session = pa_xnew(struct session, 1); + session->id = pa_xstrdup(id); + + pa_client_new_data_init(&data); + data.module = u->module; + data.driver = __FILE__; + pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Login Session %s", id); + pa_proplist_sets(data.proplist, "systemd-login.session", id); + session->client = pa_client_new(u->core, &data); + pa_client_new_data_done(&data); + + if (!session->client) { + pa_xfree(session->id); + pa_xfree(session); + return -1; + } + + pa_hashmap_put(u->sessions, session->id, session); + + pa_log_debug("Added new session %s", id); + + /* Positive exit_idle_time is only useful when we have no session tracking + * capability, so we can set it to 0 now that we have detected a session. + * The benefit of setting exit_idle_time to 0 is that pulseaudio will exit + * immediately when the session ends. That in turn is useful, because some + * systems (those that use pam_systemd but don't use systemd for managing + * pulseaudio) clean $XDG_RUNTIME_DIR on logout, but fail to terminate all + * services that depend on the files in $XDG_RUNTIME_DIR. The directory + * contains our sockets, and if the sockets are removed without terminating + * pulseaudio, a quick relogin will likely cause trouble, because a new + * instance will be spawned while the old instance is still running. */ + if (u->core->exit_idle_time > 0) + pa_core_set_exit_idle_time(u->core, 0); + + return 0; +} + +static void free_session(struct session *session) { + pa_assert(session); + + pa_log_debug("Removing session %s", session->id); + + pa_client_free(session->client); + pa_xfree(session->id); + pa_xfree(session); +} + +static int get_session_list(struct userdata *u) { + int r; + char **sessions; + pa_hashmap *h; + struct session *o; + + pa_assert(u); + + r = sd_uid_get_sessions(getuid(), 0, &sessions); + if (r < 0) + return -1; + + /* We copy all sessions that still exist from one hashmap to the + * other and then flush the remaining ones */ + + h = u->previous_sessions; + u->previous_sessions = u->sessions; + u->sessions = h; + + if (sessions) { + char **s; + + /* Note that the sessions array is allocated with libc's + * malloc()/free() calls, hence do not use pa_xfree() to free + * this here. */ + + for (s = sessions; *s; s++) { + o = pa_hashmap_remove(u->previous_sessions, *s); + if (o) + pa_hashmap_put(u->sessions, o->id, o); + else + add_session(u, *s); + + free(*s); + } + + free(sessions); + } + + pa_hashmap_remove_all(u->previous_sessions); + + return 0; +} + +static void monitor_cb( + pa_mainloop_api*a, + pa_io_event* e, + int fd, + pa_io_event_flags_t events, + void *userdata) { + + struct userdata *u = userdata; + + pa_assert(u); + + sd_login_monitor_flush(u->monitor); + get_session_list(u); +} + +int pa__init(pa_module *m) { + struct userdata *u = NULL; + pa_modargs *ma; + sd_login_monitor *monitor = NULL; + int r; + + pa_assert(m); + + /* If we are not actually running logind become a NOP */ + if (access("/run/systemd/seats/", F_OK) < 0) + return 0; + + ma = pa_modargs_new(m->argument, valid_modargs); + if (!ma) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + r = sd_login_monitor_new("session", &monitor); + if (r < 0) { + pa_log("Failed to create session monitor: %s", strerror(-r)); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->sessions = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) free_session); + u->previous_sessions = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) free_session); + u->monitor = monitor; + + u->io = u->core->mainloop->io_new(u->core->mainloop, sd_login_monitor_get_fd(monitor), PA_IO_EVENT_INPUT, monitor_cb, u); + + if (get_session_list(u) < 0) + goto fail; + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +void pa__done(pa_module *m) { + struct userdata *u; + + pa_assert(m); + + u = m->userdata; + if (!u) + return; + + if (u->sessions) { + pa_hashmap_free(u->sessions); + pa_hashmap_free(u->previous_sessions); + } + + if (u->io) + m->core->mainloop->io_free(u->io); + + if (u->monitor) + sd_login_monitor_unref(u->monitor); + + pa_xfree(u); +} |