[pulseaudio-discuss] [PATCH v3 08/11] logind: Move the code that interfaces with logind to liblogind.so

Tanu Kaskinen tanu.kaskinen at linux.intel.com
Thu Dec 4 10:44:51 PST 2014


This doesn't change the behaviour in any important way. The purpose of
this move is to provide access to logind from multiple modules without
needing to use the logind API from more than one place. Currently
there's only one module using liblogind.so, but module-tunnel-manager
will need seat tracking, so that will be another module that will need
to access logind.
---
 src/Makefile.am                    |  11 +-
 src/modules/logind/logind.c        | 242 +++++++++++++++++++++++++++++++++++++
 src/modules/logind/logind.h        |  58 +++++++++
 src/modules/module-systemd-login.c | 193 ++++++++++++-----------------
 4 files changed, 390 insertions(+), 114 deletions(-)
 create mode 100644 src/modules/logind/logind.c
 create mode 100644 src/modules/logind/logind.h

diff --git a/src/Makefile.am b/src/Makefile.am
index e6a34bb..3297d5f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1053,6 +1053,11 @@ modlibexec_LTLIBRARIES = \
 		libprotocol-native.la \
 		libtunnel-manager.la
 
+if HAVE_SYSTEMD_LOGIN
+modlibexec_LTLIBRARIES += \
+		liblogind.la
+endif
+
 if HAVE_WEBRTC
 modlibexec_LTLIBRARIES += libwebrtc-util.la
 endif
@@ -1081,6 +1086,10 @@ libcli_la_SOURCES = pulsecore/cli.c pulsecore/cli.h
 libcli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
 libcli_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINOR@.la libpulsecommon- at 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- at PA_MAJORMINOR@.la libpulsecommon- at PA_MAJORMINOR@.la libpulse.la
+
 libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h
 libprotocol_cli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
 libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINOR@.la libpulsecommon- at PA_MAJORMINOR@.la libpulse.la libcli.la
@@ -2102,7 +2111,7 @@ module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 
 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
diff --git a/src/modules/logind/logind.c b/src/modules/logind/logind.c
new file mode 100644
index 0000000..1b6923f
--- /dev/null
+++ b/src/modules/logind/logind.c
@@ -0,0 +1,242 @@
+/***
+  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);
+}
diff --git a/src/modules/logind/logind.h b/src/modules/logind/logind.h
new file mode 100644
index 0000000..49ef9cf
--- /dev/null
+++ b/src/modules/logind/logind.h
@@ -0,0 +1,58 @@
+#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
diff --git a/src/modules/module-systemd-login.c b/src/modules/module-systemd-login.c
index 8cd5fb0..5e4b2b4 100644
--- a/src/modules/module-systemd-login.c
+++ b/src/modules/module-systemd-login.c
@@ -23,25 +23,14 @@
 #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);
@@ -51,150 +40,118 @@ static const char* const valid_modargs[] = {
     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);
 
@@ -218,16 +175,26 @@ void pa__done(pa_module *m) {
     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);
 }
-- 
1.9.3



More information about the pulseaudio-discuss mailing list