@@ -67,6 +67,7 @@ obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \
obexd/src/main.c obexd/src/obexd.h \
obexd/src/plugin.h obexd/src/plugin.c \
obexd/src/log.h obexd/src/log.c \
+ obexd/src/logind.h obexd/src/logind.c \
obexd/src/manager.h obexd/src/manager.c \
obexd/src/obex.h obexd/src/obex.c obexd/src/obex-priv.h \
obexd/src/mimetype.h obexd/src/mimetype.c \
@@ -96,6 +97,8 @@ obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \
if EXTERNAL_PLUGINS
obexd_src_obexd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic
+else
+obexd_src_obexd_LDFLAGS =
endif
obexd_src_obexd_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) \
@@ -109,6 +112,13 @@ obexd-add-service-symlink:
obexd-remove-service-symlink:
endif
+if OBEX
+if SYSTEMD
+obexd_src_obexd_CPPFLAGS += -DSYSTEMD
+obexd_src_obexd_LDFLAGS += -lsystemd
+endif
+endif
+
obexd_src_obexd_SHORTNAME = obexd
obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist)
@@ -27,6 +27,7 @@
#include "gdbus/gdbus.h"
#include "obexd/src/log.h"
+#include "obexd/src/logind.h"
#include "obexd/src/obexd.h"
#include "transfer.h"
@@ -1454,7 +1455,7 @@ static struct obc_driver pbap = {
.remove = pbap_remove
};
-int pbap_init(void)
+static int pbap_init_cb(void)
{
int err;
@@ -1481,7 +1482,7 @@ int pbap_init(void)
return 0;
}
-void pbap_exit(void)
+static void pbap_exit_cb(void)
{
DBusMessage *msg;
DBusMessageIter iter;
@@ -1510,3 +1511,12 @@ void pbap_exit(void)
obc_driver_unregister(&pbap);
}
+
+int pbap_init(void)
+{
+ return logind_register(pbap_init_cb, pbap_exit_cb);
+}
+void pbap_exit(void)
+{
+ return logind_unregister(pbap_init_cb, pbap_exit_cb);
+}
@@ -35,6 +35,7 @@
#include "obexd/src/transport.h"
#include "obexd/src/service.h"
#include "obexd/src/log.h"
+#include "obexd/src/logind.h"
#define BT_RX_MTU 32767
#define BT_TX_MTU 32767
@@ -426,7 +427,7 @@ static const struct obex_transport_driver driver = {
static unsigned int listener_id = 0;
-static int bluetooth_init(void)
+static int bluetooth_init_cb(void)
{
connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
if (connection == NULL)
@@ -438,7 +439,7 @@ static int bluetooth_init(void)
return obex_transport_driver_register(&driver);
}
-static void bluetooth_exit(void)
+static void bluetooth_exit_cb(void)
{
DBusMessage *msg;
DBusMessageIter iter;
@@ -476,4 +477,13 @@ static void bluetooth_exit(void)
obex_transport_driver_unregister(&driver);
}
+static int bluetooth_init(void)
+{
+ return logind_register(bluetooth_init_cb, bluetooth_exit_cb);
+}
+static void bluetooth_exit(void)
+{
+ return logind_unregister(bluetooth_init_cb, bluetooth_exit_cb);
+}
+
OBEX_PLUGIN_DEFINE(bluetooth, bluetooth_init, bluetooth_exit)
new file mode 100644
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Enable functionality only when the user is active
+ *
+ * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ */
+
+#ifdef SYSTEMD
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <stddef.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include <systemd/sd-login.h>
+
+#include "obexd/src/log.h"
+#include "obexd/src/logind.h"
+
+sd_login_monitor *monitor;
+int uid;
+gboolean active = FALSE;
+gboolean monitoring_enabled = TRUE;
+
+struct callback_pair
+{
+ login_init_cb init_cb;
+ login_exit_cb exit_cb;
+};
+
+GSList *callbacks = NULL;
+
+static void call_init_cb(gpointer data, gpointer user_data)
+{
+ int res = ((struct callback_pair *)data)->init_cb();
+ if (res)
+ *(int *)user_data = res;
+}
+static void call_exit_cb(gpointer data, gpointer user_data)
+{
+ ((struct callback_pair *)data)->exit_cb();
+}
+
+static int update()
+{
+ char *state;
+ int res;
+ res = sd_login_monitor_flush(monitor);
+ if (res < 0)
+ return res;
+ res = sd_uid_get_state(uid, &state);
+ if (res < 0)
+ return res;
+
+ if (g_strcmp0(state,"active")) {
+ if (!active) return 0;
+ } else {
+ res = sd_uid_get_seats(uid, 1, NULL);
+ if (res < 0)
+ return res;
+ if (active == !!res) return 0;
+ }
+ active ^= TRUE;
+ res = 0;
+ g_slist_foreach(callbacks, active ? call_init_cb : call_exit_cb, &res);
+ return res;
+}
+
+static gboolean event_handler(GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ int res;
+ res = sd_login_monitor_flush(monitor);
+ if (res < 0) {
+ error("sd_login_monitor_flush(): %s", strerror(-res));
+ return FALSE;
+ }
+ if (!monitoring_enabled)
+ return TRUE;
+ res = update();
+ if (res < 0 ) {
+ error("update(): %s", strerror(-res));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean timeout_handler(gpointer user_data)
+{
+ uint64_t timeout_usec;
+ int res;
+
+ if (!event_handler(NULL, 0, NULL))
+ return FALSE;
+
+ res = sd_login_monitor_get_timeout(monitor, &timeout_usec);
+ if (res < 0) {
+ error("sd_login_monitor_get_timeout(): %s", strerror(-res));
+ return FALSE;
+ }
+
+ if (timeout_usec > (uint64_t)-1) {
+ uint64_t time_usec;
+ struct timespec ts;
+ res = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (res < 0) return -errno;
+ time_usec = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+ if (time_usec > timeout_usec) return timeout_handler(user_data);
+ g_timeout_add((timeout_usec - time_usec + 999) / 1000,
+ timeout_handler, user_data);
+ }
+
+ return FALSE;
+}
+
+static int logind_init()
+{
+ GIOChannel *channel;
+ guint source;
+ int events;
+ int fd;
+ int res;
+
+ monitor = NULL;
+
+ DBG("");
+
+ if (!monitoring_enabled)
+ return 0;
+
+ uid = getuid();
+
+ res = sd_login_monitor_new("uid", &monitor);
+ if (res < 0) {
+ monitor = NULL;
+ goto FAIL;
+ }
+
+ // Check this after creating the monitor, in case of race conditions:
+ res = update();
+ if (res < 0)
+ goto FAIL;
+
+ events = res = sd_login_monitor_get_events(monitor);
+ if (res < 0)
+ goto FAIL;
+
+ fd = res = sd_login_monitor_get_fd(monitor);
+ if (res < 0)
+ goto FAIL;
+
+ channel = g_io_channel_unix_new(fd);
+
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ source = g_io_add_watch(channel, events, event_handler, NULL);
+
+ g_io_channel_unref(channel);
+
+ timeout_handler(NULL);
+
+ return 0;
+
+ FAIL:
+ sd_login_monitor_unref(monitor);
+ monitoring_enabled = FALSE;
+ active = TRUE;
+ return res;
+}
+
+static void logind_exit()
+{
+ sd_login_monitor_unref(monitor);
+}
+
+static gint find_cb(gconstpointer a, gconstpointer b)
+{
+ return ((struct callback_pair *)a)->init_cb - (login_init_cb)b;
+}
+
+int logind_register(login_init_cb init_cb, login_exit_cb exit_cb)
+{
+ if (!monitoring_enabled)
+ return init_cb();
+ if (callbacks == NULL)
+ {
+ int res = logind_init();
+ if (res) {
+ error("logind_init(): %s - login detection disabled",
+ strerror(-res));
+ return init_cb();
+ }
+ }
+ struct callback_pair *cbs = g_new(struct callback_pair, 1);
+ cbs->init_cb = init_cb;
+ cbs->exit_cb = exit_cb;
+ callbacks = g_slist_prepend(callbacks, cbs);
+ return active ? init_cb() : 0;
+}
+void logind_unregister(login_init_cb init_cb, login_exit_cb exit_cb)
+{
+ if (!monitoring_enabled)
+ return exit_cb();
+ if (active)
+ exit_cb();
+ GSList *cb_node = g_slist_find_custom(callbacks, init_cb, find_cb);
+ if (cb_node != NULL)
+ callbacks = g_slist_delete_link(callbacks, cb_node);
+ if (callbacks == NULL)
+ logind_exit();
+}
+
+int logind_set(gboolean enabled)
+{
+ if (monitoring_enabled == enabled)
+ return 0;
+ monitoring_enabled = enabled;
+ if (enabled) {
+ active = FALSE;
+ return update();
+ } else {
+ int res = 0;
+ active = TRUE;
+ g_slist_foreach(callbacks, call_exit_cb, &res);
+ return res;
+ }
+}
+
+#endif
new file mode 100644
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ * Enable functionality only when the user is active
+ *
+ * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ */
+
+#ifdef SYSTEMD
+
+typedef int (*login_init_cb)(void);
+typedef void (*login_exit_cb)(void);
+
+int logind_register(login_init_cb init_cb, login_exit_cb exit_cb);
+void logind_unregister(login_init_cb init_cb, login_exit_cb exit_cb);
+int logind_set(gboolean enabled);
+
+#else
+
+#define logind_register(init_cb, exit_cb) init_cb()
+#define logind_unregister(init_cb, exit_cb) exit_cb()
+#define logind_set(enabled) 0
+
+#endif
@@ -35,6 +35,7 @@
#include "../client/manager.h"
#include "log.h"
+#include "logind.h"
#include "obexd.h"
#include "server.h"
@@ -275,6 +276,9 @@ int main(int argc, char *argv[])
__obex_log_init(option_debug, option_detach);
+ if (option_system_bus)
+ logind_set(FALSE);
+
DBG("Entering main loop");
main_loop = g_main_loop_new(NULL, FALSE);
Obexd is usually run as a user service, and can exhibit surprising behaviour if two users are logged in at the same time. Unregister profiles when the user is detected to be off-seat. It may be impossible to detect whether a user is on-seat in some cases. For example, a version of obexd compiled with systemd support might be run outside of a systemd environment. Warn and leave services registered if that happens. Obexd can be run as a system service, in which case this check makes no sense. Disable this check when called with `--system-bus`. Obexd can also be run by a user that does not have an active session. For example, someone could use `ssh` to access the system. There might be a use case where someone needs Bluetooth access but can't log in with a keyboard, or there might be a security issue with doing so. This isn't handled explicitly by this patch, but a future patch could add support by calling `logind_set(FALSE)` in the same way as is currently done with `--system-bus`. Based in large part on the wireplumber code mentioned by Pauli Virtanen: https://gitlab.freedesktop.org/pipewire/wireplumber/-/blob/master/modules/module-logind.c#L52 Other services are likely to need similar functionality, so I have created a gist to demonstrate the basic technique: https://gist.github.com/andrew-sayers/1c4a24f86a9a4c1b1e38d109f1bd1d1e Suggested-by: Pauli Virtanen <pav@iki.fi> Signed-off-by: Andrew Sayers <kernel.org@pileofstuff.org> --- Makefile.obexd | 10 ++ obexd/client/pbap.c | 14 ++- obexd/plugins/bluetooth.c | 14 ++- obexd/src/logind.c | 238 ++++++++++++++++++++++++++++++++++++++ obexd/src/logind.h | 26 +++++ obexd/src/main.c | 4 + 6 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 obexd/src/logind.c create mode 100644 obexd/src/logind.h