libtunnel-manager.la \
libvolume-api.la
+if HAVE_SYSTEMD_LOGIN
+modlibexec_LTLIBRARIES += \
+ liblogind.la
+endif
+
if HAVE_WEBRTC
modlibexec_LTLIBRARIES += libwebrtc-util.la
endif
libcli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
libcli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+liblogind_la_SOURCES = modules/logind/logind.c modules/logind/logind.h
+liblogind_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
+liblogind_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
+
libmain_volume_policy_la_SOURCES = \
modules/main-volume-policy/main-volume-context.c modules/main-volume-policy/main-volume-context.h \
modules/main-volume-policy/main-volume-policy.c modules/main-volume-policy/main-volume-policy.h
module_systemd_login_la_SOURCES = modules/module-systemd-login.c
module_systemd_login_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_systemd_login_la_LIBADD = $(MODULE_LIBADD) $(SYSTEMD_LIBS) $(SYSTEMDLOGIN_LIBS)
+module_systemd_login_la_LIBADD = $(MODULE_LIBADD) $(SYSTEMD_LIBS) $(SYSTEMDLOGIN_LIBS) liblogind.la
module_systemd_login_la_CFLAGS = $(AM_CFLAGS) $(SYSTEMD_CFLAGS) $(SYSTEMDLOGIN_CFLAGS)
# GConf support
--- /dev/null
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2012 Lennart Poettering
+ Copyright 2014 Intel Corporation
+
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "logind.h"
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/dynarray.h>
+#include <pulsecore/shared.h>
+
+static void session_new(pa_logind *logind, const char *id);
+static void session_free(pa_logind_session *session);
+
+static void get_sessions(pa_logind *logind) {
+ int r;
+ char **sessions;
+ pa_hashmap *old_sessions;
+ pa_logind_session *session;
+ void *state;
+ pa_dynarray *new_ids;
+ char *id;
+
+ pa_assert(logind);
+
+ r = sd_uid_get_sessions(getuid(), 0, &sessions);
+ if (r < 0) {
+ pa_log("sd_uid_get_sessions() failed: %s", pa_cstrerror(r));
+ return;
+ }
+
+ /* When we iterate over the new sessions, we drop the encountered sessions
+ * from old_sessions. The sessions that remain in old_sessions in the end
+ * will be removed. */
+ old_sessions = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+ (pa_free_cb_t) session_free);
+ PA_HASHMAP_FOREACH(session, logind->sessions, state)
+ pa_hashmap_put(old_sessions, session->id, session);
+
+ new_ids = pa_dynarray_new(NULL);
+
+ 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++) {
+ session = pa_hashmap_remove(old_sessions, *s);
+ if (session)
+ pa_hashmap_put(logind->sessions, session->id, session);
+ else {
+ /* We don't create the session yet, because creating the
+ * session fires a hook, and we want to postpone firing any
+ * hooks until the sessions hashmap is fully updated. */
+ pa_dynarray_append(new_ids, pa_xstrdup(*s));
+ }
+
+ free(*s);
+ }
+
+ free(sessions);
+ }
+
+ pa_hashmap_free(old_sessions);
+
+ while ((id = pa_dynarray_steal_last(new_ids))) {
+ session_new(logind, id);
+ pa_xfree(id);
+ }
+
+ pa_dynarray_free(new_ids);
+}
+
+static void monitor_cb(pa_mainloop_api *api, pa_io_event* event, int fd, pa_io_event_flags_t events, void *userdata) {
+ pa_logind *logind = userdata;
+
+ pa_assert(logind);
+
+ sd_login_monitor_flush(logind->monitor);
+ get_sessions(logind);
+}
+
+static void set_up_monitor(pa_logind *logind) {
+ int r;
+ sd_login_monitor *monitor = NULL;
+
+ pa_assert(logind);
+ pa_assert(!logind->monitor);
+
+ r = sd_login_monitor_new("session", &monitor);
+ if (r < 0) {
+ pa_log("sd_login_monitor_new() failed: %s", pa_cstrerror(r));
+ return;
+ }
+
+ logind->monitor_event = logind->core->mainloop->io_new(logind->core->mainloop, sd_login_monitor_get_fd(monitor),
+ PA_IO_EVENT_INPUT, monitor_cb, logind);
+}
+
+static void tear_down_monitor(pa_logind *logind) {
+ pa_assert(logind);
+
+ if (logind->monitor_event) {
+ logind->core->mainloop->io_free(logind->monitor_event);
+ logind->monitor_event = NULL;
+ }
+
+ if (logind->monitor) {
+ sd_login_monitor_unref(logind->monitor);
+ logind->monitor = NULL;
+ }
+}
+
+static pa_logind *logind_new(pa_core *core) {
+ pa_logind *logind = NULL;
+ unsigned i;
+
+ pa_assert(core);
+
+ logind = pa_xnew0(pa_logind, 1);
+ logind->core = core;
+ logind->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ logind->refcnt = 1;
+
+ for (i = 0; i < PA_LOGIND_HOOK_MAX; i++)
+ pa_hook_init(&logind->hooks[i], logind);
+
+ /* If we are not actually running logind, then let's do nothing. */
+ if (access("/run/systemd/seats/", F_OK) < 0)
+ goto finish;
+
+ set_up_monitor(logind);
+ get_sessions(logind);
+
+finish:
+ pa_shared_set(core, "logind", logind);
+
+ return logind;
+}
+
+static void logind_free(pa_logind *logind) {
+ unsigned i;
+
+ pa_assert(logind);
+
+ pa_shared_remove(logind->core, "logind");
+
+ if (logind->sessions) {
+ pa_logind_session *session;
+
+ while ((session = pa_hashmap_first(logind->sessions)))
+ session_free(session);
+ }
+
+ tear_down_monitor(logind);
+
+ for (i = 0; i < PA_LOGIND_HOOK_MAX; i++)
+ pa_hook_done(&logind->hooks[i]);
+
+ if (logind->sessions) {
+ pa_assert(pa_hashmap_isempty(logind->sessions));
+ pa_hashmap_free(logind->sessions);
+ }
+
+ pa_xfree(logind);
+}
+
+pa_logind *pa_logind_get(pa_core *core) {
+ pa_logind *logind;
+
+ pa_assert(core);
+
+ logind = pa_shared_get(core, "logind");
+ if (logind) {
+ logind->refcnt++;
+ return logind;
+ }
+
+ return logind_new(core);
+}
+
+void pa_logind_unref(pa_logind *logind) {
+ pa_assert(logind);
+ pa_assert(logind->refcnt > 0);
+
+ logind->refcnt--;
+
+ if (logind->refcnt == 0)
+ logind_free(logind);
+}
+
+static void session_new(pa_logind *logind, const char *id) {
+ pa_logind_session *session;
+
+ pa_assert(logind);
+ pa_assert(id);
+
+ session = pa_xnew0(pa_logind_session, 1);
+ session->logind = logind;
+ session->id = pa_xstrdup(id);
+
+ pa_assert_se(pa_hashmap_put(logind->sessions, session->id, session) >= 0);
+
+ pa_log_debug("Created session %s.", session->id);
+
+ pa_hook_fire(&logind->hooks[PA_LOGIND_HOOK_SESSION_ADDED], session);
+}
+
+static void session_free(pa_logind_session *session) {
+ pa_assert(session);
+
+ pa_log_debug("Freeing session %s.", session->id);
+
+ if (pa_hashmap_remove(session->logind->sessions, session->id))
+ pa_hook_fire(&session->logind->hooks[PA_LOGIND_HOOK_SESSION_REMOVED], session);
+
+ pa_xfree(session->id);
+ pa_xfree(session);
+}
--- /dev/null
+#ifndef foologindhfoo
+#define foologindhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2014 Intel Corporation
+
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/core.h>
+
+#include <systemd/sd-login.h>
+
+typedef struct pa_logind pa_logind;
+typedef struct pa_logind_session pa_logind_session;
+
+enum {
+ 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. */
+struct pa_logind {
+ pa_core *core;
+ pa_hashmap *sessions; /* id -> pa_logind_session */
+ pa_hook hooks[PA_LOGIND_HOOK_MAX];
+
+ unsigned refcnt;
+ sd_login_monitor *monitor;
+ pa_io_event *monitor_event;
+};
+
+pa_logind *pa_logind_get(pa_core *core);
+void pa_logind_unref(pa_logind *logind);
+
+struct pa_logind_session {
+ pa_logind *logind;
+ char *id;
+};
+
+#endif
#include <config.h>
#endif
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/types.h>
+#include "module-systemd-login-symdef.h"
-#include <systemd/sd-login.h>
+#include <modules/logind/logind.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>
-#include "module-systemd-login-symdef.h"
-
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Create a client for each login session of this user");
PA_MODULE_VERSION(PACKAGE_VERSION);
NULL
};
-struct session {
- char *id;
+struct session_client {
+ struct userdata *userdata;
+ pa_logind_session *session;
pa_client *client;
};
+static void session_client_free(struct session_client *client);
+
struct userdata {
pa_module *module;
pa_core *core;
- pa_hashmap *sessions, *previous_sessions;
- sd_login_monitor *monitor;
- pa_io_event *io;
+ pa_logind *logind;
+ pa_hashmap *session_clients; /* pa_logind_session -> struct session_client */
+ pa_hook_slot *session_added_slot;
+ pa_hook_slot *session_removed_slot;
};
-static int add_session(struct userdata *u, const char *id) {
- struct session *session;
+static void session_client_new(struct userdata *u, pa_logind_session *session) {
+ struct session_client *client;
pa_client_new_data data;
- session = pa_xnew(struct session, 1);
- session->id = pa_xstrdup(id);
+ pa_assert(u);
+ pa_assert(session);
+
+ client = pa_xnew0(struct session_client, 1);
+ client->userdata = u;
+ client->session = session;
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_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Login Session %s", session->id);
+ pa_proplist_sets(data.proplist, "systemd-login.session", session->id);
+ client->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);
- return 0;
-}
+ if (!client->client)
+ goto fail;
-static void free_session(struct session *session) {
- pa_assert(session);
+ pa_assert_se(pa_hashmap_put(u->session_clients, client->session, client) >= 0);
- pa_log_debug("Removing session %s", session->id);
+ return;
- pa_client_free(session->client);
- pa_xfree(session->id);
- pa_xfree(session);
+fail:
+ if (client)
+ session_client_free(client);
}
-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 */
+static void session_client_free(struct session_client *client) {
+ pa_assert(client);
- h = u->previous_sessions;
- u->previous_sessions = u->sessions;
- u->sessions = h;
+ pa_hashmap_remove(client->userdata->session_clients, client->session);
- if (sessions) {
- char **s;
+ if (client->client)
+ pa_client_free(client->client);
- /* 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);
+ pa_xfree(client);
+}
- free(*s);
- }
+static pa_hook_result_t session_added_cb(void *hook_data, void *call_data, void *userdata) {
+ pa_logind_session *session = call_data;
+ struct userdata *u = userdata;
- free(sessions);
- }
+ pa_assert(session);
+ pa_assert(u);
- pa_hashmap_remove_all(u->previous_sessions);
+ session_client_new(u, session);
- return 0;
+ return PA_HOOK_OK;
}
-static void monitor_cb(
- pa_mainloop_api*a,
- pa_io_event* e,
- int fd,
- pa_io_event_flags_t events,
- void *userdata) {
-
+static pa_hook_result_t session_removed_cb(void *hook_data, void *call_data, void *userdata) {
+ pa_logind_session *session = call_data;
struct userdata *u = userdata;
+ struct session_client *client;
+ pa_assert(session);
pa_assert(u);
- sd_login_monitor_flush(u->monitor);
- get_session_list(u);
+ client = pa_hashmap_get(u->session_clients, session);
+ if (client)
+ session_client_free(client);
+
+ return PA_HOOK_OK;
}
int pa__init(pa_module *m) {
struct userdata *u = NULL;
pa_modargs *ma;
- sd_login_monitor *monitor = NULL;
- int r;
+ pa_logind_session *session;
+ void *state;
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);
+ u->core = m->core;
+ u->logind = pa_logind_get(m->core);
+ u->session_clients = pa_hashmap_new(NULL, NULL);
+ u->session_added_slot = pa_hook_connect(&u->logind->hooks[PA_LOGIND_HOOK_SESSION_ADDED], PA_HOOK_NORMAL,
+ session_added_cb, u);
+ u->session_removed_slot = pa_hook_connect(&u->logind->hooks[PA_LOGIND_HOOK_SESSION_REMOVED], PA_HOOK_NORMAL,
+ session_removed_cb, u);
- if (get_session_list(u) < 0)
- goto fail;
+ PA_HASHMAP_FOREACH(session, u->logind->sessions, state)
+ session_client_new(u, session);
pa_modargs_free(ma);
if (!u)
return;
- if (u->sessions) {
- pa_hashmap_free(u->sessions);
- pa_hashmap_free(u->previous_sessions);
+ if (u->session_clients) {
+ struct session_client *client;
+
+ while ((client = pa_hashmap_first(u->session_clients)))
+ session_client_free(client);
}
- if (u->io)
- m->core->mainloop->io_free(u->io);
+ if (u->session_removed_slot)
+ pa_hook_slot_free(u->session_removed_slot);
+
+ if (u->session_added_slot)
+ pa_hook_slot_free(u->session_added_slot);
+
+ if (u->session_clients) {
+ pa_assert(pa_hashmap_isempty(u->session_clients));
+ pa_hashmap_free(u->session_clients);
+ }
- if (u->monitor)
- sd_login_monitor_unref(u->monitor);
+ if (u->logind)
+ pa_logind_unref(u->logind);
pa_xfree(u);
}