Hi Andrew, On Tue, Apr 29, 2025 at 3:20 PM Andrew Sayers <kernel.org@xxxxxxxxxxxxxxx> wrote: > > On Tue, Apr 29, 2025 at 01:25:26PM -0400, Luiz Augusto von Dentz wrote: > > Hi Andrew, > > > > On Tue, Apr 29, 2025 at 10:15 AM Andrew Sayers > > <kernel.org@xxxxxxxxxxxxxxx> wrote: > > > > > > 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`. > > > > > > Unregister profiles by closing private connections instead of sending > > > UnregisterProfile on the shared connection. Pipewire has apparently > > > found the latter to cause long shutdown delays, because bluetoothd > > > may be shutting down and unable to handle this message. > > > > > > 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@xxxxxx> > > > Signed-off-by: Andrew Sayers <kernel.org@xxxxxxxxxxxxxxx> > > > --- > > > Makefile.obexd | 10 ++ > > > obexd/client/pbap.c | 17 ++- > > > obexd/plugins/bluetooth.c | 14 ++- > > > obexd/src/logind.c | 239 ++++++++++++++++++++++++++++++++++++++ > > > obexd/src/logind.h | 26 +++++ > > > obexd/src/main.c | 4 + > > > 6 files changed, 305 insertions(+), 5 deletions(-) > > > create mode 100644 obexd/src/logind.c > > > create mode 100644 obexd/src/logind.h > > > > > > diff --git a/Makefile.obexd b/Makefile.obexd > > > index 74dd977a0..b59cfaf8f 100644 > > > --- a/Makefile.obexd > > > +++ b/Makefile.obexd > > > @@ -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) > > > diff --git a/obexd/client/pbap.c b/obexd/client/pbap.c > > > index 90f8bdc02..51b523592 100644 > > > --- a/obexd/client/pbap.c > > > +++ b/obexd/client/pbap.c > > > @@ -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,13 +1455,13 @@ static struct obc_driver pbap = { > > > .remove = pbap_remove > > > }; > > > > > > -int pbap_init(void) > > > +static int pbap_init_cb(void) > > > { > > > int err; > > > > > > DBG(""); > > > > > > - conn = obex_get_dbus_connection(); > > > + conn = obex_setup_dbus_connection_private(NULL, NULL); > > > if (!conn) > > > return -EIO; > > > > > > @@ -1481,7 +1482,7 @@ int pbap_init(void) > > > return 0; > > > } > > > > > > -void pbap_exit(void) > > > +static void pbap_exit_cb(void) > > > { > > > DBG(""); > > > > > > @@ -1496,9 +1497,19 @@ void pbap_exit(void) > > > } > > > > > > if (conn) { > > > + dbus_connection_close(conn); > > > dbus_connection_unref(conn); > > > conn = NULL; > > > } > > > > > > 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); > > > +} > > > diff --git a/obexd/plugins/bluetooth.c b/obexd/plugins/bluetooth.c > > > index 8cf718922..7ff27a8a8 100644 > > > --- a/obexd/plugins/bluetooth.c > > > +++ b/obexd/plugins/bluetooth.c > > > @@ -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) > > > { > > > GSList *l; > > > > > > @@ -462,4 +463,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) > > > diff --git a/obexd/src/logind.c b/obexd/src/logind.c > > > new file mode 100644 > > > index 000000000..ff2bf3219 > > > --- /dev/null > > > +++ b/obexd/src/logind.c > > > @@ -0,0 +1,239 @@ > > > +// SPDX-License-Identifier: GPL-2.0-or-later > > > +/* > > > + * > > > + * Enable functionality only when the user is active > > > + * > > > + * Copyright (C) 2007-2010 Marcel Holtmann <marcel@xxxxxxxxxxxx> > > > + * > > > + * > > > + */ > > > + > > > +#ifdef SYSTEMD > > > + > > > +#include <assert.h> > > > +#include <errno.h> > > > +#include <poll.h> > > > +#include <stddef.h> > > > +#include <stdlib.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" > > > + > > > +static sd_login_monitor * monitor; > > > +static int uid; > > > +static gboolean active = FALSE; > > > +static gboolean monitoring_enabled = TRUE; > > > +static guint event_source; > > > + > > > +struct callback_pair { > > > + logind_init_cb init_cb; > > > + logind_exit_cb exit_cb; > > > +}; > > > + > > > +GSList *callbacks; > > > + > > > +static void call_init_cb(gpointer data, gpointer user_data) > > > +{ > > > + int res; > > > + > > > + 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(void) > > > +{ > > > + char *state = NULL; > > > + gboolean state_is_active; > > > + int res; > > > + > > > + res = sd_login_monitor_flush(monitor); > > > + if (res < 0) > > > + return res; > > > + res = sd_uid_get_state(uid, &state); > > > + state_is_active = g_strcmp0(state, "active"); > > > + free(state); > > > + if (res < 0) > > > + return res; > > > + > > > + if (state_is_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 int check_event(void) > > > +{ > > > + int res; > > > + > > > + res = sd_login_monitor_flush(monitor); > > > + if (res < 0) > > > + return res; > > > + if (!monitoring_enabled) > > > + return 0; > > > + res = update(); > > > + if (res < 0) > > > + return res; > > > + > > > + return 0; > > > +} > > > + > > > + > > > +static gboolean event_handler(GIOChannel *source, GIOCondition condition, > > > + gpointer data) > > > +{ > > > + int res; > > > + > > > + res = check_event(); > > > + if (res) { > > > + error("%s: %s", __func__, strerror(-res)); > > > + return FALSE; > > > + } > > > + > > > + return TRUE; > > > +} > > > + > > > +static int logind_init(void) > > > +{ > > > + GIOChannel *channel; > > > + 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); > > > + > > > + event_source = g_io_add_watch(channel, events, event_handler, NULL); > > > + > > > + g_io_channel_unref(channel); > > > + > > > + return check_event(); > > > + > > > +FAIL: > > > + sd_login_monitor_unref(monitor); > > > + monitoring_enabled = FALSE; > > > + active = TRUE; > > > + return res; > > > +} > > > + > > > +static void logind_exit(void) > > > +{ > > > + if (event_source) { > > > + g_source_remove(event_source); > > > + event_source = 0; > > > + } > > > + sd_login_monitor_unref(monitor); > > > +} > > > + > > > +static gint find_cb(gconstpointer a, gconstpointer b) > > > +{ > > > + return ((struct callback_pair *)a)->init_cb - (logind_init_cb)b; > > > +} > > > + > > > +int logind_register(logind_init_cb init_cb, logind_exit_cb exit_cb) > > > +{ > > > + struct callback_pair *cbs; > > > + > > > + if (!monitoring_enabled) > > > + return init_cb(); > > > + if (callbacks == NULL) { > > > + int res; > > > + > > > + res = logind_init(); > > > + if (res) { > > > + error("logind_init(): %s - login detection disabled", > > > + strerror(-res)); > > > + return init_cb(); > > > + } > > > + } > > > + 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(logind_init_cb init_cb, logind_exit_cb exit_cb) > > > +{ > > > + GSList *cb_node; > > > + > > > + if (!monitoring_enabled) > > > + return exit_cb(); > > > + if (active) > > > + exit_cb(); > > > + 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) > > > +{ > > > + int res = 0; > > > + > > > + if (monitoring_enabled == enabled) > > > + return 0; > > > + > > > + monitoring_enabled = enabled; > > > + if (enabled) { > > > + active = FALSE; > > > + return update(); > > > + } > > > + > > > + active = TRUE; > > > + g_slist_foreach(callbacks, call_exit_cb, &res); > > > + return res; > > > +} > > > + > > > +#endif > > > diff --git a/obexd/src/logind.h b/obexd/src/logind.h > > > new file mode 100644 > > > index 000000000..1a92a8b87 > > > --- /dev/null > > > +++ b/obexd/src/logind.h > > > @@ -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@xxxxxxxxxxxx> > > > + * > > > + * > > > + */ > > > + > > > +#ifdef SYSTEMD > > > + > > > +typedef int (*logind_init_cb)(void); > > > +typedef void (*logind_exit_cb)(void); > > > + > > > +int logind_register(logind_init_cb init_cb, logind_exit_cb exit_cb); > > > +void logind_unregister(logind_init_cb init_cb, logind_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 > > > diff --git a/obexd/src/main.c b/obexd/src/main.c > > > index ca95a70de..df150973e 100644 > > > --- a/obexd/src/main.c > > > +++ b/obexd/src/main.c > > > @@ -35,6 +35,7 @@ > > > #include "../client/manager.h" > > > > > > #include "log.h" > > > +#include "logind.h" > > > #include "obexd.h" > > > #include "server.h" > > > > > > @@ -283,6 +284,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); > > > -- > > > 2.49.0 > > > > Applying: obexd: Unregister profiles when the user is inactive > > WARNING:MACRO_ARG_UNUSED: Argument 'exit_cb' is not used in function-like macro > > #409: FILE: obexd/src/logind.h:22: > > +#define logind_register(init_cb, exit_cb) init_cb() > > > > WARNING:MACRO_ARG_UNUSED: Argument 'init_cb' is not used in function-like macro > > #410: FILE: obexd/src/logind.h:23: > > +#define logind_unregister(init_cb, exit_cb) exit_cb() > > > > WARNING:MACRO_ARG_UNUSED: Argument 'enabled' is not used in function-like macro > > #411: FILE: obexd/src/logind.h:24: > > +#define logind_set(enabled) 0 > > Those #define's are dummy implementations for use when systemd is disabled. > Which of these would you rather silence the warnings with? > > // inline functions: > static inline int logind_set(gboolean enabled) { return 0; } I think inline is probably better, but I don't have a strong opinion about the usage of the options below. > // comma operator: > #define logind_set(enabled) (enabled,0) > > // varargs: > #define login_set(...) 0 > > > > > -- > > Luiz Augusto von Dentz > > -- Luiz Augusto von Dentz