summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-09-23 12:49:20 +0200
committerThomas Haller <thaller@redhat.com>2019-09-23 12:49:20 +0200
commite79f1b623d4154631f66d8bc6cca151a1d840b4e (patch)
tree5fd7a28f36359ad9c659aa04adeac15a86559b5c
parentd19a403faa480b20f9ca6c9ce9e66a015b16cf46 (diff)
parent4154d9618c904c2286b332b56f3515806cb1bb3b (diff)
downloadNetworkManager-e79f1b623d4154631f66d8bc6cca151a1d840b4e.tar.gz
bluez: merge branch 'th/bluez-rework-2'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/251
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am67
-rw-r--r--libnm-core/nm-core-internal.h4
-rw-r--r--libnm-core/nm-utils.c8
-rw-r--r--man/nmcli-examples.xml22
-rw-r--r--po/POTFILES.in2
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.c192
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.h100
-rw-r--r--shared/nm-glib-aux/nm-macros-internal.h6
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c38
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h10
-rw-r--r--shared/nm-std-aux/nm-dbus-compat.h2
-rw-r--r--src/NetworkManagerUtils.c32
-rw-r--r--src/NetworkManagerUtils.h34
-rw-r--r--src/devices/bluetooth/meson.build2
-rw-r--r--src/devices/bluetooth/nm-bluez-common.h1
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c1207
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h70
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c2835
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.h39
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.c910
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.h41
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c584
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.h27
-rw-r--r--src/devices/bluetooth/nm-device-bt.c880
-rw-r--r--src/devices/bluetooth/nm-device-bt.h34
-rw-r--r--src/devices/bluetooth/tests/nm-bt-test.c220
-rw-r--r--src/devices/nm-device-bridge.c98
-rw-r--r--src/devices/nm-device-bridge.h3
-rw-r--r--src/devices/nm-device-factory.c22
-rw-r--r--src/devices/nm-device-factory.h35
-rw-r--r--src/devices/nm-device-ppp.c2
-rw-r--r--src/devices/nm-device-wpan.c2
-rw-r--r--src/devices/nm-device.c52
-rw-r--r--src/devices/nm-device.h33
-rw-r--r--src/devices/ovs/nm-ovs-factory.c6
-rw-r--r--src/devices/team/nm-team-factory.c2
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c2
-rw-r--r--src/devices/wifi/nm-iwd-manager.c2
-rw-r--r--src/devices/wwan/libnm-wwan.ver6
-rw-r--r--src/devices/wwan/nm-device-modem.c11
-rw-r--r--src/devices/wwan/nm-modem-manager.c14
-rw-r--r--src/devices/wwan/nm-modem-manager.h3
-rw-r--r--src/devices/wwan/nm-modem.c64
-rw-r--r--src/devices/wwan/nm-modem.h8
-rw-r--r--src/devices/wwan/nm-wwan-factory.c14
-rw-r--r--src/nm-dbus-manager.c2
-rw-r--r--src/nm-manager.c32
-rw-r--r--src/nm-manager.h3
-rw-r--r--src/nm-policy.c2
-rw-r--r--src/ppp/nm-ppp-manager.c4
-rw-r--r--src/settings/nm-secret-agent.c2
-rw-r--r--src/settings/nm-settings.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c2
54 files changed, 4911 insertions, 2889 deletions
diff --git a/.gitignore b/.gitignore
index 55e8317e31..afa51c3ec2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -206,6 +206,7 @@ test-*.trs
/src/NetworkManager
/src/NetworkManager.ver
+/src/devices/bluetooth/tests/nm-bt-test
/src/devices/tests/test-acd
/src/devices/tests/test-lldp
/src/devices/wifi/tests/test-devices-wifi
diff --git a/Makefile.am b/Makefile.am
index 48e0cd55df..55ad993927 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3408,18 +3408,41 @@ EXTRA_DIST += \
if WITH_MODEM_MANAGER_1
+noinst_LTLIBRARIES += src/devices/bluetooth/libnm-bluetooth-utils.la
+
+src_devices_bluetooth_libnm_bluetooth_utils_la_SOURCES = \
+ src/devices/bluetooth/nm-bluez-common.h \
+ src/devices/bluetooth/nm-bt-error.c \
+ src/devices/bluetooth/nm-bt-error.h \
+ $(NULL)
+
+src_devices_bluetooth_libnm_bluetooth_utils_la_CPPFLAGS = \
+ $(src_cppflags_base) \
+ $(NULL)
+
+src_devices_bluetooth_libnm_bluetooth_utils_la_LIBADD = \
+ $(GLIB_LIBS) \
+ $(NULL)
+
+if WITH_BLUEZ5_DUN
+src_devices_bluetooth_libnm_bluetooth_utils_la_SOURCES += \
+ src/devices/bluetooth/nm-bluez5-dun.c \
+ src/devices/bluetooth/nm-bluez5-dun.h \
+ $(NULL)
+
+src_devices_bluetooth_libnm_bluetooth_utils_la_CPPFLAGS += $(BLUEZ5_CFLAGS)
+src_devices_bluetooth_libnm_bluetooth_utils_la_LIBADD += $(BLUEZ5_LIBS)
+endif
+
+$(src_devices_bluetooth_libnm_bluetooth_utils_la_OBJECTS): $(libnm_core_lib_h_pub_mkenums)
+
+###############################################################################
+
core_plugins += src/devices/bluetooth/libnm-device-plugin-bluetooth.la
src_devices_bluetooth_libnm_device_plugin_bluetooth_la_SOURCES = \
src/devices/bluetooth/nm-bluez-manager.c \
- src/devices/bluetooth/nm-bluez-common.h \
- src/devices/bluetooth/nm-bluez-device.c \
- src/devices/bluetooth/nm-bluez-device.h \
- src/devices/bluetooth/nm-bluez5-manager.c \
- src/devices/bluetooth/nm-bluez5-manager.h \
- src/devices/bluetooth/nm-bt-error.h \
- src/devices/bluetooth/nm-bt-error.c \
- \
+ src/devices/bluetooth/nm-bluez-manager.h \
src/devices/bluetooth/nm-device-bt.c \
src/devices/bluetooth/nm-device-bt.h \
$(NULL)
@@ -3431,18 +3454,30 @@ src_devices_bluetooth_libnm_device_plugin_bluetooth_la_LDFLAGS = \
-Wl,--version-script="$(srcdir)/linker-script-devices.ver"
src_devices_bluetooth_libnm_device_plugin_bluetooth_la_LIBADD = \
+ src/devices/bluetooth/libnm-bluetooth-utils.la \
src/devices/wwan/libnm-wwan.la \
- $(GLIB_LIBS)
+ $(GLIB_LIBS) \
+ $(NULL)
-if WITH_BLUEZ5_DUN
-src_devices_bluetooth_libnm_device_plugin_bluetooth_la_CPPFLAGS += $(BLUEZ5_CFLAGS)
+$(src_devices_bluetooth_libnm_device_plugin_bluetooth_la_OBJECTS): $(libnm_core_lib_h_pub_mkenums)
-src_devices_bluetooth_libnm_device_plugin_bluetooth_la_SOURCES += \
- src/devices/bluetooth/nm-bluez5-dun.c \
- src/devices/bluetooth/nm-bluez5-dun.h
+###############################################################################
-src_devices_bluetooth_libnm_device_plugin_bluetooth_la_LIBADD += $(BLUEZ5_LIBS)
-endif
+check_programs_norun += \
+ src/devices/bluetooth/tests/nm-bt-test
+
+src_devices_bluetooth_tests_nm_bt_test_CPPFLAGS = \
+ $(src_cppflags_test) \
+ $(NULL)
+src_devices_bluetooth_tests_nm_bt_test_LDADD = \
+ src/devices/bluetooth/libnm-bluetooth-utils.la \
+ src/libNetworkManager.la \
+ $(GLIB_LIBS) \
+ $(NULL)
+
+$(src_devices_bluetooth_tests_nm_bt_test_OBJECTS): $(libnm_core_lib_h_pub_mkenums)
+
+###############################################################################
check-local-devices-bluetooth: src/devices/bluetooth/libnm-device-plugin-bluetooth.la
$(srcdir)/tools/check-exports.sh $(builddir)/src/devices/bluetooth/.libs/libnm-device-plugin-bluetooth.so "$(srcdir)/linker-script-devices.ver"
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 37523e2564..9f4f19839f 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -131,6 +131,10 @@ _nm_setting_secret_flags_valid (NMSettingSecretFlags flags)
/*****************************************************************************/
+const char *nm_bluetooth_capability_to_string (NMBluetoothCapabilities capabilities, char *buf, gsize len);
+
+/*****************************************************************************/
+
typedef enum { /*< skip >*/
NM_SETTING_PARSE_FLAGS_NONE = 0,
NM_SETTING_PARSE_FLAGS_STRICT = 1LL << 0,
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 5fa0020fa3..ae48b16af4 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -5812,6 +5812,14 @@ nm_utils_version (void)
/*****************************************************************************/
+NM_UTILS_FLAGS2STR_DEFINE (nm_bluetooth_capability_to_string, NMBluetoothCapabilities,
+ NM_UTILS_FLAGS2STR (NM_BT_CAPABILITY_NONE, "NONE"),
+ NM_UTILS_FLAGS2STR (NM_BT_CAPABILITY_DUN, "DUN"),
+ NM_UTILS_FLAGS2STR (NM_BT_CAPABILITY_NAP, "NAP"),
+)
+
+/*****************************************************************************/
+
/**
* nm_utils_base64secret_decode:
* @base64_key: the (possibly invalid) base64 encode key.
diff --git a/man/nmcli-examples.xml b/man/nmcli-examples.xml
index e777c326d1..53e003fb11 100644
--- a/man/nmcli-examples.xml
+++ b/man/nmcli-examples.xml
@@ -587,6 +587,28 @@ Connection 'ethernet-4' (de89cdeb-a3e1-4d53-8fa0-c22546c775f4) successfully
</para>
</example>
+ <example><title>Bluetooth connection profiles</title>
+ <para>NetworkManger supports both connecting to NAP and DUN devices as a client. It also
+ supports sharing the network via a NAP server.
+ </para>
+ <para>For NAP client connections, NetworkManager automatically creates a suitable in-memory profile
+ for paired devices if none is available. You may use that generated profile directly, but you may also modify
+ and persist it, which will prevent to automatically re-create it. You may also create a profile from scratch.
+ For example, the following uses DHCP and IPv6 autoconf for address configuration:
+ </para>
+<screen><prompt>$ </prompt><userinput>nmcli connection add type bluetooth con-name "Profile for My Bluetooth Device (NAP)" autoconnect no bluetooth.type panu bluetooth.bdaddr "$BDADDR"</userinput></screen>
+ <para>For DUN connections, the user needs to configure modem settings and hence no profile
+ gets created automatically. The modem settings depend on your device and you either need
+ a "gsm" or a "csma" section. For example,
+ </para>
+<screen><prompt>$ </prompt><userinput>nmcli connection add type bluetooth con-name "Profile for My Bluetooth Device (DUN)" autoconnect no bluetooth.type dun bluetooth.bdaddr "$BDADDR" gsm.apn apn.com</userinput></screen>
+ <para>Finally, you can create a bluetooth hotspot. BlueZ implements those as a bridge device,
+ so such profiles also have a bridge section. Also, you probably want to set IP methods as "shared",
+ so that clients get automatic IP addressing. Note that the "shared" IPv4 method requires dnsmasq to be available.
+ </para>
+<screen><prompt>$ </prompt><userinput>nmcli connection add type bluetooth con-name "My Bluetooth Hotspot" autoconnect no ifname btnap0 bluetooth.type nap ipv4.method shared ipv6.method shared</userinput></screen>
+ </example>
+
</refsect1>
<refsect1>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 90ed9ebdbe..d3246bccfb 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -148,7 +148,7 @@ src/dhcp/nm-dhcp-dhclient-utils.c
src/dhcp/nm-dhcp-manager.c
src/dns/nm-dns-manager.c
src/devices/adsl/nm-device-adsl.c
-src/devices/bluetooth/nm-bluez-device.c
+src/devices/bluetooth/nm-bluez-manager.c
src/devices/bluetooth/nm-device-bt.c
src/devices/nm-device-6lowpan.c
src/devices/nm-device-bond.c
diff --git a/shared/nm-glib-aux/nm-dbus-aux.c b/shared/nm-glib-aux/nm-dbus-aux.c
index 4b8c629e2c..51db326a75 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.c
+++ b/shared/nm-glib-aux/nm-dbus-aux.c
@@ -53,3 +53,195 @@ nm_dbus_connection_call_get_name_owner (GDBusConnection *dbus_connection,
_nm_dbus_connection_call_get_name_owner_cb,
nm_utils_user_data_pack (user_data, callback));
}
+
+/*****************************************************************************/
+
+static void
+_nm_dbus_connection_call_get_all_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gs_unref_variant GVariant *ret = NULL;
+ gs_free_error GError *error = NULL;
+ gpointer orig_user_data;
+ NMDBusConnectionCallDefaultCb callback;
+
+ nm_utils_user_data_unpack (user_data, &orig_user_data, &callback);
+
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+
+ nm_assert ((!!ret) != (!!error));
+
+ callback (ret, error, orig_user_data);
+}
+
+void
+nm_dbus_connection_call_get_all (GDBusConnection *dbus_connection,
+ const char *bus_name,
+ const char *object_path,
+ const char *interface_name,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMDBusConnectionCallDefaultCb callback,
+ gpointer user_data)
+{
+ nm_assert (callback);
+
+ g_dbus_connection_call (dbus_connection,
+ bus_name,
+ object_path,
+ DBUS_INTERFACE_PROPERTIES,
+ "GetAll",
+ g_variant_new ("(s)", interface_name),
+ G_VARIANT_TYPE ("(a{sv})"),
+ G_DBUS_CALL_FLAGS_NONE,
+ timeout_msec,
+ cancellable,
+ _nm_dbus_connection_call_get_all_cb,
+ nm_utils_user_data_pack (user_data, callback));
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ NMDBusConnectionSignalObjectMangerCb callback;
+ gpointer user_data;
+ GDestroyNotify user_data_free_func;
+} SubscribeObjectManagerData;
+
+static void
+_subscribe_object_manager_cb (GDBusConnection *connection,
+ const char *sender_name,
+ const char *arg_object_path,
+ const char *interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ const SubscribeObjectManagerData *d = user_data;
+
+ nm_assert (nm_streq0 (interface_name, DBUS_INTERFACE_OBJECT_MANAGER));
+
+ if (nm_streq (signal_name, "InterfacesAdded")) {
+ gs_unref_variant GVariant *interfaces_and_properties = NULL;
+ const char *object_path;
+
+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oa{sa{sv}})")))
+ return;
+
+ g_variant_get (parameters,
+ "(&o@a{sa{sv}})",
+ &object_path,
+ &interfaces_and_properties);
+
+ d->callback (object_path, interfaces_and_properties, NULL, d->user_data);
+ return;
+ }
+
+ if (nm_streq (signal_name, "InterfacesRemoved")) {
+ gs_free const char **interfaces = NULL;
+ const char *object_path;
+
+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oas)")))
+ return;
+
+ g_variant_get (parameters,
+ "(&o^a&s)",
+ &object_path,
+ &interfaces);
+
+ d->callback (object_path, NULL, interfaces, d->user_data);
+ return;
+ }
+}
+
+static void
+_subscribe_object_manager_data_free (gpointer ptr)
+{
+ SubscribeObjectManagerData *d = ptr;
+
+ if (d->user_data_free_func)
+ d->user_data_free_func (d->user_data);
+ nm_g_slice_free (d);
+}
+
+guint
+nm_dbus_connection_signal_subscribe_object_manager (GDBusConnection *dbus_connection,
+ const char *service_name,
+ const char *object_path,
+ NMDBusConnectionSignalObjectMangerCb callback,
+ gpointer user_data,
+ GDestroyNotify user_data_free_func)
+{
+ SubscribeObjectManagerData *d;
+
+ g_return_val_if_fail (callback, 0);
+
+ d = g_slice_new (SubscribeObjectManagerData);
+ *d = (SubscribeObjectManagerData) {
+ .callback = callback,
+ .user_data = user_data,
+ .user_data_free_func = user_data_free_func,
+ };
+
+ return nm_dbus_connection_signal_subscribe_object_manager_plain (dbus_connection,
+ service_name,
+ object_path,
+ NULL,
+ _subscribe_object_manager_cb,
+ d,
+ _subscribe_object_manager_data_free);
+}
+
+/*****************************************************************************/
+
+static void
+_nm_dbus_connection_call_get_managed_objects_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gs_unref_variant GVariant *ret = NULL;
+ gs_unref_variant GVariant *arg = NULL;
+ gs_free_error GError *error = NULL;
+ gpointer orig_user_data;
+ NMDBusConnectionCallDefaultCb callback;
+
+ nm_utils_user_data_unpack (user_data, &orig_user_data, &callback);
+
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+
+ nm_assert ((!!ret) != (!!error));
+
+ if (ret) {
+ nm_assert (g_variant_is_of_type (ret, G_VARIANT_TYPE ("(a{oa{sa{sv}}})")));
+ arg = g_variant_get_child_value (ret, 0);
+ }
+
+ callback (arg, error, orig_user_data);
+}
+
+void
+nm_dbus_connection_call_get_managed_objects (GDBusConnection *dbus_connection,
+ const char *bus_name,
+ const char *object_path,
+ GDBusCallFlags flags,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMDBusConnectionCallDefaultCb callback,
+ gpointer user_data)
+{
+ nm_assert (callback);
+
+ g_dbus_connection_call (dbus_connection,
+ bus_name,
+ object_path,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ "GetManagedObjects",
+ NULL,
+ G_VARIANT_TYPE ("(a{oa{sa{sv}}})"),
+ flags,
+ timeout_msec,
+ cancellable,
+ _nm_dbus_connection_call_get_managed_objects_cb,
+ nm_utils_user_data_pack (user_data, callback));
+}
diff --git a/shared/nm-glib-aux/nm-dbus-aux.h b/shared/nm-glib-aux/nm-dbus-aux.h
index 6200da023e..d5e3bf4780 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.h
+++ b/shared/nm-glib-aux/nm-dbus-aux.h
@@ -28,6 +28,12 @@ nm_clear_g_dbus_connection_signal (GDBusConnection *dbus_connection,
/*****************************************************************************/
+typedef void (*NMDBusConnectionCallDefaultCb) (GVariant *result,
+ GError *error,
+ gpointer user_data);
+
+/*****************************************************************************/
+
static inline void
nm_dbus_connection_call_start_service_by_name (GDBusConnection *dbus_connection,
const char *name,
@@ -77,11 +83,95 @@ typedef void (*NMDBusConnectionCallGetNameOwnerCb) (const char *name_owner,
gpointer user_data);
void nm_dbus_connection_call_get_name_owner (GDBusConnection *dbus_connection,
- const char *service_name,
- int timeout_msec,
- GCancellable *cancellable,
- NMDBusConnectionCallGetNameOwnerCb callback,
- gpointer user_data);
+ const char *service_name,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMDBusConnectionCallGetNameOwnerCb callback,
+ gpointer user_data);
+
+static inline guint
+nm_dbus_connection_signal_subscribe_properties_changed (GDBusConnection *dbus_connection,
+ const char *bus_name,
+ const char *object_path,
+ const char *interface_name,
+ GDBusSignalCallback callback,
+ gpointer user_data,
+ GDestroyNotify user_data_free_func)
+
+{
+ nm_assert (bus_name);
+
+ /* it seems that using a non-unique name causes problems that we get signals
+ * also from unrelated senders. Usually, you are anyway monitoring the name-owner,
+ * so you should have the unique name at hand.
+ *
+ * If not, investigate this, ensure that it works, and lift this restriction. */
+ nm_assert (g_dbus_is_unique_name (bus_name));
+
+ return g_dbus_connection_signal_subscribe (dbus_connection,
+ bus_name,
+ DBUS_INTERFACE_PROPERTIES,
+ "PropertiesChanged",
+ object_path,
+ interface_name,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ callback,
+ user_data,
+ user_data_free_func);
+}
+
+void nm_dbus_connection_call_get_all (GDBusConnection *dbus_connection,
+ const char *bus_name,
+ const char *object_path,
+ const char *interface_name,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMDBusConnectionCallDefaultCb callback,
+ gpointer user_data);
+
+/*****************************************************************************/
+
+static inline guint
+nm_dbus_connection_signal_subscribe_object_manager_plain (GDBusConnection *dbus_connection,
+ const char *service_name,
+ const char *object_path,
+ const char *signal_name,
+ GDBusSignalCallback callback,
+ gpointer user_data,
+ GDestroyNotify user_data_free_func)
+{
+ return g_dbus_connection_signal_subscribe (dbus_connection,
+ service_name,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ signal_name,
+ object_path,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ callback,
+ user_data,
+ user_data_free_func);
+}
+
+typedef void (*NMDBusConnectionSignalObjectMangerCb) (const char *object_path,
+ GVariant *added_interfaces_and_properties,
+ const char *const*removed_interfaces,
+ gpointer user_data);
+
+guint nm_dbus_connection_signal_subscribe_object_manager (GDBusConnection *dbus_connection,
+ const char *service_name,
+ const char *object_path,
+ NMDBusConnectionSignalObjectMangerCb callback,
+ gpointer user_data,
+ GDestroyNotify user_data_free_func);
+
+void nm_dbus_connection_call_get_managed_objects (GDBusConnection *dbus_connection,
+ const char *bus_name,
+ const char *object_path,
+ GDBusCallFlags flags,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMDBusConnectionCallDefaultCb callback,
+ gpointer user_data);
/*****************************************************************************/
diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h
index 5c3b06717e..9292f3699d 100644
--- a/shared/nm-glib-aux/nm-macros-internal.h
+++ b/shared/nm-glib-aux/nm-macros-internal.h
@@ -310,6 +310,12 @@ _nm_auto_protect_errno (int *p_saved_errno)
NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_unref_gsource, g_source_unref);
#define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource)
+NM_AUTO_DEFINE_FCN0 (guint, _nm_auto_remove_source, g_source_remove);
+#define nm_auto_remove_source nm_auto(_nm_auto_remove_source)
+
+NM_AUTO_DEFINE_FCN0 (GIOChannel *, _nm_auto_unref_io_channel, g_io_channel_unref)
+#define nm_auto_unref_io_channel nm_auto(_nm_auto_unref_io_channel)
+
NM_AUTO_DEFINE_FCN0 (GMainLoop *, _nm_auto_unref_gmainloop, g_main_loop_unref);
#define nm_auto_unref_gmainloop nm_auto(_nm_auto_unref_gmainloop)
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index f03c66ddae..43cab16b2c 100644
--- a/shared/nm-glib-aux/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -2288,6 +2288,44 @@ nm_utils_hash_keys_to_array (GHashTable *hash,
return keys;
}
+gpointer *
+nm_utils_hash_values_to_array (GHashTable *hash,
+ GCompareDataFunc compare_func,
+ gpointer user_data,
+ guint *out_len)
+{
+ GHashTableIter iter;
+ gpointer value;
+ gpointer *arr;
+ guint i, len;
+
+ if ( !hash
+ || (len = g_hash_table_size (hash)) == 0u) {
+ NM_SET_OUT (out_len, 0);
+ return NULL;
+ }
+
+ arr = g_new (gpointer, ((gsize) len) + 1);
+ i = 0;
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &value))
+ arr[i++] = value;
+
+ nm_assert (i == len);
+ arr[len] = NULL;
+
+ if ( len > 1
+ && compare_func) {
+ g_qsort_with_data (arr,
+ len,
+ sizeof (gpointer),
+ compare_func,
+ user_data);
+ }
+
+ return arr;
+}
+
gboolean
nm_utils_hashtable_same_keys (const GHashTable *a,
const GHashTable *b)
diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index b5116a1290..75b5d387c5 100644
--- a/shared/nm-glib-aux/nm-shared-utils.h
+++ b/shared/nm-glib-aux/nm-shared-utils.h
@@ -638,6 +638,7 @@ _nm_g_slice_free_fcn_define (8)
_nm_g_slice_free_fcn_define (10)
_nm_g_slice_free_fcn_define (12)
_nm_g_slice_free_fcn_define (16)
+_nm_g_slice_free_fcn_define (32)
#define _nm_g_slice_free_fcn1(mem_size) \
({ \
@@ -652,7 +653,8 @@ _nm_g_slice_free_fcn_define (16)
|| ((mem_size) == 8) \
|| ((mem_size) == 10) \
|| ((mem_size) == 12) \
- || ((mem_size) == 16)); \
+ || ((mem_size) == 16) \
+ || ((mem_size) == 32)); \
switch ((mem_size)) { \
case 1: _fcn = _nm_g_slice_free_fcn_1; break; \
case 2: _fcn = _nm_g_slice_free_fcn_2; break; \
@@ -661,6 +663,7 @@ _nm_g_slice_free_fcn_define (16)
case 10: _fcn = _nm_g_slice_free_fcn_10; break; \
case 12: _fcn = _nm_g_slice_free_fcn_12; break; \
case 16: _fcn = _nm_g_slice_free_fcn_16; break; \
+ case 32: _fcn = _nm_g_slice_free_fcn_32; break; \
default: g_assert_not_reached (); _fcn = NULL; break; \
} \
_fcn; \
@@ -952,6 +955,11 @@ gpointer *nm_utils_hash_keys_to_array (GHashTable *hash,
gpointer user_data,
guint *out_len);
+gpointer *nm_utils_hash_values_to_array (GHashTable *hash,
+ GCompareDataFunc compare_func,
+ gpointer user_data,
+ guint *out_len);
+
static inline const char **
nm_utils_strdict_get_keys (const GHashTable *hash,
gboolean sorted,
diff --git a/shared/nm-std-aux/nm-dbus-compat.h b/shared/nm-std-aux/nm-dbus-compat.h
index 263584d27b..b04d145474 100644
--- a/shared/nm-std-aux/nm-dbus-compat.h
+++ b/shared/nm-std-aux/nm-dbus-compat.h
@@ -31,6 +31,8 @@
/** The interface supported by most dbus peers */
#define DBUS_INTERFACE_PEER "org.freedesktop.DBus.Peer"
+#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
+
/** This is a special interface whose methods can only be invoked
* by the local implementation (messages from remote apps aren't
* allowed to specify this interface).
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index b928b8b3b4..19e51cf198 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -946,9 +946,10 @@ nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule,
struct _NMShutdownWaitObjHandle {
CList lst;
- GObject *watched_obj;
+ gpointer watched_obj;
char *msg_reason;
bool free_msg_reason:1;
+ bool is_cancellable:1;
};
static CList _shutdown_waitobj_lst_head;
@@ -967,7 +968,7 @@ _shutdown_waitobj_unregister (NMShutdownWaitObjHandle *handle)
static void
_shutdown_waitobj_cb (gpointer user_data,
- GObject *where_the_object_was)
+ GObject *where_the_object_was)
{
NMShutdownWaitObjHandle *handle = user_data;
@@ -980,6 +981,8 @@ _shutdown_waitobj_cb (gpointer user_data,
* nm_shutdown_wait_obj_register_full:
* @watched_obj: the object to watch. Takes a weak reference on the object
* to be notified when it gets destroyed.
+ * @wait_type: whether @watched_obj is just a plain GObject or a GCancellable
+ * that should be cancelled.
* @msg_reason: a reason message, for debugging and logging purposes.
* @free_msg_reason: if %TRUE, then ownership of @msg_reason will be taken
* and the string will be freed with g_free() afterwards. If %FALSE,
@@ -993,21 +996,41 @@ _shutdown_waitobj_cb (gpointer user_data,
* the reference-counter of @watched_obj as signal, that the object
* is still used.
*
+ * If @wait_type is %NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE, then during shutdown
+ * (after %NM_SHUTDOWN_TIMEOUT_MS), the cancellable will be cancelled to notify
+ * the source of the shutdown. Note that otherwise, in this mode also @watched_obj
+ * is only tracked with a weak-pointer. Especially, it does not register to the
+ * "cancelled" signal to automatically unregister (otherwise, you would never
+ * know whether the returned NMShutdownWaitObjHandle is still valid.
+ *
* FIXME(shutdown): proper shutdown is not yet implemented, and registering
* an object (currently) has no effect.
*
+ * FIXME(shutdown): during shutdown, after %NM_SHUTDOWN_TIMEOUT_MS timeout, cancel
+ * all remaining %NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE instances. Also, when somebody
+ * enqueues a cancellable after that point, cancel it right away on an idle handler.
+ *
* Returns: a handle to unregister the object. The caller may choose to ignore
* the handle, in which case, the object will be automatically unregistered,
* once it gets destroyed.
+ * Note that the handle is only valid as long as @watched_obj exists. If
+ * you plan to use it, ensure that you take care of not using it after
+ * destroying @watched_obj.
*/
NMShutdownWaitObjHandle *
-nm_shutdown_wait_obj_register_full (GObject *watched_obj,
+nm_shutdown_wait_obj_register_full (gpointer watched_obj,
+ NMShutdownWaitType wait_type,
char *msg_reason,
gboolean free_msg_reason)
{
NMShutdownWaitObjHandle *handle;
- g_return_val_if_fail (G_IS_OBJECT (watched_obj), NULL);
+ if (wait_type == NM_SHUTDOWN_WAIT_TYPE_OBJECT)
+ g_return_val_if_fail (G_IS_OBJECT (watched_obj), NULL);
+ else if (wait_type == NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE)
+ g_return_val_if_fail (G_IS_CANCELLABLE (watched_obj), NULL);
+ else
+ g_return_val_if_reached (NULL);
if (G_UNLIKELY (!_shutdown_waitobj_lst_head.next))
c_list_init (&_shutdown_waitobj_lst_head);
@@ -1020,6 +1043,7 @@ nm_shutdown_wait_obj_register_full (GObject *watched_obj,
.watched_obj = watched_obj,
.msg_reason = msg_reason,
.free_msg_reason = free_msg_reason,
+ .is_cancellable = (wait_type == NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE),
};
c_list_link_tail (&_shutdown_waitobj_lst_head, &handle->lst);
g_object_weak_ref (watched_obj, _shutdown_waitobj_cb, handle);
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index e957b95e29..72be1c4cf6 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -74,13 +74,43 @@ NMPlatformRoutingRule *nm_ip_routing_rule_to_platform (const NMIPRoutingRule *ru
#define NM_SHUTDOWN_TIMEOUT_MS 1500
#define NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG 500
+typedef enum {
+ /* The watched_obj argument is a GObject, and shutdown is delayed until the object
+ * gets destroyed (or unregistered). */
+ NM_SHUTDOWN_WAIT_TYPE_OBJECT,
+
+ /* The watched_obj argument is a GCancellable, and shutdown is delayed until the object
+ * gets destroyed (or unregistered). Note that after NM_SHUTDOWN_TIMEOUT_MS, the
+ * cancellable will be cancelled to notify listeners about the shutdown. */
+ NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE,
+} NMShutdownWaitType;
+
typedef struct _NMShutdownWaitObjHandle NMShutdownWaitObjHandle;
-NMShutdownWaitObjHandle *nm_shutdown_wait_obj_register_full (GObject *watched_obj,
+NMShutdownWaitObjHandle *nm_shutdown_wait_obj_register_full (gpointer watched_obj,
+ NMShutdownWaitType wait_type,
char *msg_reason,
gboolean free_msg_reason);
-#define nm_shutdown_wait_obj_register(watched_obj, msg_reason) nm_shutdown_wait_obj_register_full((watched_obj), (""msg_reason""), FALSE)
+static inline NMShutdownWaitObjHandle *
+nm_shutdown_wait_obj_register_object_full (gpointer watched_obj,
+ char *msg_reason,
+ gboolean free_msg_reason)
+{
+ return nm_shutdown_wait_obj_register_full (watched_obj, NM_SHUTDOWN_WAIT_TYPE_OBJECT, msg_reason, free_msg_reason);
+}
+
+#define nm_shutdown_wait_obj_register_object(watched_obj, msg_reason) nm_shutdown_wait_obj_register_object_full((watched_obj), (""msg_reason""), FALSE)
+
+static inline NMShutdownWaitObjHandle *
+nm_shutdown_wait_obj_register_cancellable_full (GCancellable *watched_obj,
+ char *msg_reason,
+ gboolean free_msg_reason)
+{
+ return nm_shutdown_wait_obj_register_full (watched_obj, NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE, msg_reason, free_msg_reason);
+}
+
+#define nm_shutdown_wait_obj_register_cancellable(watched_obj, msg_reason) nm_shutdown_wait_obj_register_cancellable_full((watched_obj), (""msg_reason""), FALSE)
void nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle);
diff --git a/src/devices/bluetooth/meson.build b/src/devices/bluetooth/meson.build
index 2def5b74ca..31fbf00674 100644
--- a/src/devices/bluetooth/meson.build
+++ b/src/devices/bluetooth/meson.build
@@ -1,7 +1,5 @@
sources = files(
- 'nm-bluez-device.c',
'nm-bluez-manager.c',
- 'nm-bluez5-manager.c',
'nm-bt-error.c',
'nm-device-bt.c',
)
diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h
index 80bbb9d9c6..0289da0fe1 100644
--- a/src/devices/bluetooth/nm-bluez-common.h
+++ b/src/devices/bluetooth/nm-bluez-common.h
@@ -13,7 +13,6 @@
#define NM_BLUEZ_SERVICE "org.bluez"
#define NM_BLUEZ_MANAGER_PATH "/"
-#define NM_OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager"
#define NM_BLUEZ5_ADAPTER_INTERFACE "org.bluez.Adapter1"
#define NM_BLUEZ5_DEVICE_INTERFACE "org.bluez.Device1"
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
deleted file mode 100644
index 072582ed87..0000000000
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ /dev/null
@@ -1,1207 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* NetworkManager -- Network link manager
- *
- * Copyright (C) 2009 - 2012 Red Hat, Inc.
- * Copyright (C) 2013 Intel Corporation.
- */
-
-#include "nm-default.h"
-
-#include "nm-bluez-device.h"
-
-#include "nm-core-internal.h"
-#include "nm-bt-error.h"
-#include "nm-bluez-common.h"
-#include "settings/nm-settings.h"
-#include "settings/nm-settings-connection.h"
-#include "NetworkManagerUtils.h"
-
-#if WITH_BLUEZ5_DUN
-#include "nm-bluez5-dun.h"
-#endif
-
-/*****************************************************************************/
-
-#define VARIANT_IS_OF_TYPE_BOOLEAN(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_BOOLEAN) ))
-#define VARIANT_IS_OF_TYPE_STRING(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING) ))
-#define VARIANT_IS_OF_TYPE_OBJECT_PATH(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_OBJECT_PATH) ))
-#define VARIANT_IS_OF_TYPE_STRING_ARRAY(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING_ARRAY) ))
-
-/*****************************************************************************/
-
-NM_GOBJECT_PROPERTIES_DEFINE (NMBluezDevice,
- PROP_PATH,
- PROP_ADDRESS,
- PROP_NAME,
- PROP_CAPABILITIES,
- PROP_USABLE,
- PROP_CONNECTED,
-);
-
-enum {
- INITIALIZED,
- REMOVED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
- char *path;
-
- GDBusConnection *dbus_connection;
-
- GDBusProxy *proxy;
-
- GDBusProxy *adapter5;
- gboolean adapter_powered;
-
- gboolean initialized;
- gboolean usable;
- NMBluetoothCapabilities connection_bt_type;
-
- guint check_emit_usable_id;
-
- char *adapter_address;
- char *address;
- char *name;
- guint32 capabilities;
- gboolean connected;
- gboolean paired;
-
- char *b4_iface;
-#if WITH_BLUEZ5_DUN
- NMBluez5DunContext *b5_dun_context;
-#endif
-
- NMSettings *settings;
- GSList *connections;
-
- NMSettingsConnection *pan_connection;
- gboolean pan_connection_no_autocreate;
-} NMBluezDevicePrivate;
-
-struct _NMBluezDevice {
- GObject parent;
- NMBluezDevicePrivate _priv;
-};
-
-struct _NMBluezDeviceClass {
- GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
-
-#define NM_BLUEZ_DEVICE_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluezDevice, NM_IS_BLUEZ_DEVICE)
-
-/*****************************************************************************/
-
-#define _NMLOG_PREFIX_NAME "bluez"
-#define _NMLOG_DOMAIN LOGD_BT
-#define _NMLOG(level, ...) \
- G_STMT_START { \
- if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
- const char *_path = (self) ? NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path : NULL; \
- \
- _nm_log ((level), \
- (_NMLOG_DOMAIN), \
- 0, \
- NULL, \
- NULL, \
- "%s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
- NM_PRINT_FMT_QUOTED (_path, _NMLOG_PREFIX_NAME"[", _path, "]: ", _NMLOG_PREFIX_NAME": ") \
- _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
- } \
- } G_STMT_END
-
-/*****************************************************************************/
-
-static void cp_connection_added (NMSettings *settings,
- NMSettingsConnection *sett_conn,
- NMBluezDevice *self);
-static gboolean connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn);
-
-/*****************************************************************************/
-
-const char *
-nm_bluez_device_get_path (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path;
-}
-
-const char *
-nm_bluez_device_get_address (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->address;
-}
-
-gboolean
-nm_bluez_device_get_initialized (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->initialized;
-}
-
-gboolean
-nm_bluez_device_get_usable (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->usable;
-}
-
-const char *
-nm_bluez_device_get_name (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->name;
-}
-
-guint32
-nm_bluez_device_get_capabilities (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->capabilities;
-}
-
-gboolean
-nm_bluez_device_get_connected (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv;
-
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
- priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- return priv->connected;
-}
-
-static void
-pan_connection_check_create (NMBluezDevice *self)
-{
- gs_unref_object NMConnection *connection = NULL;
- NMSettingsConnection *added;
- NMSetting *setting;
- gs_free char *id = NULL;
- char uuid[37];
- GError *error = NULL;
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- g_return_if_fail (priv->capabilities & NM_BT_CAPABILITY_NAP);
- g_return_if_fail (priv->connections == NULL);
- g_return_if_fail (priv->name);
-
- if (priv->pan_connection || priv->pan_connection_no_autocreate) {
- /* already have a connection or we don't want to create one, nothing to do. */
- return;
- }
-
- /* Only try once to create a connection. If it does not succeed, we do not try again. Also,
- * if the connection gets deleted later, do not create another one for this device. */
- priv->pan_connection_no_autocreate = TRUE;
-
- /* create a new connection */
-
- connection = nm_simple_connection_new ();
-
- /* Setting: Connection */
- nm_utils_uuid_generate_buf (uuid);
- id = g_strdup_printf (_("%s Network"), priv->name);
- setting = nm_setting_connection_new ();
- g_object_set (setting,
- NM_SETTING_CONNECTION_ID, id,
- NM_SETTING_CONNECTION_UUID, uuid,
- NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
- NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
- NULL);
- nm_connection_add_setting (connection, setting);
-
- /* Setting: Bluetooth */
- setting = nm_setting_bluetooth_new ();
- g_object_set (G_OBJECT (setting),
- NM_SETTING_BLUETOOTH_BDADDR, priv->address,
- NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
- NULL);
- nm_connection_add_setting (connection, setting);
-
- if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
- _LOGE ("couldn't generate a connection for NAP device: %s",
- error->message);
- g_error_free (error);
- g_return_if_reached ();
- }
-
- /* Adding a new connection raises a signal which eventually calls check_emit_usable (again)
- * which then already finds the suitable connection in priv->connections. This is confusing,
- * so block the signal. check_emit_usable will succeed after this function call returns. */
- g_signal_handlers_block_by_func (priv->settings, cp_connection_added, self);
- nm_settings_add_connection (priv->settings,
- connection,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
- NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
- &added,
- &error);
- g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self);
-
- if (added) {
- nm_assert (!g_slist_find (priv->connections, added));
- nm_assert (connection_compatible (self, added));
- priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
- priv->pan_connection = added;
- _LOGD ("added new Bluetooth connection for NAP device: '%s' (%s)",
- id, uuid);
- } else {
- _LOGW ("couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
- id, uuid, error->message);
- g_clear_error (&error);
- }
-}
-
-static gboolean
-check_emit_usable (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- gboolean new_usable;
-
- /* only expect the supported capabilities set. */
- nm_assert ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE );
-
- new_usable = ( priv->initialized
- && priv->capabilities
- && priv->name
- && priv->paired
- && priv->adapter5
- && priv->adapter_powered
- && priv->dbus_connection
- && priv->address
- && priv->adapter_address);
-
- if (!new_usable)
- goto END;
-
- if (priv->connections)
- goto END;
-
- if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) {
- /* non NAP devices are only usable, if they already have a connection. */
- new_usable = FALSE;
- goto END;
- }
-
- pan_connection_check_create (self);
- new_usable = !!priv->pan_connection;
-
-END:
- if (new_usable != priv->usable) {
- priv->usable = new_usable;
- _notify (self, PROP_USABLE);
- }
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-check_emit_usable_schedule (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- if (priv->check_emit_usable_id == 0)
- priv->check_emit_usable_id = g_idle_add ((GSourceFunc) check_emit_usable, self);
-}
-
-/*****************************************************************************/
-
-static gboolean
-connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- NMConnection *connection = nm_settings_connection_get_connection (sett_conn);
- NMSettingBluetooth *s_bt;
- const char *bt_type;
- const char *bdaddr;
-
- if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
- return FALSE;
-
- s_bt = nm_connection_get_setting_bluetooth (connection);
- if (!s_bt)
- return FALSE;
-
- if (!priv->address)
- return FALSE;
-
- bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
- if (!bdaddr)
- return FALSE;
- if (!nm_utils_hwaddr_matches (bdaddr, -1, priv->address, -1))
- return FALSE;
-
- bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
-
- if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_NAP))
- return FALSE;
-
- if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
- && !(priv->capabilities & NM_BT_CAPABILITY_DUN))
- return FALSE;
-
- if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)
- && !(priv->capabilities & NM_BT_CAPABILITY_NAP))
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-_internal_track_connection (NMBluezDevice *self,
- NMSettingsConnection *sett_conn,
- gboolean tracked)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- gboolean was_tracked;
-
- was_tracked = !!g_slist_find (priv->connections, sett_conn);
- if (was_tracked == !!tracked)
- return FALSE;
-
- if (tracked)
- priv->connections = g_slist_prepend (priv->connections, g_object_ref (sett_conn));
- else {
- priv->connections = g_slist_remove (priv->connections, sett_conn);
- if (priv->pan_connection == sett_conn)
- priv->pan_connection = NULL;
- g_object_unref (sett_conn);
- }
-
- return TRUE;
-}
-
-static void
-cp_connection_added (NMSettings *settings,
- NMSettingsConnection *sett_conn,
- NMBluezDevice *self)
-{
- if (connection_compatible (self, sett_conn)) {
- if (_internal_track_connection (self, sett_conn, TRUE))
- check_emit_usable (self);
- }
-}
-
-static void
-cp_connection_removed (NMSettings *settings,
- NMSettingsConnection *sett_conn,
- NMBluezDevice *self)
-{
- if (_internal_track_connection (self, sett_conn, FALSE))
- check_emit_usable (self);
-}
-
-static void
-cp_connection_updated (NMSettings *settings,
- NMSettingsConnection *sett_conn,
- guint update_reason_u,
- NMBluezDevice *self)
-{
- if (_internal_track_connection (self, sett_conn,
- connection_compatible (self, sett_conn)))
- check_emit_usable_schedule (self);
-}
-
-static void
-load_connections (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- NMSettingsConnection *const*connections;
- guint i;
- gboolean changed = FALSE;
-
- connections = nm_settings_get_connections (priv->settings, NULL);
- for (i = 0; connections[i]; i++) {
- if (connection_compatible (self, connections[i]))
- changed |= _internal_track_connection (self, connections[i], TRUE);
- }
- if (changed)
- check_emit_usable (self);
-}
-
-/*****************************************************************************/
-
-static void
-bluez_disconnect_cb (GDBusConnection *dbus_connection,
- GAsyncResult *res,
- gpointer user_data)
-{
- gs_unref_object NMBluezDevice *self = user_data;
- gs_free_error GError *error = NULL;
- gs_unref_variant GVariant *variant = NULL;
-
- variant = g_dbus_connection_call_finish (dbus_connection, res, &error);
- if (!variant) {
- if (!strstr (error->message, "org.bluez.Error.NotConnected"))
- _LOGW ("failed to disconnect: %s", error->message);
- }
-}
-
-void
-nm_bluez_device_disconnect (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GVariant *args = NULL;
- const char *dbus_iface = NULL;
-
- g_return_if_fail (priv->dbus_connection);
-
- /* FIXME: if we are in the process of connecting and cancel the
- * connection attempt, we must complete the pending connect request.
- * However, we must also ensure that we don't leave a connected device. */
- if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
-#if WITH_BLUEZ5_DUN
- nm_bluez5_dun_cleanup (priv->b5_dun_context);
-#endif
- priv->connected = FALSE;
- goto out;
- } else if (priv->connection_bt_type == NM_BT_CAPABILITY_NAP) {
- dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
- } else
- nm_assert_not_reached ();
-
- g_dbus_connection_call (priv->dbus_connection,
- NM_BLUEZ_SERVICE,
- priv->path,
- dbus_iface,
- "Disconnect",
- args ?: g_variant_new("()"),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- 10000,
- NULL,
- (GAsyncReadyCallback) bluez_disconnect_cb,
- g_object_ref (self));
-
-out:
- g_clear_pointer (&priv->b4_iface, g_free);
- priv->connection_bt_type = NM_BT_CAPABILITY_NONE;
-}
-
-static void
-_connect_complete (NMBluezDevice *self,
- const char *device,
- NMBluezDeviceConnectCallback callback,
- gpointer callback_user_data,
- GError *error)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- nm_assert ((device || error) && !(device && error));
-
- if (device) {
- priv->connected = TRUE;
- _notify (self, PROP_CONNECTED);
- }
-
- if (callback)
- callback (self, device, error, callback_user_data);
-}
-
-static void
-_connect_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- gs_unref_object NMBluezDevice *self = NULL;
- NMBluezDevicePrivate *priv;
- NMBluezDeviceConnectCallback callback;
- gpointer callback_user_data;
- gs_free_error GError *error = NULL;
- char *device = NULL;
- gs_unref_variant GVariant *variant = NULL;
-
- nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
-
- priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- variant = _nm_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, G_VARIANT_TYPE ("(s)"), &error);
- if (variant) {
- g_variant_get (variant, "(s)", &device);
- priv->b4_iface = device;
- }
-
- _connect_complete (self, device, callback, callback_user_data, error);
-}
-
-#if WITH_BLUEZ5_DUN
-static void
-_connect_cb_bluez5_dun (NMBluez5DunContext *context,
- const char *device,
- GError *error,
- gpointer user_data)
-{
- gs_unref_object NMBluezDevice *self = NULL;
- gs_unref_object GCancellable *cancellable = NULL;
- NMBluezDeviceConnectCallback callback;
- gpointer callback_user_data;
- gs_free_error GError *cancelled_error = NULL;
-
- nm_utils_user_data_unpack (user_data, &self, &cancellable, &callback, &callback_user_data);
-
- /* FIXME(shutdown): the async operation nm_bluez5_dun_connect() should be cancellable.
- * Fake it here. */
- if (g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error))
- error = cancelled_error;
-
- _connect_complete (self, device, callback, callback_user_data, error);
-}
-#else /* WITH_BLUEZ5_DUN */
-static void
-_connect_cb_bluez5_dun_idle_no_b5 (gpointer user_data,
- GCancellable *cancellable)
-{
- gs_unref_object NMBluezDevice *self = NULL;
- NMBluezDeviceConnectCallback callback;
- gpointer callback_user_data;
- gs_free_error GError *error = NULL;
-
- nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
-
- if (!g_cancellable_set_error_if_cancelled (cancellable, &error)) {
- g_set_error (&error,
- NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "NetworkManager built without support for Bluez 5");
- }
- callback (self, NULL, error, callback_user_data);
-}
-#endif /* WITH_BLUEZ5_DUN */
-
-void
-nm_bluez_device_connect_async (NMBluezDevice *self,
- NMBluetoothCapabilities connection_bt_type,
- GCancellable *cancellable,
- NMBluezDeviceConnectCallback callback,
- gpointer callback_user_data)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- const char *dbus_iface = NULL;
- const char *connect_type = NULL;
-
- g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
-
- priv->connection_bt_type = connection_bt_type;
-
- if (connection_bt_type == NM_BT_CAPABILITY_NAP) {
- connect_type = BLUETOOTH_CONNECT_NAP;
- dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
- } else if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
- connect_type = BLUETOOTH_CONNECT_DUN;
-#if WITH_BLUEZ5_DUN
- if (priv->b5_dun_context == NULL)
- priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address);
- nm_bluez5_dun_connect (priv->b5_dun_context,
- _connect_cb_bluez5_dun,
- nm_utils_user_data_pack (g_object_ref (self),
- nm_g_object_ref (cancellable),
- callback,
- callback_user_data));
-#else
- if (callback) {
- nm_utils_invoke_on_idle (_connect_cb_bluez5_dun_idle_no_b5,
- nm_utils_user_data_pack (g_object_ref (self),
- callback,
- callback_user_data),
- cancellable);
- }
-#endif
- return;
- } else
- g_return_if_reached ();
-
- /* FIXME: we need to remember that a connect is in progress.
- * So, if the request gets cancelled, that we disconnect the
- * connection that was established in the meantime. */
- g_dbus_connection_call (priv->dbus_connection,
- NM_BLUEZ_SERVICE,
- priv->path,
- dbus_iface,
- "Connect",
- g_variant_new ("(s)", connect_type),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- 20000,
- cancellable,
- _connect_cb,
- nm_utils_user_data_pack (g_object_ref (self),
- callback,
- callback_user_data));
-}
-
-/*****************************************************************************/
-
-static void
-set_adapter_address (NMBluezDevice *self, const char *address)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- g_return_if_fail (address);
-
- if (priv->adapter_address)
- g_free (priv->adapter_address);
- priv->adapter_address = g_strdup (address);
-}
-
-static guint32
-convert_uuids_to_capabilities (const char **strings)
-{
- const char **iter;
- guint32 capabilities = 0;
-
- for (iter = strings; iter && *iter; iter++) {
- char **parts;
-
- parts = g_strsplit (*iter, "-", -1);
- if (parts && parts[0]) {
- switch (g_ascii_strtoull (parts[0], NULL, 16)) {
- case 0x1103:
- capabilities |= NM_BT_CAPABILITY_DUN;
- break;
- case 0x1116:
- capabilities |= NM_BT_CAPABILITY_NAP;
- break;
- default:
- break;
- }
- }
- g_strfreev (parts);
- }
-
- return capabilities;
-}
-
-static void
-_set_property_capabilities (NMBluezDevice *self, const char **uuids)
-{
- guint32 uint_val;
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- uint_val = convert_uuids_to_capabilities (uuids);
- if (priv->capabilities != uint_val) {
- if (priv->capabilities) {
- /* changing (relevant) capabilities is not supported and ignored -- except setting initially */
- _LOGW ("ignore change of capabilities for Bluetooth device from %u to %u",
- priv->capabilities, uint_val);
- return;
- }
- _LOGD ("set capabilities for Bluetooth device: %s%s%s",
- uint_val & NM_BT_CAPABILITY_NAP ? "NAP" : "",
- ((uint_val & NM_BT_CAPABILITY_DUN) && (uint_val &NM_BT_CAPABILITY_NAP)) ? " | " : "",
- uint_val & NM_BT_CAPABILITY_DUN ? "DUN" : "");
- priv->capabilities = uint_val;
- _notify (self, PROP_CAPABILITIES);
- }
-}
-
-/**
- * priv->address can only be set one to a certain (non NULL) value. Every later attempt
- * to reset it to another value will be ignored and a warning will be logged.
- **/
-static void
-_set_property_address (NMBluezDevice *self, const char *addr)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- if (g_strcmp0 (priv->address, addr) == 0)
- return;
-
- if (!addr) {
- _LOGW ("cannot reset address from '%s' to NULL", priv->address);
- return;
- }
-
- if (priv->address != NULL) {
- _LOGW ("cannot reset address from '%s' to '%s'", priv->address, addr);
- return;
- }
-
- if (!nm_utils_hwaddr_valid (addr, ETH_ALEN)) {
- _LOGW ("cannot set address to '%s' (invalid value)", addr);
- return;
- }
-
- priv->address = g_strdup (addr);
- _notify (self, PROP_ADDRESS);
-}
-
-static void
-_take_variant_property_address (NMBluezDevice *self, GVariant *v)
-{
- _set_property_address (self, VARIANT_IS_OF_TYPE_STRING (v) ? g_variant_get_string (v, NULL) : NULL);
- if (v)
- g_variant_unref (v);
-}
-
-static void
-_take_variant_property_name (NMBluezDevice *self, GVariant *v)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- const char *str;
-
- if (VARIANT_IS_OF_TYPE_STRING (v)) {
- str = g_variant_get_string (v, NULL);
- if (g_strcmp0 (priv->name, str)) {
- g_free (priv->name);
- priv->name = g_strdup (str);
- _notify (self, PROP_NAME);
- }
- }
- if (v)
- g_variant_unref (v);
-}
-
-static void
-_take_variant_property_uuids (NMBluezDevice *self, GVariant *v)
-{
- if (VARIANT_IS_OF_TYPE_STRING_ARRAY (v)) {
- const char **uuids = g_variant_get_strv (v, NULL);
-
- _set_property_capabilities (self, uuids);
- g_free (uuids);
- }
- if (v)
- g_variant_unref (v);
-}
-
-static void
-_take_variant_property_connected (NMBluezDevice *self, GVariant *v)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- if (VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
- gboolean connected = g_variant_get_boolean (v);
-
- if (priv->connected != connected) {
- priv->connected = connected;
- _notify (self, PROP_CONNECTED);
- }
- }
- if (v)
- g_variant_unref (v);
-}
-
-static void
-_take_variant_property_paired (NMBluezDevice *self, GVariant *v)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- if (VARIANT_IS_OF_TYPE_BOOLEAN (v))
- priv->paired = g_variant_get_boolean (v);
-
- if (v)
- g_variant_unref (v);
-}
-
-static void
-adapter5_on_properties_changed (GDBusProxy *proxy,
- GVariant *changed_properties,
- GStrv invalidated_properties,
- gpointer user_data)
-{
- NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GVariantIter i;
- const char *property;
- GVariant *v;
-
- g_variant_iter_init (&i, changed_properties);
- while (g_variant_iter_next (&i, "{&sv}", &property, &v)) {
- if (!strcmp (property, "Powered") && VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
- gboolean powered = g_variant_get_boolean (v);
- if (priv->adapter_powered != powered)
- priv->adapter_powered = powered;
- }
- g_variant_unref (v);
- }
-
- check_emit_usable (self);
-}
-
-static void
-adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GError *error = NULL;
- GVariant *v;
-
- priv->adapter5 = g_dbus_proxy_new_for_bus_finish (res, &error);
- if (!priv->adapter5) {
- _LOGW ("failed to acquire adapter proxy: %s", error->message);
- g_clear_error (&error);
- g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
- } else {
- g_signal_connect (priv->adapter5, "g-properties-changed",
- G_CALLBACK (adapter5_on_properties_changed), self);
-
- /* Check adapter's powered state */
- v = g_dbus_proxy_get_cached_property (priv->adapter5, "Powered");
- priv->adapter_powered = VARIANT_IS_OF_TYPE_BOOLEAN (v) ? g_variant_get_boolean (v) : FALSE;
- if (v)
- g_variant_unref (v);
-
- v = g_dbus_proxy_get_cached_property (priv->adapter5, "Address");
- if (VARIANT_IS_OF_TYPE_STRING (v))
- set_adapter_address (self, g_variant_get_string (v, NULL));
-
- priv->initialized = TRUE;
- g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
-
- check_emit_usable (self);
- }
-
- g_object_unref (self);
-}
-
-static void
-_take_one_variant_property (NMBluezDevice *self, const char *property, GVariant *v)
-{
- if (v) {
- if (!g_strcmp0 (property, "Address"))
- _take_variant_property_address (self, v);
- else if (!g_strcmp0 (property, "Connected"))
- _take_variant_property_connected (self, v);
- else if (!g_strcmp0 (property, "Paired"))
- _take_variant_property_paired (self, v);
- else if (!g_strcmp0 (property, "Name"))
- _take_variant_property_name (self, v);
- else if (!g_strcmp0 (property, "UUIDs"))
- _take_variant_property_uuids (self, v);
- else
- g_variant_unref (v);
- }
-}
-
-static void
-_set_properties (NMBluezDevice *self, GVariant *properties)
-{
- GVariantIter i;
- const char *property;
- GVariant *v;
-
- g_object_freeze_notify (G_OBJECT (self));
- g_variant_iter_init (&i, properties);
- while (g_variant_iter_next (&i, "{&sv}", &property, &v))
- _take_one_variant_property (self, property, v);
- g_object_thaw_notify (G_OBJECT (self));
-}
-
-static void
-properties_changed (GDBusProxy *proxy,
- GVariant *changed_properties,
- GStrv invalidated_properties,
- gpointer user_data)
-{
- NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
-
- _set_properties (self, changed_properties);
- check_emit_usable (self);
-}
-
-static void
-query_properties (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GVariant *v;
-
- g_object_freeze_notify (G_OBJECT (self));
- _take_variant_property_address (self, g_dbus_proxy_get_cached_property (priv->proxy, "Address"));
- _take_variant_property_connected (self, g_dbus_proxy_get_cached_property (priv->proxy, "Connected"));
- _take_variant_property_paired (self, g_dbus_proxy_get_cached_property (priv->proxy, "Paired"));
- _take_variant_property_name (self, g_dbus_proxy_get_cached_property (priv->proxy, "Name"));
- _take_variant_property_uuids (self, g_dbus_proxy_get_cached_property (priv->proxy, "UUIDs"));
- g_object_thaw_notify (G_OBJECT (self));
-
- v = g_dbus_proxy_get_cached_property (priv->proxy, "Adapter");
- if (VARIANT_IS_OF_TYPE_OBJECT_PATH (v)) {
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- NM_BLUEZ_SERVICE,
- g_variant_get_string (v, NULL),
- NM_BLUEZ5_ADAPTER_INTERFACE,
- NULL,
- (GAsyncReadyCallback) adapter5_on_acquired,
- g_object_ref (self));
- g_variant_unref (v);
- } else {
- /* If the Adapter property is unset at this point, we won't try to acquire the adapter later on
- * and the device stays unusable. This should not happen, but if it does, log a debug message. */
- _LOGD ("device has no adapter property and cannot be used");
- }
-
- /* Check if any connections match this device */
- load_connections (self);
-}
-
-static void
-on_proxy_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GError *error = NULL;
-
- priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-
- if (!priv->proxy) {
- _LOGW ("failed to acquire device proxy: %s", error->message);
- g_clear_error (&error);
- g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
- } else {
- g_signal_connect (priv->proxy, "g-properties-changed",
- G_CALLBACK (properties_changed), self);
- query_properties (self);
- }
- g_object_unref (self);
-}
-
-/*****************************************************************************/
-
-static void
-get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
-
- switch (prop_id) {
- case PROP_PATH:
- g_value_set_string (value, priv->path);
- break;
- case PROP_ADDRESS:
- g_value_set_string (value, priv->address);
- break;
- case PROP_NAME:
- g_value_set_string (value, priv->name);
- break;
- case PROP_CAPABILITIES:
- g_value_set_uint (value, priv->capabilities);
- break;
- case PROP_USABLE:
- g_value_set_boolean (value, priv->usable);
- break;
- case PROP_CONNECTED:
- g_value_set_boolean (value, priv->connected);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
-
- switch (prop_id) {
- case PROP_PATH:
- /* construct-only */
- priv->path = g_value_dup_string (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-/*****************************************************************************/
-
-static void
-nm_bluez_device_init (NMBluezDevice *self)
-{
-}
-
-NMBluezDevice *
-nm_bluez_device_new (GDBusConnection *dbus_connection,
- const char *path,
- NMSettings *settings)
-{
- NMBluezDevice *self;
- NMBluezDevicePrivate *priv;
-
- g_return_val_if_fail (path != NULL, NULL);
- g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
- g_return_val_if_fail (G_IS_DBUS_CONNECTION (dbus_connection), NULL);
-
- self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE,
- NM_BLUEZ_DEVICE_PATH, path,
- NULL);
- if (!self)
- return NULL;
-
- _LOGD ("create NMBluezDevice");
-
- priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- priv->settings = g_object_ref (settings);
-
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, G_CALLBACK (cp_connection_added), self);
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), self);
-
- priv->dbus_connection = g_object_ref (dbus_connection);
-
- g_dbus_proxy_new (priv->dbus_connection,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- NM_BLUEZ_SERVICE,
- priv->path,
- NM_BLUEZ5_DEVICE_INTERFACE,
- NULL,
- (GAsyncReadyCallback) on_proxy_acquired,
- g_object_ref (self));
-
- return self;
-}
-
-static void
-dispose (GObject *object)
-{
- NMBluezDevice *self = NM_BLUEZ_DEVICE (object);
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- NMSettingsConnection *to_delete = NULL;
-
- nm_clear_g_source (&priv->check_emit_usable_id);
-
- if (priv->pan_connection) {
- /* Check whether we want to remove the created connection. If so, we take a reference
- * and delete it at the end of dispose(). */
- if (NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->pan_connection),
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
- to_delete = g_object_ref (priv->pan_connection);
-
- priv->pan_connection = NULL;
- }
-
-#if WITH_BLUEZ5_DUN
- if (priv->b5_dun_context) {
- nm_bluez5_dun_free (priv->b5_dun_context);
- priv->b5_dun_context = NULL;
- }
-#endif
-
- if (priv->settings) {
- g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
- g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self);
- g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
- }
-
- g_slist_free_full (priv->connections, g_object_unref);
- priv->connections = NULL;
-
- if (priv->adapter5) {
- g_signal_handlers_disconnect_by_func (priv->adapter5, adapter5_on_properties_changed, self);
- g_clear_object (&priv->adapter5);
- }
-
- g_clear_object (&priv->dbus_connection);
-
- G_OBJECT_CLASS (nm_bluez_device_parent_class)->dispose (object);
-
- if (to_delete) {
- _LOGD ("removing Bluetooth connection for NAP device: '%s' (%s)",
- nm_settings_connection_get_id (to_delete),
- nm_settings_connection_get_uuid (to_delete));
- nm_settings_connection_delete (to_delete, FALSE);
- g_object_unref (to_delete);
- }
-
- g_clear_object (&priv->settings);
-}
-
-static void
-finalize (GObject *object)
-{
- NMBluezDevice *self = NM_BLUEZ_DEVICE (object);
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- _LOGD ("finalize NMBluezDevice");
-
- g_free (priv->path);
- g_free (priv->adapter_address);
- g_free (priv->address);
- g_free (priv->name);
- g_free (priv->b4_iface);
-
- if (priv->proxy)
- g_signal_handlers_disconnect_by_data (priv->proxy, object);
- g_clear_object (&priv->proxy);
-
- G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object);
-}
-
-static void
-nm_bluez_device_class_init (NMBluezDeviceClass *config_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (config_class);
-
- object_class->get_property = get_property;
- object_class->set_property = set_property;
- object_class->dispose = dispose;
- object_class->finalize = finalize;
-
- obj_properties[PROP_PATH] =
- g_param_spec_string (NM_BLUEZ_DEVICE_PATH, "", "",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_ADDRESS] =
- g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS, "", "",
- NULL,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_NAME] =
- g_param_spec_string (NM_BLUEZ_DEVICE_NAME, "", "",
- NULL,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_CAPABILITIES] =
- g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES, "", "",
- 0, G_MAXUINT, 0,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_USABLE] =
- g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE, "", "",
- FALSE,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_CONNECTED] =
- g_param_spec_boolean (NM_BLUEZ_DEVICE_CONNECTED, "", "",
- FALSE,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
- signals[INITIALIZED] = g_signal_new (NM_BLUEZ_DEVICE_INITIALIZED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL, NULL,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- signals[REMOVED] = g_signal_new (NM_BLUEZ_DEVICE_REMOVED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-}
-
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
deleted file mode 100644
index 3bccea8a88..0000000000
--- a/src/devices/bluetooth/nm-bluez-device.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* NetworkManager -- Network link manager
- *
- * Copyright (C) 2009 - 2014 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_BLUEZ_DEVICE_H__
-#define __NETWORKMANAGER_BLUEZ_DEVICE_H__
-
-#include "nm-connection.h"
-
-#define NM_TYPE_BLUEZ_DEVICE (nm_bluez_device_get_type ())
-#define NM_BLUEZ_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDevice))
-#define NM_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
-#define NM_IS_BLUEZ_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_DEVICE))
-#define NM_IS_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ_DEVICE))
-#define NM_BLUEZ_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
-
-/* Properties */
-#define NM_BLUEZ_DEVICE_PATH "path"
-#define NM_BLUEZ_DEVICE_ADDRESS "address"
-#define NM_BLUEZ_DEVICE_NAME "name"
-#define NM_BLUEZ_DEVICE_CAPABILITIES "capabilities"
-#define NM_BLUEZ_DEVICE_USABLE "usable"
-#define NM_BLUEZ_DEVICE_CONNECTED "connected"
-
-/* Signals */
-#define NM_BLUEZ_DEVICE_INITIALIZED "initialized"
-#define NM_BLUEZ_DEVICE_REMOVED "removed"
-
-typedef struct _NMBluezDevice NMBluezDevice;
-typedef struct _NMBluezDeviceClass NMBluezDeviceClass;
-
-GType nm_bluez_device_get_type (void);
-
-NMBluezDevice *nm_bluez_device_new (GDBusConnection *dbus_connection,
- const char *path,
- NMSettings *settings);
-
-const char *nm_bluez_device_get_path (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_initialized (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_usable (NMBluezDevice *self);
-
-const char *nm_bluez_device_get_address (NMBluezDevice *self);
-
-const char *nm_bluez_device_get_name (NMBluezDevice *self);
-
-guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_connected (NMBluezDevice *self);
-
-typedef void (*NMBluezDeviceConnectCallback) (NMBluezDevice *self,
- const char *device,
- GError *error,
- gpointer user_data);
-
-void
-nm_bluez_device_connect_async (NMBluezDevice *self,
- NMBluetoothCapabilities connection_bt_type,
- GCancellable *cancellable,
- NMBluezDeviceConnectCallback callback,
- gpointer callback_user_data);
-
-void
-nm_bluez_device_disconnect (NMBluezDevice *self);
-
-#endif /* __NETWORKMANAGER_BLUEZ_DEVICE_H__ */
-
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index cf9e521c74..22b40c0f85 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -6,52 +6,181 @@
#include "nm-default.h"
+#include "nm-bluez-manager.h"
+
#include <signal.h>
#include <stdlib.h>
#include <gmodule.h>
+#include "nm-glib-aux/nm-dbus-aux.h"
+#include "nm-glib-aux/nm-c-list.h"
+#include "nm-dbus-manager.h"
#include "devices/nm-device-factory.h"
#include "devices/nm-device-bridge.h"
#include "nm-setting-bluetooth.h"
#include "settings/nm-settings.h"
-#include "nm-bluez5-manager.h"
-#include "nm-bluez-device.h"
#include "nm-bluez-common.h"
#include "nm-device-bt.h"
+#include "nm-manager.h"
+#include "nm-bluez5-dun.h"
#include "nm-core-internal.h"
#include "platform/nm-platform.h"
#include "nm-std-aux/nm-dbus-compat.h"
/*****************************************************************************/
-#define NM_TYPE_BLUEZ_MANAGER (nm_bluez_manager_get_type ())
-#define NM_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager))
-#define NM_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
-#define NM_IS_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER))
-#define NM_IS_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ_MANAGER))
-#define NM_BLUEZ_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+#if WITH_BLUEZ5_DUN
+#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_DUN
+#else
+#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_NONE
+#endif
+#define _NM_BT_CAPABILITY_SUPPORTED (NM_BT_CAPABILITY_NAP | _NM_BT_CAPABILITY_SUPPORTED_DUN)
+
+typedef struct {
+ const char *bdaddr;
+ CList lst_head;
+ NMBluetoothCapabilities bt_type:8;
+ char bdaddr_data[];
+} ConnDataHead;
+
+typedef struct {
+ NMSettingsConnection *sett_conn;
+ ConnDataHead *cdata_hd;
+ CList lst;
+} ConnDataElem;
+
+typedef struct {
+ GCancellable *ext_cancellable;
+ GCancellable *int_cancellable;
+ NMBtVTableRegisterCallback callback;
+ gpointer callback_user_data;
+ gulong ext_cancelled_id;
+} NetworkServerRegisterReqData;
typedef struct {
+ GCancellable *ext_cancellable;
+ GCancellable *int_cancellable;
+ NMBluezManagerConnectCb callback;
+ gpointer callback_user_data;
+ char *device_name;
+ gulong ext_cancelled_id;
+ guint timeout_id;
+ guint timeout_wait_connect_id;
+} DeviceConnectReqData;
+typedef struct {
+ const char *object_path;
+
+ NMBluezManager *self;
+
+ /* Fields name with "d_" prefix are purely cached values from BlueZ's
+ * ObjectManager D-Bus interface. There is no logic whatsoever about
+ * them.
+ */
+
+ CList process_change_lst;
+
+ struct {
+ char *address;
+ } d_adapter;
+
+ struct {
+ char *address;
+ char *name;
+ char *adapter;
+ } d_device;
+
+ struct {
+ char *interface;
+ } d_network;
+
+ struct {
+ CList lst;
+ char *adapter_address;
+ NMDevice *device_br;
+ NetworkServerRegisterReqData *r_req_data;
+ } x_network_server;
+
+ struct {
+ NMSettingsConnection *panu_connection;
+ NMDeviceBt *device_bt;
+ DeviceConnectReqData *c_req_data;
+ NMBluez5DunContext *connect_dun_context;
+ gulong device_bt_signal_id;
+ } x_device;
+
+ /* indicate whether the D-Bus object has the particular D-Bus interface. */
+ bool d_has_adapter_iface:1;
+ bool d_has_device_iface:1;
+ bool d_has_network_iface:1;
+ bool d_has_network_server_iface:1;
+
+ /* cached D-Bus properties for Device1 ("d_device*"). */
+ NMBluetoothCapabilities d_device_capabilities:6;
+ bool d_device_connected:1;
+ bool d_device_paired:1;
+
+ /* cached D-Bus properties for Network1 ("d_network*"). */
+ bool d_network_connected:1;
+
+ /* cached D-Bus properties for Adapter1 ("d_adapter*"). */
+ bool d_adapter_powered:1;
+
+ /* properties related to device ("x_device*"). */
+ NMBluetoothCapabilities x_device_connect_bt_type:6;
+ bool x_device_is_usable:1;
+ bool x_device_is_connected:1;
+
+ bool x_device_panu_connection_allow_create:1;
+
+ /* flag to remember last time when we checked wether the object
+ * was a suitable adapter that is usable to a device. */
+ bool was_usable_adapter_for_device_before:1;
+
+ char _object_path_intern[];
+} BzDBusObj;
+
+typedef struct {
+ NMManager *manager;
NMSettings *settings;
- NMBluez5Manager *manager5;
- guint watch_name_id;
+ GDBusConnection *dbus_connection;
+
+ NMBtVTableNetworkServer vtable_network_server;
+
+ GCancellable *name_owner_get_cancellable;
+ GCancellable *get_managed_objects_cancellable;
+
+ GHashTable *bzobjs;
+
+ char *name_owner;
+
+ GHashTable *conn_data_heads;
+ GHashTable *conn_data_elems;
+
+ CList network_server_lst_head;
- GDBusProxy *introspect_proxy;
- GCancellable *async_cancellable;
+ CList process_change_lst_head;
+
+ guint name_owner_changed_id;
+
+ guint managed_objects_changed_id;
+
+ guint properties_changed_id;
+
+ guint process_change_idle_id;
+
+ bool settings_registered:1;
} NMBluezManagerPrivate;
-typedef struct {
+struct _NMBluezManager {
NMDeviceFactory parent;
NMBluezManagerPrivate _priv;
-} NMBluezManager;
+};
-typedef struct {
+struct _NMBluezManagerClass {
NMDeviceFactoryClass parent;
-} NMBluezManagerClass;
-
-static GType nm_bluez_manager_get_type (void);
+};
G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, NM_TYPE_DEVICE_FACTORY);
@@ -77,256 +206,2583 @@ nm_device_factory_create (GError **error)
/*****************************************************************************/
-static void check_bluez_and_try_setup (NMBluezManager *self);
+static NMBluetoothCapabilities
+convert_uuids_to_capabilities (const char *const*strv)
+{
+ NMBluetoothCapabilities capabilities = NM_BT_CAPABILITY_NONE;
+
+ if (strv) {
+ for (; strv[0]; strv++) {
+ gs_free char *s_part1 = NULL;
+ const char *str = strv[0];
+ const char *s;
+
+ s = strchr (str, '-');
+ if (!s)
+ continue;
+
+ s_part1 = g_strndup (str, s - str);
+ switch (g_ascii_strtoull (s_part1, NULL, 16)) {
+ case 0x1103:
+ capabilities |= NM_BT_CAPABILITY_DUN;
+ break;
+ case 0x1116:
+ capabilities |= NM_BT_CAPABILITY_NAP;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return capabilities;
+}
+
+/*****************************************************************************/
+
+static void _cleanup_for_name_owner (NMBluezManager *self);
+static void _connect_disconnect (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *reason);
+static gboolean _bzobjs_network_server_is_usable (const BzDBusObj *bzobj,
+ gboolean require_powered);
+static gboolean _bzobjs_is_dead (const BzDBusObj *bzobj);
+static gboolean _bzobjs_device_is_usable (const BzDBusObj *bzobj,
+ BzDBusObj **out_adapter_bzobj,
+ gboolean *out_create_panu_connection);
+static gboolean _bzobjs_adapter_is_usable_for_device (const BzDBusObj *bzobj);
+static ConnDataHead *_conn_track_find_head (NMBluezManager *self,
+ NMBluetoothCapabilities bt_type,
+ const char *bdaddr);
+static void _process_change_idle_schedule (NMBluezManager *self,
+ BzDBusObj *bzobj);
+static void _network_server_unregister_bridge (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *reason);
+static gboolean _connect_timeout_wait_connected_cb (gpointer user_data);
+
+/*****************************************************************************/
+
+static void
+_dbus_call_complete_cb_nop (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ /* we don't do anything at all. The only reason to register this
+ * callback is so that GDBusConnection keeps the cancellable alive
+ * long enough until the call completes.
+ *
+ * Note that this cancellable in turn is registered via
+ * nm_shutdown_wait_obj_register_*(), to block shutdown until
+ * we are done. */
+}
+
+/*****************************************************************************/
+
+static void
+_network_server_register_req_data_complete (NetworkServerRegisterReqData *r_req_data,
+ GError *error)
+{
+ nm_clear_g_signal_handler (r_req_data->ext_cancellable, &r_req_data->ext_cancelled_id);
+
+ nm_clear_g_cancellable (&r_req_data->int_cancellable);
+
+ if (r_req_data->callback) {
+ gs_free GError *error_cancelled = NULL;
+
+ if (g_cancellable_set_error_if_cancelled (r_req_data->ext_cancellable, &error_cancelled))
+ error = error_cancelled;
+
+ r_req_data->callback (error, r_req_data->callback_user_data);
+ }
+
+ g_object_unref (r_req_data->ext_cancellable);
+ nm_g_slice_free (r_req_data);
+}
+
+static void
+_device_connect_req_data_complete (DeviceConnectReqData *c_req_data,
+ NMBluezManager *self,
+ const char *device_name,
+ GError *error)
+{
+ nm_assert ((!!device_name) != (!!error));
+
+ nm_clear_g_signal_handler (c_req_data->ext_cancellable, &c_req_data->ext_cancelled_id);
+
+ nm_clear_g_cancellable (&c_req_data->int_cancellable);
+ nm_clear_g_source (&c_req_data->timeout_id);
+ nm_clear_g_source (&c_req_data->timeout_wait_connect_id);
+
+ if (c_req_data->callback) {
+ gs_free GError *error_cancelled = NULL;
+
+ if (g_cancellable_set_error_if_cancelled (c_req_data->ext_cancellable, &error_cancelled)) {
+ error = error_cancelled;
+ device_name = NULL;
+ }
+
+ c_req_data->callback (self, TRUE, device_name, error, c_req_data->callback_user_data);
+ }
+
+ g_object_unref (c_req_data->ext_cancellable);
+ nm_clear_g_free (&c_req_data->device_name);
+ nm_g_slice_free (c_req_data);
+}
/*****************************************************************************/
-struct AsyncData {
+static BzDBusObj *
+_bz_dbus_obj_new (NMBluezManager *self,
+ const char *object_path)
+{
+ BzDBusObj *bzobj;
+ gsize l;
+
+ nm_assert (NM_IS_BLUEZ_MANAGER (self));
+
+ l = strlen (object_path) + 1;
+
+ bzobj = g_malloc (sizeof (BzDBusObj) + l);
+ *bzobj = (BzDBusObj) {
+ .object_path = bzobj->_object_path_intern,
+ .self = self,
+ .x_network_server.lst = C_LIST_INIT (bzobj->x_network_server.lst),
+ .process_change_lst = C_LIST_INIT (bzobj->process_change_lst),
+ .x_device_panu_connection_allow_create = TRUE,
+ };
+ memcpy (bzobj->_object_path_intern, object_path, l);
+
+ return bzobj;
+}
+
+static void
+_bz_dbus_obj_free (BzDBusObj *bzobj)
+{
+ nm_assert (bzobj);
+ nm_assert (NM_IS_BLUEZ_MANAGER (bzobj->self));
+ nm_assert (!bzobj->x_network_server.device_br);
+ nm_assert (!bzobj->x_network_server.r_req_data);
+ nm_assert (!bzobj->x_device.c_req_data);
+
+ c_list_unlink_stale (&bzobj->process_change_lst);
+ c_list_unlink_stale (&bzobj->x_network_server.lst);
+ g_free (bzobj->x_network_server.adapter_address);
+ g_free (bzobj->d_adapter.address);
+ g_free (bzobj->d_network.interface);
+ g_free (bzobj->d_device.address);
+ g_free (bzobj->d_device.name);
+ g_free (bzobj->d_device.adapter);
+ g_free (bzobj);
+}
+
+/*****************************************************************************/
+
+static const char *
+_bzobj_to_string (const BzDBusObj *bzobj, char *buf, gsize len)
+{
+ char *buf0 = buf;
+ const char *prefix = "";
+ gboolean device_is_usable;
+ gboolean create_panu_connection = FALSE;
+ gboolean network_server_is_usable;
+ char sbuf_cap[100];
+
+ if (len > 0)
+ buf[0] = '\0';
+
+ if (bzobj->d_has_adapter_iface) {
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append_str (&buf, &len, "Adapter1 {");
+ if (bzobj->d_adapter.address) {
+ nm_utils_strbuf_append (&buf, &len, " d.address: \"%s\"", bzobj->d_adapter.address);
+ if (bzobj->d_adapter_powered)
+ nm_utils_strbuf_append_str (&buf, &len, ",");
+ }
+ if (bzobj->d_adapter_powered)
+ nm_utils_strbuf_append (&buf, &len, " d.powered: 1");
+ nm_utils_strbuf_append_str (&buf, &len, " }");
+ }
+
+ if (bzobj->d_has_device_iface) {
+ const char *prefix1 = "";
+
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append_str (&buf, &len, "Device1 {");
+ if (bzobj->d_device.address) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.address: \"%s\"", prefix1, bzobj->d_device.address);
+ prefix1 = ",";
+ }
+ if (bzobj->d_device.name) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.name: \"%s\"", prefix1, bzobj->d_device.name);
+ prefix1 = ",";
+ }
+ if (bzobj->d_device.adapter) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.adapter: \"%s\"", prefix1, bzobj->d_device.adapter);
+ prefix1 = ",";
+ }
+ if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.capabilities: \"%s\"",
+ prefix1,
+ nm_bluetooth_capability_to_string (bzobj->d_device_capabilities, sbuf_cap, sizeof (sbuf_cap)));
+ prefix1 = ",";
+ }
+ if (bzobj->d_device_connected) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.connected: 1", prefix1);
+ prefix1 = ",";
+ }
+ if (bzobj->d_device_paired) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.paired: 1", prefix1);
+ prefix1 = ",";
+ }
+ nm_utils_strbuf_append_str (&buf, &len, " }");
+ }
+
+ network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
+
+ if ( bzobj->d_has_network_server_iface
+ || network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst))
+ || !c_list_is_empty (&bzobj->x_network_server.lst)
+ || !nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL, bzobj->x_network_server.adapter_address)
+ || bzobj->x_network_server.device_br
+ || bzobj->x_network_server.r_req_data) {
+
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+
+ nm_utils_strbuf_append (&buf, &len, "NetworkServer1 { ");
+
+ if (!bzobj->d_has_network_server_iface)
+ nm_utils_strbuf_append (&buf, &len, " has-d-iface: 0, ");
+
+ if (network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst)))
+ nm_utils_strbuf_append (&buf, &len, "usable: %d, used: %d", !!network_server_is_usable, !network_server_is_usable);
+ else if (network_server_is_usable)
+ nm_utils_strbuf_append (&buf, &len, "used: 1");
+ else
+ nm_utils_strbuf_append (&buf, &len, "usable: 0");
+
+ if (!nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL, bzobj->x_network_server.adapter_address)) {
+ if (bzobj->x_network_server.adapter_address)
+ nm_utils_strbuf_append (&buf, &len, ", adapter-address: \"%s\"", bzobj->x_network_server.adapter_address);
+ else
+ nm_utils_strbuf_append (&buf, &len, ", adapter-address: <NULL>");
+ }
+
+ if (bzobj->x_network_server.device_br)
+ nm_utils_strbuf_append (&buf, &len, ", bridge-device: 1");
+
+ if (bzobj->x_network_server.r_req_data)
+ nm_utils_strbuf_append (&buf, &len, ", register-in-progress: 1");
+
+ nm_utils_strbuf_append_str (&buf, &len, " }");
+ }
+
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, &create_panu_connection);
+
+ if ( bzobj->d_has_network_iface
+ || bzobj->d_network.interface
+ || bzobj->d_network_connected
+ || create_panu_connection
+ || bzobj->x_device.panu_connection
+ || device_is_usable != bzobj->x_device_is_usable
+ || bzobj->x_device.device_bt
+ || bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
+ || bzobj->x_device.connect_dun_context
+ || bzobj->x_device.c_req_data
+ || bzobj->x_device_is_connected != bzobj->d_network_connected) {
+
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append_str (&buf, &len, "Network1 {");
+ if (bzobj->d_network.interface)
+ nm_utils_strbuf_append (&buf, &len, " d.interface: \"%s\", ", bzobj->d_network.interface);
+ if (bzobj->d_network_connected)
+ nm_utils_strbuf_append (&buf, &len, " d.connected: %d, ", !!bzobj->d_network_connected);
+ if (!bzobj->d_has_network_iface)
+ nm_utils_strbuf_append (&buf, &len, " has-d-iface: 0, ");
+ if (device_is_usable != bzobj->x_device_is_usable)
+ nm_utils_strbuf_append (&buf, &len, " usable: %d, used: %d", !!device_is_usable, !device_is_usable);
+ else if (device_is_usable)
+ nm_utils_strbuf_append (&buf, &len, " used: 1");
+ else
+ nm_utils_strbuf_append (&buf, &len, " usable: 0");
+
+ if (create_panu_connection)
+ nm_utils_strbuf_append (&buf, &len, ", create-panu-connection: 1");
+
+ if (bzobj->x_device.panu_connection)
+ nm_utils_strbuf_append (&buf, &len, ", has-panu-connection: 1");
+
+ if (bzobj->x_device.device_bt)
+ nm_utils_strbuf_append (&buf, &len, ", has-device: 1");
+
+ if ( bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
+ || bzobj->x_device.connect_dun_context) {
+ nm_utils_strbuf_append (&buf, &len, ", connect: %s%s",
+ nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->x_device.connect_dun_context ? ",with-dun-context" : "");
+ }
+
+ if (bzobj->x_device.c_req_data)
+ nm_utils_strbuf_append (&buf, &len, ", connecting: 1");
+
+ if (bzobj->x_device_is_connected != bzobj->d_network_connected)
+ nm_utils_strbuf_append (&buf, &len, ", connected: %d", !!bzobj->x_device_is_connected);
+
+ nm_utils_strbuf_append_str (&buf, &len, " }");
+ }
+
+ if (_bzobjs_is_dead (bzobj)) {
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append_str (&buf, &len, "dead: 1");
+ }
+
+ if (!c_list_is_empty (&bzobj->process_change_lst)) {
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append (&buf, &len, "change-pending-on-idle: 1");
+ }
+
+ if (_bzobjs_adapter_is_usable_for_device (bzobj) != bzobj->was_usable_adapter_for_device_before) {
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append (&buf, &len, "change-usable-adapter-for-device: 1");
+ }
+
+ return buf0;
+}
+
+#define _LOG_bzobj(bzobj, context) \
+ G_STMT_START { \
+ const BzDBusObj *const _bzobj = (bzobj); \
+ char _buf[500]; \
+ \
+ _LOGT ("change %-21s %s : { %s }", \
+ (context), \
+ _bzobj->object_path, \
+ _bzobj_to_string (_bzobj, _buf, sizeof (_buf))); \
+ } G_STMT_END
+
+static gboolean
+_bzobjs_is_dead (const BzDBusObj *bzobj)
+{
+ return !bzobj->d_has_adapter_iface
+ && !bzobj->d_has_device_iface
+ && !bzobj->d_has_network_iface
+ && !bzobj->d_has_network_server_iface
+ && c_list_is_empty (&bzobj->process_change_lst);
+}
+
+static BzDBusObj *
+_bzobjs_get (NMBluezManager *self, const char *object_path)
+{
+ return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->bzobjs, &object_path);
+}
+
+static BzDBusObj *
+_bzobjs_add (NMBluezManager *self,
+ const char *object_path)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ BzDBusObj *bzobj;
+
+ bzobj = _bz_dbus_obj_new (self, object_path);
+ if (!g_hash_table_add (priv->bzobjs, bzobj))
+ nm_assert_not_reached ();
+ return bzobj;
+}
+
+static void
+_bzobjs_del (BzDBusObj *bzobj)
+{
+ nm_assert (bzobj);
+ nm_assert (bzobj == _bzobjs_get (bzobj->self, bzobj->object_path));
+
+ if (!g_hash_table_remove (NM_BLUEZ_MANAGER_GET_PRIVATE (bzobj->self)->bzobjs, bzobj))
+ nm_assert_not_reached ();
+}
+
+static void
+_bzobjs_del_if_dead (BzDBusObj *bzobj)
+{
+ if (_bzobjs_is_dead (bzobj))
+ _bzobjs_del (bzobj);
+}
+
+static BzDBusObj *
+_bzobjs_init (NMBluezManager *self, BzDBusObj **inout, const char *object_path)
+{
+ nm_assert (NM_IS_BLUEZ_MANAGER (self));
+ nm_assert (object_path);
+ nm_assert (inout);
+
+ if (!*inout) {
+ *inout = _bzobjs_get (self, object_path);
+ if (!*inout)
+ *inout = _bzobjs_add (self, object_path);
+ }
+
+ nm_assert (nm_streq ((*inout)->object_path, object_path));
+ nm_assert (*inout == _bzobjs_get (self, object_path));
+ return *inout;
+}
+
+static gboolean
+_bzobjs_adapter_is_usable_for_device (const BzDBusObj *bzobj)
+{
+ return bzobj->d_has_adapter_iface
+ && bzobj->d_adapter.address
+ && bzobj->d_adapter_powered;
+}
+
+static gboolean
+_bzobjs_device_is_usable (const BzDBusObj *bzobj,
+ BzDBusObj **out_adapter_bzobj,
+ gboolean *out_create_panu_connection)
+{
NMBluezManager *self;
- GCancellable *async_cancellable;
-};
+ NMBluezManagerPrivate *priv;
+ gboolean usable_dun = FALSE;
+ gboolean usable_nap = FALSE;
+ BzDBusObj *bzobj_adapter;
+ gboolean create_panu_connection = FALSE;
+
+ if ( !bzobj->d_has_device_iface
+ || !NM_FLAGS_ANY ((NMBluetoothCapabilities) bzobj->d_device_capabilities, _NM_BT_CAPABILITY_SUPPORTED)
+ || !bzobj->d_device.name
+ || !bzobj->d_device.address
+ || !bzobj->d_device_paired
+ || !bzobj->d_device.adapter)
+ goto out_unusable;
+
+ self = bzobj->self;
+
+ priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ if (!priv->settings_registered)
+ goto out_unusable;
+
+ bzobj_adapter = _bzobjs_get (self, bzobj->d_device.adapter);
+ if ( !bzobj_adapter
+ || !_bzobjs_adapter_is_usable_for_device (bzobj_adapter))
+ goto out_unusable;
-static struct AsyncData *
-async_data_pack (NMBluezManager *self)
+#if WITH_BLUEZ5_DUN
+ if (NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_DUN)) {
+ if (_conn_track_find_head (self, NM_BT_CAPABILITY_DUN, bzobj->d_device.address))
+ usable_dun = TRUE;
+ }
+#endif
+
+ if (NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_NAP)) {
+ if (!bzobj->d_has_network_iface)
+ usable_nap = FALSE;
+ else if (_conn_track_find_head (self, NM_BT_CAPABILITY_NAP, bzobj->d_device.address))
+ usable_nap = TRUE;
+ else if (bzobj->x_device_panu_connection_allow_create) {
+ /* We didn't yet try to create a connection. Presume we are going to create
+ * it when the time comes... */
+ usable_nap = TRUE;
+ create_panu_connection = TRUE;
+ }
+ }
+
+ if ( !usable_dun
+ && !usable_nap) {
+ if ( bzobj->x_device.device_bt
+ && nm_device_get_state (NM_DEVICE (bzobj->x_device.device_bt)) > NM_DEVICE_STATE_DISCONNECTED) {
+ /* The device is still activated... the absence of a profile does not
+ * render it unusable (yet). But since there is no more profile, the
+ * device is probably about to disconnect. */
+ } else
+ goto out_unusable;
+ }
+
+ NM_SET_OUT (out_create_panu_connection, create_panu_connection);
+ NM_SET_OUT (out_adapter_bzobj, bzobj_adapter);
+ return TRUE;
+
+out_unusable:
+ NM_SET_OUT (out_create_panu_connection, FALSE);
+ NM_SET_OUT (out_adapter_bzobj, NULL);
+ return FALSE;
+}
+
+static gboolean
+_bzobjs_device_is_connected (const BzDBusObj *bzobj)
+{
+ nm_assert (_bzobjs_device_is_usable (bzobj, NULL, NULL));
+
+ if ( !bzobj->d_has_device_iface
+ || !bzobj->d_device_connected)
+ return FALSE;
+
+ if ( bzobj->d_has_network_iface
+ && bzobj->d_network_connected)
+ return TRUE;
+ if (bzobj->x_device.connect_dun_context) {
+ /* As long as we have a dun-context, we consider it connected.
+ *
+ * We require NMDeviceBt to try to connect to the modem, and if that fails,
+ * it will disconnect. */
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+_bzobjs_network_server_is_usable (const BzDBusObj *bzobj,
+ gboolean require_powered)
+{
+ return bzobj->d_has_network_server_iface
+ && bzobj->d_has_adapter_iface
+ && bzobj->d_adapter.address
+ && ( !require_powered
+ || bzobj->d_adapter_powered);
+}
+
+/*****************************************************************************/
+
+static ConnDataHead *
+_conn_data_head_new (NMBluetoothCapabilities bt_type,
+ const char *bdaddr)
+{
+ ConnDataHead *cdata_hd;
+ gsize l;
+
+ nm_assert (NM_IN_SET (bt_type, NM_BT_CAPABILITY_DUN,
+ NM_BT_CAPABILITY_NAP));
+ nm_assert (bdaddr);
+
+ l = strlen (bdaddr) + 1;
+ cdata_hd = g_malloc (sizeof (ConnDataHead) + l);
+ *cdata_hd = (ConnDataHead) {
+ .bdaddr = cdata_hd->bdaddr_data,
+ .lst_head = C_LIST_INIT (cdata_hd->lst_head),
+ .bt_type = bt_type,
+ };
+ memcpy (cdata_hd->bdaddr_data, bdaddr, l);
+
+ nm_assert (cdata_hd->bt_type == bt_type);
+
+ return cdata_hd;
+}
+
+static guint
+_conn_data_head_hash (gconstpointer ptr)
+{
+ const ConnDataHead *cdata_hd = ptr;
+ NMHashState h;
+
+ nm_hash_init (&h, 520317467u);
+ nm_hash_update_val (&h, (NMBluetoothCapabilities) cdata_hd->bt_type);
+ nm_hash_update_str (&h, cdata_hd->bdaddr);
+ return nm_hash_complete (&h);
+}
+
+static gboolean
+_conn_data_head_equal (gconstpointer a, gconstpointer b)
+{
+ const ConnDataHead *cdata_hd_a = a;
+ const ConnDataHead *cdata_hd_b = b;
+
+ return cdata_hd_a->bt_type == cdata_hd_b->bt_type
+ && nm_streq (cdata_hd_a->bdaddr, cdata_hd_b->bdaddr);
+}
+
+static ConnDataHead *
+_conn_track_find_head (NMBluezManager *self,
+ NMBluetoothCapabilities bt_type,
+ const char *bdaddr)
+{
+ ConnDataHead cdata_hd = {
+ .bt_type = bt_type,
+ .bdaddr = bdaddr,
+ };
+
+ return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->conn_data_heads, &cdata_hd);
+}
+
+static ConnDataElem *
+_conn_track_find_elem (NMBluezManager *self,
+ NMSettingsConnection *sett_conn)
+{
+ G_STATIC_ASSERT (G_STRUCT_OFFSET (ConnDataElem, sett_conn) == 0);
+
+ return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->conn_data_elems, &sett_conn);
+}
+
+static gboolean
+_conn_track_is_relevant_connection (NMConnection *connection,
+ NMBluetoothCapabilities *out_bt_type,
+ const char **out_bdaddr)
+{
+ NMSettingBluetooth *s_bt;
+ NMBluetoothCapabilities bt_type;
+ const char *bdaddr;
+ const char *b_type;
+
+ s_bt = nm_connection_get_setting_bluetooth (connection);
+ if (!s_bt)
+ return FALSE;
+
+ if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
+ return FALSE;
+
+ bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
+ if (!bdaddr)
+ return FALSE;
+
+ b_type = nm_setting_bluetooth_get_connection_type (s_bt);
+
+ if (nm_streq (b_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+ bt_type = NM_BT_CAPABILITY_DUN;
+ else if (nm_streq (b_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+ bt_type = NM_BT_CAPABILITY_NAP;
+ else
+ return FALSE;
+
+ NM_SET_OUT (out_bt_type, bt_type);
+ NM_SET_OUT (out_bdaddr, bdaddr);
+ return TRUE;
+}
+
+static gboolean
+_conn_track_is_relevant_sett_conn (NMSettingsConnection *sett_conn,
+ NMBluetoothCapabilities *out_bt_type,
+ const char **out_bdaddr)
+{
+ NMConnection *connection;
+
+ connection = nm_settings_connection_get_connection (sett_conn);
+ if (!connection)
+ return FALSE;
+
+ return _conn_track_is_relevant_connection (connection, out_bt_type, out_bdaddr);
+}
+
+static gboolean
+_conn_track_is_relevant_for_sett_conn (NMSettingsConnection *sett_conn,
+ NMBluetoothCapabilities bt_type,
+ const char *bdaddr)
+{
+ NMBluetoothCapabilities x_bt_type;
+ const char *x_bdaddr;
+
+ return bdaddr
+ && _conn_track_is_relevant_sett_conn (sett_conn, &x_bt_type, &x_bdaddr)
+ && x_bt_type == bt_type
+ && nm_streq (x_bdaddr, bdaddr);
+}
+
+static void
+_conn_track_schedule_notify (NMBluezManager *self,
+ NMBluetoothCapabilities bt_type,
+ const char *bdaddr)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ GHashTableIter iter;
+ BzDBusObj *bzobj;
+
+ g_hash_table_iter_init (&iter, priv->bzobjs);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+ gboolean device_is_usable;
+
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+ if (bzobj->x_device_is_usable != device_is_usable)
+ _process_change_idle_schedule (self, bzobj);
+ }
+}
+
+static void
+_conn_track_update (NMBluezManager *self,
+ NMSettingsConnection *sett_conn,
+ gboolean track,
+ gboolean *out_changed,
+ gboolean *out_changed_usable,
+ ConnDataElem **out_conn_data_elem)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ ConnDataHead *cdata_hd;
+ ConnDataElem *cdata_el;
+ ConnDataElem *cdata_el_remove = NULL;
+ NMBluetoothCapabilities bt_type;
+ const char *bdaddr;
+ gboolean changed = FALSE;
+ gboolean changed_usable = FALSE;
+ char sbuf_cap[100];
+
+ nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+
+ cdata_el = _conn_track_find_elem (self, sett_conn);
+
+ if (track)
+ track = _conn_track_is_relevant_sett_conn (sett_conn, &bt_type, &bdaddr);
+
+ if (!track) {
+ cdata_el_remove = g_steal_pointer (&cdata_el);
+ goto out_remove;
+ }
+
+ if (cdata_el) {
+ cdata_hd = cdata_el->cdata_hd;
+ if ( cdata_hd->bt_type != bt_type
+ || !nm_streq (cdata_hd->bdaddr, bdaddr))
+ cdata_el_remove = g_steal_pointer (&cdata_el);
+ }
+
+ if (!cdata_el) {
+ _LOGT ("connecton: track for %s, %s: %s (%s)",
+ nm_bluetooth_capability_to_string (bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bdaddr,
+ nm_settings_connection_get_uuid (sett_conn),
+ nm_settings_connection_get_id (sett_conn));
+ changed = TRUE;
+ cdata_hd = _conn_track_find_head (self, bt_type, bdaddr);
+ if (!cdata_hd) {
+ changed_usable = TRUE;
+ cdata_hd = _conn_data_head_new (bt_type, bdaddr);
+ if (!g_hash_table_add (priv->conn_data_heads, cdata_hd))
+ nm_assert_not_reached ();
+ _conn_track_schedule_notify (self, bt_type, bdaddr);
+ }
+ cdata_el = g_slice_new (ConnDataElem);
+ cdata_el->sett_conn = sett_conn;
+ cdata_el->cdata_hd = cdata_hd;
+ c_list_link_tail (&cdata_hd->lst_head, &cdata_el->lst);
+ if (!g_hash_table_add (priv->conn_data_elems, cdata_el))
+ nm_assert_not_reached ();
+ }
+
+out_remove:
+ if (cdata_el_remove) {
+ GHashTableIter iter;
+ BzDBusObj *bzobj;
+
+ _LOGT ("connecton: untrack for %s, %s: %s (%s)",
+ nm_bluetooth_capability_to_string (cdata_el_remove->cdata_hd->bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ cdata_el_remove->cdata_hd->bdaddr,
+ nm_settings_connection_get_uuid (sett_conn),
+ nm_settings_connection_get_id (sett_conn));
+
+ g_hash_table_iter_init (&iter, priv->bzobjs);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+ if (bzobj->x_device.panu_connection == sett_conn)
+ bzobj->x_device.panu_connection = NULL;
+ }
+
+ changed = TRUE;
+ cdata_hd = cdata_el_remove->cdata_hd;
+ c_list_unlink_stale (&cdata_el_remove->lst);
+ if (!g_hash_table_remove (priv->conn_data_elems, cdata_el_remove))
+ nm_assert_not_reached ();
+ if (c_list_is_empty (&cdata_hd->lst_head)) {
+ changed_usable = TRUE;
+ _conn_track_schedule_notify (self, cdata_hd->bt_type, cdata_hd->bdaddr);
+ if (!g_hash_table_remove (priv->conn_data_heads, cdata_hd))
+ nm_assert_not_reached ();
+ }
+ }
+
+ NM_SET_OUT (out_changed, changed);
+ NM_SET_OUT (out_changed_usable, changed_usable);
+ NM_SET_OUT (out_conn_data_elem, cdata_el);
+}
+
+/*****************************************************************************/
+
+static void
+cp_connection_added (NMSettings *settings,
+ NMSettingsConnection *sett_conn,
+ NMBluezManager *self)
{
- struct AsyncData *data = g_new (struct AsyncData, 1);
+ _conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL);
+}
- data->self = self;
- data->async_cancellable = g_object_ref (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->async_cancellable);
- return data;
+static void
+cp_connection_updated (NMSettings *settings,
+ NMSettingsConnection *sett_conn,
+ guint update_reason_u,
+ NMBluezManager *self)
+{
+ _conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL);
+}
+
+static void
+cp_connection_removed (NMSettings *settings,
+ NMSettingsConnection *sett_conn,
+ NMBluezManager *self)
+{
+ _conn_track_update (self, sett_conn, FALSE, NULL, NULL, NULL);
}
+/*****************************************************************************/
+
static NMBluezManager *
-async_data_unpack (struct AsyncData *async_data)
+_network_server_get_bluez_manager (const NMBtVTableNetworkServer *vtable_network_server)
{
- NMBluezManager *self = g_cancellable_is_cancelled (async_data->async_cancellable)
- ? NULL : async_data->self;
+ NMBluezManager *self;
+
+ self = (NMBluezManager *) (((char *) vtable_network_server) - G_STRUCT_OFFSET (NMBluezManager, _priv.vtable_network_server));
+
+ g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (self), NULL);
- g_object_unref (async_data->async_cancellable);
- g_free (async_data);
return self;
}
-/**
- * Cancel any current attempt to detect the version and cleanup
- * the related fields.
- **/
+static BzDBusObj *
+_network_server_find_has_device (NMBluezManagerPrivate *priv,
+ NMDevice *device)
+{
+ BzDBusObj *bzobj;
+
+ c_list_for_each_entry (bzobj, &priv->network_server_lst_head, x_network_server.lst) {
+ if (bzobj->x_network_server.device_br == device)
+ return bzobj;
+ }
+ return NULL;
+}
+
+static BzDBusObj *
+_network_server_find_available (NMBluezManagerPrivate *priv,
+ const char *addr,
+ NMDevice *device_accept_busy)
+{
+ BzDBusObj *bzobj;
+
+ c_list_for_each_entry (bzobj, &priv->network_server_lst_head, x_network_server.lst) {
+ if (bzobj->x_network_server.device_br) {
+ if (bzobj->x_network_server.device_br != device_accept_busy)
+ continue;
+ }
+ if ( addr
+ && !nm_streq (addr, bzobj->d_adapter.address))
+ continue;
+ nm_assert (!bzobj->x_network_server.r_req_data);
+ return bzobj;
+ }
+ return NULL;
+}
+
+static gboolean
+_network_server_vt_is_available (const NMBtVTableNetworkServer *vtable,
+ const char *addr,
+ NMDevice *device_accept_busy)
+{
+ NMBluezManager *self = _network_server_get_bluez_manager (vtable);
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ return !!_network_server_find_available (priv, addr, device_accept_busy);
+}
+
static void
-cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name)
+_network_server_register_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
+ gs_unref_variant GVariant *ret = NULL;
+ gs_free_error GError *error = NULL;
+ BzDBusObj *bzobj;
+
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+ if ( !ret
+ && nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ bzobj = user_data;
+
+ if (!ret) {
+ _LOGT ("NAP: [%s]: registering failed: %s", bzobj->object_path, error->message);
+ } else
+ _LOGT ("NAP: [%s]: registration successful", bzobj->object_path);
+
+ g_clear_object (&bzobj->x_network_server.r_req_data->int_cancellable);
+ _network_server_register_req_data_complete (g_steal_pointer (&bzobj->x_network_server.r_req_data), error);
+}
+
+static void
+_network_server_register_cancelled_cb (GCancellable *cancellable,
+ BzDBusObj *bzobj)
+{
+ _network_server_unregister_bridge (bzobj->self, bzobj, "registration cancelled");
+}
+
+static gboolean
+_network_server_vt_register_bridge (const NMBtVTableNetworkServer *vtable,
+ const char *addr,
+ NMDevice *device,
+ GCancellable *cancellable,
+ NMBtVTableRegisterCallback callback,
+ gpointer callback_user_data,
+ GError **error)
+{
+ NMBluezManager *self = _network_server_get_bluez_manager (vtable);
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ NetworkServerRegisterReqData *r_req_data;
+ BzDBusObj *bzobj;
+ const char *ifname;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
+
+ nm_assert (!g_cancellable_is_cancelled (cancellable));
+ nm_assert (!_network_server_find_has_device (priv, device));
+
+ ifname = nm_device_get_iface (device);
+ g_return_val_if_fail (ifname, FALSE);
+
+ g_return_val_if_fail (ifname, FALSE);
+
+ bzobj = _network_server_find_available (priv, addr, NULL);
+ if (!bzobj) {
+ /* The device checked that a network server is available, before
+ * starting the activation, but for some reason it no longer is.
+ * Indicate that the activation should not proceed. */
+ if (addr) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "adapter %s is not available for %s",
+ addr, ifname);
+ } else {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "no adapter available for %s",
+ ifname);
+ }
+ return FALSE;
+ }
+
+ _LOGD ("NAP: [%s]: registering \"%s\" on adapter %s",
+ bzobj->object_path,
+ ifname,
+ bzobj->d_adapter.address);
+
+ r_req_data = g_slice_new (NetworkServerRegisterReqData);
+ *r_req_data = (NetworkServerRegisterReqData) {
+ .int_cancellable = g_cancellable_new (),
+ .ext_cancellable = g_object_ref (cancellable),
+ .callback = callback,
+ .callback_user_data = callback_user_data,
+ .ext_cancelled_id = g_signal_connect (cancellable,
+ "cancelled",
+ G_CALLBACK (_network_server_register_cancelled_cb),
+ bzobj),
+ };
+
+ bzobj->x_network_server.device_br = g_object_ref (device);
+ bzobj->x_network_server.r_req_data = r_req_data;
+
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
+ "Register",
+ g_variant_new ("(ss)",
+ BLUETOOTH_CONNECT_NAP,
+ ifname),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ bzobj->x_network_server.r_req_data->int_cancellable,
+ _network_server_register_cb,
+ bzobj);
+ return TRUE;
+}
+
+static void
+_network_server_unregister_bridge_complete_on_idle_cb (gpointer user_data,
+ GCancellable *cancellable)
+{
+ gs_free_error GError *error = NULL;
+ gs_free char *reason = NULL;
+ NetworkServerRegisterReqData *r_req_data;
+
+ nm_utils_user_data_unpack (user_data, &r_req_data, &reason);
+
+ nm_utils_error_set (&error, NM_UTILS_ERROR_UNKNOWN,
+ "registration was aborted due to %s",
+ reason);
+ _network_server_register_req_data_complete (r_req_data, error);
+}
+
+static void
+_network_server_unregister_bridge (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *reason)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ _nm_unused gs_unref_object NMDevice *device = NULL;
+ NetworkServerRegisterReqData *r_req_data;
+
+ nm_assert (NM_IS_DEVICE (bzobj->x_network_server.device_br));
+
+ _LOGD ("NAP: [%s]: unregistering \"%s\" (%s)",
+ bzobj->object_path,
+ nm_device_get_iface (bzobj->x_network_server.device_br),
+ reason);
+
+ device = g_steal_pointer (&bzobj->x_network_server.device_br);
+
+ r_req_data = g_steal_pointer (&bzobj->x_network_server.r_req_data);
+
+ if (priv->name_owner) {
+ gs_unref_object GCancellable *cancellable = NULL;
+
+ cancellable = g_cancellable_new ();
- nm_clear_g_cancellable (&priv->async_cancellable);
+ nm_shutdown_wait_obj_register_cancellable_full (cancellable,
+ g_strdup_printf ("bt-unregister-nap[%s]", bzobj->object_path),
+ TRUE);
- g_clear_object (&priv->introspect_proxy);
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
+ "Unregister",
+ g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ cancellable,
+ _dbus_call_complete_cb_nop,
+ NULL);
+ }
- if (do_unwatch_name && priv->watch_name_id) {
- g_bus_unwatch_name (priv->watch_name_id);
- priv->watch_name_id = 0;
+ if (r_req_data) {
+ nm_clear_g_cancellable (&r_req_data->int_cancellable);
+ nm_utils_invoke_on_idle (_network_server_unregister_bridge_complete_on_idle_cb,
+ nm_utils_user_data_pack (r_req_data, g_strdup (reason)),
+ r_req_data->ext_cancellable);
}
+
+ _nm_device_bridge_notify_unregister_bt_nap (device, reason);
+}
+
+static gboolean
+_network_server_vt_unregister_bridge (const NMBtVTableNetworkServer *vtable,
+ NMDevice *device)
+{
+ NMBluezManager *self = _network_server_get_bluez_manager (vtable);
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ BzDBusObj *bzobj;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+ bzobj = _network_server_find_has_device (priv, device);
+ if (bzobj)
+ _network_server_unregister_bridge (self, bzobj, "disconnecting");
+
+ return TRUE;
}
static void
-manager_bdaddr_added_cb (GObject *manager,
- NMBluezDevice *bt_device,
- const char *bdaddr,
- const char *name,
- const char *object_path,
- guint32 capabilities,
- gpointer user_data)
+_network_server_process_change (BzDBusObj *bzobj,
+ gboolean *out_emit_device_availability_changed)
{
- NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
- NMDevice *device;
- gboolean has_dun = (capabilities & NM_BT_CAPABILITY_DUN);
- gboolean has_nap = (capabilities & NM_BT_CAPABILITY_NAP);
+ NMBluezManager *self = bzobj->self;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gboolean network_server_is_usable;
+ gboolean emit_device_availability_changed = FALSE;
+
+ network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
+
+ if (!network_server_is_usable) {
+
+ if (!c_list_is_empty (&bzobj->x_network_server.lst)) {
+ emit_device_availability_changed = TRUE;
+ c_list_unlink (&bzobj->x_network_server.lst);
+ }
+
+ nm_clear_g_free (&bzobj->x_network_server.adapter_address);
+
+ if (bzobj->x_network_server.device_br) {
+ _network_server_unregister_bridge (self,
+ bzobj,
+ _bzobjs_network_server_is_usable (bzobj, FALSE)
+ ? "adapter disabled"
+ : "adapter disappeared");
+ }
+
+ } else {
+
+ if (!nm_streq0 (bzobj->x_network_server.adapter_address, bzobj->d_adapter.address)) {
+ emit_device_availability_changed = TRUE;
+ g_free (bzobj->x_network_server.adapter_address);
+ bzobj->x_network_server.adapter_address = g_strdup (bzobj->d_adapter.address);
+ }
- g_return_if_fail (bdaddr != NULL);
- g_return_if_fail (name != NULL);
- g_return_if_fail (object_path != NULL);
- g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE);
- g_return_if_fail (NM_IS_BLUEZ_DEVICE (bt_device));
+ if (c_list_is_empty (&bzobj->x_network_server.lst)) {
+ emit_device_availability_changed = TRUE;
+ c_list_link_tail (&priv->network_server_lst_head, &bzobj->x_network_server.lst);
+ }
- device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities);
- if (!device)
+ }
+
+ if (emit_device_availability_changed)
+ NM_SET_OUT (out_emit_device_availability_changed, TRUE);
+}
+
+/*****************************************************************************/
+
+static void
+_conn_create_panu_connection (NMBluezManager *self,
+ BzDBusObj *bzobj)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gs_unref_object NMConnection *connection = NULL;
+ NMSettingsConnection *added;
+ NMSetting *setting;
+ gs_free char *id = NULL;
+ char uuid[37];
+ gs_free_error GError *error = NULL;
+
+ nm_utils_uuid_generate_buf (uuid);
+ id = g_strdup_printf (_("%s Network"), bzobj->d_device.name);
+
+ connection = nm_simple_connection_new ();
+
+ setting = nm_setting_connection_new ();
+ g_object_set (setting,
+ NM_SETTING_CONNECTION_ID, id,
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ setting = nm_setting_bluetooth_new ();
+ g_object_set (setting,
+ NM_SETTING_BLUETOOTH_BDADDR, bzobj->d_device.address,
+ NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
+ _LOGE ("connection: couldn't generate a connection for NAP device: %s",
+ error->message);
+ g_return_if_reached ();
+ }
+
+ nm_assert (_conn_track_is_relevant_connection (connection, NULL, NULL));
+
+ _LOGT ("connection: create in-memory PANU connection %s (%s) for device \"%s\" (%s)",
+ uuid,
+ id,
+ bzobj->d_device.name,
+ bzobj->d_device.address);
+
+ nm_settings_add_connection (priv->settings,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &added,
+ &error);
+ if (!added) {
+ _LOGW ("connection: couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
+ id, uuid, error->message);
return;
+ }
+
+ if ( !_conn_track_is_relevant_for_sett_conn (added, NM_BT_CAPABILITY_NAP, bzobj->d_device.address)
+ || !_conn_track_find_elem (self, added)
+ || bzobj->x_device.panu_connection) {
+ _LOGE ("connection: something went wrong creating PANU connection %s (%s) for device '%s'",
+ uuid, id, bzobj->d_device.address);
+ g_return_if_reached ();
+ }
+
+ bzobj->x_device.panu_connection = added;
+}
+
+/*****************************************************************************/
+
+static void
+_device_state_changed_cb (NMDevice *device,
+ guint new_state_u,
+ guint old_state_u,
+ guint reason_u,
+ gpointer user_data)
+{
+ BzDBusObj *bzobj = user_data;
- _LOGI ("BT device %s (%s) added (%s%s%s)",
- name,
- bdaddr,
- has_dun ? "DUN" : "",
- has_dun && has_nap ? " " : "",
- has_nap ? "NAP" : "");
- g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
- g_object_unref (device);
+ if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) {
+ /* the device got unusable? Need to revisit it... */
+ _process_change_idle_schedule (bzobj->self, bzobj);
+ }
}
static void
-manager_network_server_added_cb (GObject *manager,
- gpointer user_data)
+_device_process_change (BzDBusObj *bzobj)
{
- nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (user_data), NULL);
+ NMBluezManager *self = bzobj->self;
+ gs_unref_object NMDeviceBt *device_added = NULL;
+ gs_unref_object NMDeviceBt *device_deleted = NULL;
+ gboolean device_is_usable;
+ gboolean create_panu_connection = FALSE;
+
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, &create_panu_connection);
+
+ if (create_panu_connection) {
+ bzobj->x_device_panu_connection_allow_create = FALSE;
+ _conn_create_panu_connection (self, bzobj);
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+ } else {
+ if ( device_is_usable
+ && bzobj->x_device_panu_connection_allow_create
+ && NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_NAP)
+ && _conn_track_find_head (self, NM_BT_CAPABILITY_NAP, bzobj->d_device.address) ) {
+ /* We have a useable device and also a panu-connection. We block future attemps
+ * to generate a connection. */
+ bzobj->x_device_panu_connection_allow_create = FALSE;
+ }
+ if (bzobj->x_device.panu_connection) {
+ if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (bzobj->x_device.panu_connection),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) {
+ /* the connection that we generated earlier still exists, but it's not longer the same
+ * as it was when we created it. Forget about it, so that we don't delete the profile later... */
+ bzobj->x_device.panu_connection = NULL;
+ } else {
+ if ( !device_is_usable
+ || !_conn_track_is_relevant_for_sett_conn (bzobj->x_device.panu_connection,
+ NM_BT_CAPABILITY_NAP,
+ bzobj->d_device.address)) {
+ _LOGT ("connection: delete in-memory PANU connection %s (%s) as device %s",
+ nm_settings_connection_get_uuid (bzobj->x_device.panu_connection),
+ nm_settings_connection_get_id (bzobj->x_device.panu_connection),
+ !device_is_usable ? "is now unusable" : "no longer matches");
+ bzobj->x_device_panu_connection_allow_create = TRUE;
+ nm_settings_connection_delete (g_steal_pointer (&bzobj->x_device.panu_connection), FALSE);
+ }
+ }
+ }
+ }
+
+ bzobj->x_device_is_connected = device_is_usable
+ && _bzobjs_device_is_connected (bzobj);
+
+ bzobj->x_device_is_usable = device_is_usable;
+
+ if (bzobj->x_device.device_bt) {
+ const char *device_to_delete_msg;
+
+ if (!device_is_usable)
+ device_to_delete_msg = "device became unusable";
+ else if (!_nm_device_bt_for_same_device (bzobj->x_device.device_bt,
+ bzobj->object_path,
+ bzobj->d_device.address,
+ NULL,
+ bzobj->d_device_capabilities))
+ device_to_delete_msg = "device is no longer compatible";
+ else
+ device_to_delete_msg = NULL;
+
+ if (device_to_delete_msg) {
+ nm_clear_g_signal_handler (bzobj->x_device.device_bt, &bzobj->x_device.device_bt_signal_id);
+
+ device_deleted = g_steal_pointer (&bzobj->x_device.device_bt);
+
+ _LOGD ("[%s]: drop device because %s",
+ bzobj->object_path,
+ device_to_delete_msg);
+
+ _connect_disconnect (self, bzobj, device_to_delete_msg);
+ }
+ }
+
+ if (device_is_usable) {
+ if (!bzobj->x_device.device_bt) {
+ bzobj->x_device.device_bt = nm_device_bt_new (self,
+ bzobj->object_path,
+ bzobj->d_device.address,
+ bzobj->d_device.name,
+ bzobj->d_device_capabilities);
+ device_added = g_object_ref (bzobj->x_device.device_bt);
+ bzobj->x_device.device_bt_signal_id = g_signal_connect (device_added,
+ NM_DEVICE_STATE_CHANGED,
+ G_CALLBACK (_device_state_changed_cb),
+ bzobj);
+ } else
+ _nm_device_bt_notify_set_name (bzobj->x_device.device_bt, bzobj->d_device.name);
+
+ _nm_device_bt_notify_set_connected (bzobj->x_device.device_bt, bzobj->x_device_is_connected);
+ }
+
+ if ( bzobj->x_device.c_req_data
+ && !bzobj->x_device.c_req_data->int_cancellable
+ && bzobj->x_device_is_connected) {
+ gs_free char *device_name = g_steal_pointer (&bzobj->x_device.c_req_data->device_name);
+
+ _device_connect_req_data_complete (g_steal_pointer (&bzobj->x_device.c_req_data),
+ self,
+ device_name,
+ NULL);
+ }
+
+ if (device_added)
+ g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device_added);
+
+ if (device_deleted)
+ _nm_device_bt_notify_removed (device_deleted);
}
+/*****************************************************************************/
+
static void
-setup_bluez5 (NMBluezManager *self)
+_process_change_idle_all (NMBluezManager *self,
+ gboolean *out_emit_device_availability_changed)
{
- NMBluez5Manager *manager;
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ BzDBusObj *bzobj;
+
+ while ((bzobj = c_list_first_entry (&priv->process_change_lst_head, BzDBusObj, process_change_lst))) {
+
+ c_list_unlink (&bzobj->process_change_lst);
- g_return_if_fail (!priv->manager5);
+ _LOG_bzobj (bzobj, "before-processing");
- cleanup_checking (self, TRUE);
+ _device_process_change (bzobj);
- priv->manager5 = manager = nm_bluez5_manager_new (priv->settings);
+ _network_server_process_change (bzobj, out_emit_device_availability_changed);
- g_signal_connect (manager,
- NM_BLUEZ_MANAGER_BDADDR_ADDED,
- G_CALLBACK (manager_bdaddr_added_cb),
- self);
- g_signal_connect (manager,
- NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED,
- G_CALLBACK (manager_network_server_added_cb),
- self);
+ _LOG_bzobj (bzobj, "after-processing");
- nm_bluez5_manager_query_devices (manager);
+ _bzobjs_del_if_dead (bzobj);
+ }
+
+ nm_clear_g_source (&priv->process_change_idle_id);
+}
+
+static gboolean
+_process_change_idle_cb (gpointer user_data)
+{
+ NMBluezManager *self = user_data;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gboolean emit_device_availability_changed = FALSE;
+
+ _process_change_idle_all (self, &emit_device_availability_changed);
+
+ if (emit_device_availability_changed)
+ nm_manager_notify_device_availibility_maybe_changed (priv->manager);
+
+ return G_SOURCE_CONTINUE;
}
static void
-watch_name_on_appeared (GDBusConnection *connection,
- const char *name,
- const char *name_owner,
- gpointer user_data)
+_process_change_idle_schedule (NMBluezManager *self,
+ BzDBusObj *bzobj)
{
- check_bluez_and_try_setup (NM_BLUEZ_MANAGER (user_data));
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj->process_change_lst);
+ if (priv->process_change_idle_id == 0)
+ priv->process_change_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, _process_change_idle_cb, self, NULL);
}
static void
-check_bluez_and_try_setup_final_step (NMBluezManager *self, gboolean ready, const char *reason)
+_dbus_process_changes (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *log_reason)
{
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gboolean network_server_is_usable;
+ gboolean adapter_is_usable_for_device;
+ gboolean device_is_usable;
+ gboolean changes = FALSE;
+ gboolean recheck_devices_for_adapter = FALSE;
+
+ nm_assert (bzobj);
+
+ _LOG_bzobj (bzobj, log_reason);
+
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+
+ if (bzobj->x_device_is_usable != device_is_usable)
+ changes = TRUE;
+ else if (bzobj->x_device.device_bt) {
+ if (!device_is_usable)
+ changes = TRUE;
+ else {
+ if ( bzobj->x_device_is_connected != _bzobjs_device_is_connected (bzobj)
+ || !_nm_device_bt_for_same_device (bzobj->x_device.device_bt,
+ bzobj->object_path,
+ bzobj->d_device.address,
+ bzobj->d_device.name,
+ bzobj->d_device_capabilities))
+ changes = TRUE;
+ }
+ }
- if (ready) {
- setup_bluez5 (self);
+ adapter_is_usable_for_device = _bzobjs_adapter_is_usable_for_device (bzobj);
+ if (adapter_is_usable_for_device != bzobj->was_usable_adapter_for_device_before) {
+ /* this function does not modify bzobj in any other cases except here.
+ * Usually changes are processed delayed, in the idle handler.
+ *
+ * But the bzobj->was_usable_adapter_for_device_before only exists to know whether
+ * we need to re-check device availability. It is correct to set the flag
+ * here, right before we checked. */
+ bzobj->was_usable_adapter_for_device_before = adapter_is_usable_for_device;
+ recheck_devices_for_adapter = TRUE;
+ changes = TRUE;
+ }
+
+ if (!changes) {
+ network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
+
+ if (network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst)))
+ changes = TRUE;
+ else if ( bzobj->x_network_server.device_br
+ && !network_server_is_usable)
+ changes = TRUE;
+ else if (!nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL,
+ bzobj->x_network_server.adapter_address))
+ changes = TRUE;
+ }
+
+ if (changes)
+ _process_change_idle_schedule (self, bzobj);
+
+ if (recheck_devices_for_adapter) {
+ GHashTableIter iter;
+ BzDBusObj *bzobj2;
+
+ /* we got a change to the availability of an adapter. We might need to recheck
+ * all devices that use this adapter... */
+ g_hash_table_iter_init (&iter, priv->bzobjs);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj2, NULL)) {
+ if (bzobj2 == bzobj)
+ continue;
+ if (!nm_streq0 (bzobj2->d_device.adapter, bzobj->object_path))
+ continue;
+ if (c_list_is_empty (&bzobj2->process_change_lst))
+ _dbus_process_changes (self, bzobj2, "adapter-changed");
+ else
+ nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj2->process_change_lst);
+ }
+ }
+
+ _bzobjs_del_if_dead (bzobj);
+}
+
+/*****************************************************************************/
+
+#define ALL_RELEVANT_INTERFACE_NAMES NM_MAKE_STRV (NM_BLUEZ5_ADAPTER_INTERFACE, \
+ NM_BLUEZ5_DEVICE_INTERFACE, \
+ NM_BLUEZ5_NETWORK_INTERFACE, \
+ NM_BLUEZ5_NETWORK_SERVER_INTERFACE)
+
+static gboolean
+_dbus_handle_properties_changed (NMBluezManager *self,
+ const char *object_path,
+ const char *interface_name,
+ GVariant *changed_properties,
+ const char *const*invalidated_properties,
+ BzDBusObj **inout_bzobj)
+{
+ BzDBusObj *bzobj = NULL;
+ gboolean changed = FALSE;
+ const char *property_name;
+ GVariant *property_value;
+ GVariantIter iter_prop;
+ gsize i;
+
+ if (!invalidated_properties)
+ invalidated_properties = NM_PTRARRAY_EMPTY (const char *);
+
+ nm_assert (g_variant_is_of_type (changed_properties, G_VARIANT_TYPE ("a{sv}")));
+
+ if (inout_bzobj) {
+ bzobj = *inout_bzobj;
+ nm_assert (!bzobj || nm_streq (object_path, bzobj->object_path));
+ }
+
+ if (changed_properties)
+ g_variant_iter_init (&iter_prop, changed_properties);
+
+ if (nm_streq (interface_name, NM_BLUEZ5_ADAPTER_INTERFACE)) {
+ _bzobjs_init (self, &bzobj, object_path);
+ if (!bzobj->d_has_adapter_iface) {
+ changed = TRUE;
+ bzobj->d_has_adapter_iface = TRUE;
+ }
+
+ while ( changed_properties
+ && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+ _nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+ if (nm_streq (property_name, "Address")) {
+ gs_free char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+ ? nm_utils_hwaddr_canonical (g_variant_get_string (property_value, NULL), ETH_ALEN)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_adapter.address, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_adapter.address);
+ bzobj->d_adapter.address = g_steal_pointer (&s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Powered")) {
+ bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+ && g_variant_get_boolean (property_value);
+
+ if (bzobj->d_adapter_powered != v) {
+ changed = TRUE;
+ bzobj->d_adapter_powered = v;
+ }
+ continue;
+ }
+ }
+
+ for (i = 0; (property_name = invalidated_properties[i]); i++) {
+ if (nm_streq (property_name, "Address")) {
+ if (bzobj->d_adapter.address) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_adapter.address);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Powered")) {
+ if (bzobj->d_adapter_powered) {
+ changed = TRUE;
+ bzobj->d_adapter_powered = FALSE;
+ }
+ continue;
+ }
+ }
+
+ } else if (nm_streq (interface_name, NM_BLUEZ5_DEVICE_INTERFACE)) {
+ _bzobjs_init (self, &bzobj, object_path);
+ if (!bzobj->d_has_device_iface) {
+ changed = TRUE;
+ bzobj->d_has_device_iface = TRUE;
+ }
+
+ while ( changed_properties
+ && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+ _nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+ if (nm_streq (property_name, "Address")) {
+ gs_free char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+ ? nm_utils_hwaddr_canonical (g_variant_get_string (property_value, NULL), ETH_ALEN)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_device.address, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.address);
+ bzobj->d_device.address = g_steal_pointer (&s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Name")) {
+ const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+ ? g_variant_get_string (property_value, NULL)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_device.name, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.name);
+ bzobj->d_device.name = g_strdup (s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Adapter")) {
+ const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_OBJECT_PATH)
+ ? g_variant_get_string (property_value, NULL)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_device.adapter, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.adapter);
+ bzobj->d_device.adapter = g_strdup (s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "UUIDs")) {
+ NMBluetoothCapabilities capabilities = NM_BT_CAPABILITY_NONE;
+
+ if (g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING_ARRAY)) {
+ gs_free const char **s = g_variant_get_strv (property_value, NULL);
+
+ capabilities = convert_uuids_to_capabilities (s);
+ }
+ if (bzobj->d_device_capabilities != capabilities) {
+ changed = TRUE;
+ bzobj->d_device_capabilities = capabilities;
+ nm_assert (bzobj->d_device_capabilities == capabilities);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Connected")) {
+ bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+ && g_variant_get_boolean (property_value);
+
+ if (bzobj->d_device_connected != v) {
+ changed = TRUE;
+ bzobj->d_device_connected = v;
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Paired")) {
+ bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+ && g_variant_get_boolean (property_value);
+
+ if (bzobj->d_device_paired != v) {
+ changed = TRUE;
+ bzobj->d_device_paired = v;
+ }
+ continue;
+ }
+ }
+
+ for (i = 0; (property_name = invalidated_properties[i]); i++) {
+ if (nm_streq (property_name, "Address")) {
+ if (bzobj->d_device.address) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.address);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Name")) {
+ if (bzobj->d_device.name) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.name);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Adapter")) {
+ if (bzobj->d_device.adapter) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.adapter);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "UUIDs")) {
+ if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+ changed = TRUE;
+ bzobj->d_device_capabilities = NM_BT_CAPABILITY_NONE;
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Connected")) {
+ if (bzobj->d_device_connected) {
+ changed = TRUE;
+ bzobj->d_device_connected = FALSE;
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Paired")) {
+ if (bzobj->d_device_paired) {
+ changed = TRUE;
+ bzobj->d_device_paired = FALSE;
+ }
+ continue;
+ }
+ }
+
+ } else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_INTERFACE)) {
+ _bzobjs_init (self, &bzobj, object_path);
+ if (!bzobj->d_has_network_iface) {
+ changed = TRUE;
+ bzobj->d_has_network_iface = TRUE;
+ }
+
+ while ( changed_properties
+ && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+ _nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+ if (nm_streq (property_name, "Interface")) {
+ const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+ ? g_variant_get_string (property_value, NULL)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_network.interface, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_network.interface);
+ bzobj->d_network.interface = g_strdup (s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Connected")) {
+ bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+ && g_variant_get_boolean (property_value);
+
+ if (bzobj->d_network_connected != v) {
+ changed = TRUE;
+ bzobj->d_network_connected = v;
+ }
+ continue;
+ }
+ }
+
+ for (i = 0; (property_name = invalidated_properties[i]); i++) {
+ if (nm_streq (property_name, "Interface")) {
+ if (bzobj->d_network.interface) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_network.interface);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Connected")) {
+ if (bzobj->d_network_connected) {
+ changed = TRUE;
+ bzobj->d_network_connected = FALSE;
+ }
+ continue;
+ }
+ }
+
+ } else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) {
+ _bzobjs_init (self, &bzobj, object_path);
+ if (!bzobj->d_has_network_server_iface) {
+ changed = TRUE;
+ bzobj->d_has_network_server_iface = TRUE;
+ }
+ }
+
+ nm_assert (!changed || bzobj);
+
+ if (inout_bzobj)
+ *inout_bzobj = bzobj;
+
+ return changed;
+}
+
+static void
+_dbus_handle_interface_added (NMBluezManager *self,
+ const char *object_path,
+ GVariant *ifaces,
+ gboolean initial_get_managed_objects)
+{
+ BzDBusObj *bzobj = NULL;
+ gboolean changed = FALSE;
+ const char *interface_name;
+ GVariant *changed_properties;
+ GVariantIter iter_ifaces;
+
+ nm_assert (g_variant_is_of_type (ifaces, G_VARIANT_TYPE ("a{sa{sv}}")));
+
+ g_variant_iter_init (&iter_ifaces, ifaces);
+ while (g_variant_iter_next (&iter_ifaces, "{&s@a{sv}}", &interface_name, &changed_properties)) {
+ _nm_unused gs_unref_variant GVariant *changed_properties_free = changed_properties;
+
+ if (_dbus_handle_properties_changed (self, object_path, interface_name, changed_properties, NULL, &bzobj))
+ changed = TRUE;
+ }
+
+ if (changed) {
+ _dbus_process_changes (self,
+ bzobj,
+ initial_get_managed_objects
+ ? "dbus-init"
+ : "dbus-iface-added");
+ }
+}
+
+static gboolean
+_dbus_handle_interface_removed (NMBluezManager *self,
+ const char *object_path,
+ BzDBusObj **inout_bzobj,
+ const char *const*removed_interfaces)
+{
+ gboolean changed = FALSE;
+ BzDBusObj *bzobj;
+ gsize i;
+
+ if ( inout_bzobj
+ && *inout_bzobj) {
+ bzobj = *inout_bzobj;
+ nm_assert (bzobj == _bzobjs_get (self, object_path));
+ } else {
+ bzobj = _bzobjs_get (self, object_path);
+ if (!bzobj)
+ return FALSE;
+ NM_SET_OUT (inout_bzobj, bzobj);
+ }
+
+ for (i = 0; removed_interfaces[i]; i++) {
+ const char *interface_name = removed_interfaces[i];
+
+ if (nm_streq (interface_name, NM_BLUEZ5_ADAPTER_INTERFACE)) {
+ if (bzobj->d_has_adapter_iface) {
+ changed = TRUE;
+ bzobj->d_has_adapter_iface = FALSE;
+ }
+ if (bzobj->d_adapter.address) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_adapter.address);
+ }
+ if (bzobj->d_adapter_powered) {
+ changed = TRUE;
+ bzobj->d_adapter_powered = FALSE;
+ }
+ } else if (nm_streq (interface_name, NM_BLUEZ5_DEVICE_INTERFACE)) {
+ if (bzobj->d_has_device_iface) {
+ changed = TRUE;
+ bzobj->d_has_device_iface = FALSE;
+ }
+ if (bzobj->d_device.address) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.address);
+ }
+ if (bzobj->d_device.name) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.name);
+ }
+ if (bzobj->d_device.adapter) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.adapter);
+ }
+ if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+ changed = TRUE;
+ bzobj->d_device_capabilities = NM_BT_CAPABILITY_NONE;
+ }
+ if (bzobj->d_device_connected) {
+ changed = TRUE;
+ bzobj->d_device_connected = FALSE;
+ }
+ if (bzobj->d_device_paired) {
+ changed = TRUE;
+ bzobj->d_device_paired = FALSE;
+ }
+ } else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_INTERFACE)) {
+ if (bzobj->d_has_network_iface) {
+ changed = TRUE;
+ bzobj->d_has_network_iface = FALSE;
+ }
+ if (bzobj->d_network.interface) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_network.interface);
+ }
+ if (bzobj->d_network_connected) {
+ changed = TRUE;
+ bzobj->d_network_connected = FALSE;
+ }
+ } else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) {
+ if (bzobj->d_has_network_server_iface) {
+ changed = TRUE;
+ bzobj->d_has_network_server_iface = FALSE;
+ }
+ }
+ }
+
+ return changed;
+}
+
+static void
+_dbus_managed_objects_changed_cb (const char *object_path,
+ GVariant *added_interfaces_and_properties,
+ const char *const*removed_interfaces,
+ gpointer user_data)
+{
+ NMBluezManager *self = user_data;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ BzDBusObj *bzobj = NULL;
+ gboolean changed;
+
+ if (priv->get_managed_objects_cancellable) {
+ /* we still wait for the initial GetManagedObjects(). Ignore the event. */
return;
}
- _LOGD ("detecting BlueZ version failed: %s", reason);
+ if (!added_interfaces_and_properties) {
+ changed = _dbus_handle_interface_removed (self, object_path, &bzobj, removed_interfaces);
+ if (changed)
+ _dbus_process_changes (self, bzobj, "dbus-iface-removed");
+ } else
+ _dbus_handle_interface_added (self, object_path, added_interfaces_and_properties, FALSE);
+}
- /* cancel current attempts to detect the version. */
- cleanup_checking (self, FALSE);
- if (!priv->watch_name_id) {
- priv->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
- NM_BLUEZ_SERVICE,
- G_BUS_NAME_WATCHER_FLAGS_NONE,
- watch_name_on_appeared,
- NULL,
- self,
- NULL);
+static void
+_dbus_properties_changed_cb (GDBusConnection *connection,
+ const char *sender_name,
+ const char *object_path,
+ const char *signal_interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ NMBluezManager *self = user_data;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ const char *interface_name;
+ gs_unref_variant GVariant *changed_properties = NULL;
+ gs_free const char **invalidated_properties = NULL;
+ BzDBusObj *bzobj = NULL;
+
+ if (priv->get_managed_objects_cancellable) {
+ /* we still wait for the initial GetManagedObjects(). Ignore the event. */
+ return;
}
+
+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
+ return;
+
+ g_variant_get (parameters,
+ "(&s@a{sv}^a&s)",
+ &interface_name,
+ &changed_properties,
+ &invalidated_properties);
+
+ if (_dbus_handle_properties_changed (self, object_path, interface_name, changed_properties, invalidated_properties, &bzobj))
+ _dbus_process_changes (self, bzobj, "dbus-property-changed");
}
static void
-check_bluez_and_try_setup_do_introspect (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
+_dbus_get_managed_objects_cb (GVariant *result,
+ GError *error,
+ gpointer user_data)
{
- NMBluezManager *self = async_data_unpack (user_data);
+ NMBluezManager *self;
NMBluezManagerPrivate *priv;
- GError *error = NULL;
- gs_unref_variant GVariant *result = NULL;
- const char *reason = NULL;
+ GVariantIter iter;
+ const char *object_path;
+ GVariant *ifaces;
- if (!self)
+ if ( !result
+ && nm_utils_error_is_cancelled (error, FALSE))
return;
+ self = user_data;
priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
- g_return_if_fail (priv->introspect_proxy);
- g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable));
-
- g_clear_object (&priv->async_cancellable);
+ g_clear_object (&priv->get_managed_objects_cancellable);
- result = _nm_dbus_proxy_call_finish (priv->introspect_proxy, res,
- G_VARIANT_TYPE ("(s)"), &error);
if (!result) {
- char *reason2;
-
- g_dbus_error_strip_remote_error (error);
- reason2 = g_strdup_printf ("introspect failed with %s", error->message);
- check_bluez_and_try_setup_final_step (self, FALSE, reason2);
- g_error_free (error);
- g_free (reason2);
+ _LOGT ("initial GetManagedObjects() call failed: %s", error->message);
+ _cleanup_for_name_owner (self);
return;
}
- check_bluez_and_try_setup_final_step (self, TRUE, reason);
+ _LOGT ("initial GetManagedObjects call succeeded");
+
+ g_variant_iter_init (&iter, result);
+ while (g_variant_iter_next (&iter, "{&o@a{sa{sv}}}", &object_path, &ifaces)) {
+ _nm_unused gs_unref_variant GVariant *ifaces_free = ifaces;
+
+ _dbus_handle_interface_added (self, object_path, ifaces, TRUE);
+ }
}
+/*****************************************************************************/
+
static void
-check_bluez_and_try_setup_on_new_proxy (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
+_cleanup_for_name_owner (NMBluezManager *self)
{
- NMBluezManager *self = async_data_unpack (user_data);
- NMBluezManagerPrivate *priv;
- GError *error = NULL;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gboolean emit_device_availability_changed = FALSE;
+ GHashTableIter iter;
+ BzDBusObj *bzobj;
+ gboolean first = TRUE;
+
+ nm_clear_g_cancellable (&priv->get_managed_objects_cancellable);
+
+ nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+ &priv->managed_objects_changed_id);
+ nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+ &priv->properties_changed_id);
+
+ nm_clear_g_free (&priv->name_owner);
+
+ g_hash_table_iter_init (&iter, priv->bzobjs);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+ if (first) {
+ first = FALSE;
+ _LOGT ("drop all objects form D-Bus cache...");
+ }
+ _dbus_handle_interface_removed (self,
+ bzobj->object_path,
+ &bzobj,
+ ALL_RELEVANT_INTERFACE_NAMES);
+ nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj->process_change_lst);
+ }
+ _process_change_idle_all (self, &emit_device_availability_changed);
+ nm_assert (g_hash_table_size (priv->bzobjs) == 0);
+
+ if (emit_device_availability_changed)
+ nm_manager_notify_device_availibility_maybe_changed (priv->manager);
+}
+
+static void
+name_owner_changed (NMBluezManager *self,
+ const char *owner)
+{
+ _nm_unused gs_unref_object NMBluezManager *self_keep_alive = g_object_ref (self);
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ owner = nm_str_not_empty (owner);
+
+ if (!owner)
+ _LOGT ("D-Bus name for bluez has no owner");
+ else
+ _LOGT ("D-Bus name for bluez has owner %s", owner);
+
+ nm_clear_g_cancellable (&priv->name_owner_get_cancellable);
- if (!self)
+ if (nm_streq0 (priv->name_owner, owner))
return;
- priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ _cleanup_for_name_owner (self);
- g_return_if_fail (!priv->introspect_proxy);
- g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable));
+ if (!owner)
+ return;
- priv->introspect_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ priv->name_owner = g_strdup (owner);
+
+ priv->get_managed_objects_cancellable = g_cancellable_new ();
+
+ priv->managed_objects_changed_id = nm_dbus_connection_signal_subscribe_object_manager (priv->dbus_connection,
+ priv->name_owner,
+ NM_BLUEZ_MANAGER_PATH,
+ _dbus_managed_objects_changed_cb,
+ self,
+ NULL);
+
+ priv->properties_changed_id = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection,
+ priv->name_owner,
+ NULL,
+ NULL,
+ _dbus_properties_changed_cb,
+ self,
+ NULL);
+
+ nm_dbus_connection_call_get_managed_objects (priv->dbus_connection,
+ priv->name_owner,
+ NM_BLUEZ_MANAGER_PATH,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 20000,
+ priv->get_managed_objects_cancellable,
+ _dbus_get_managed_objects_cb,
+ self);
+}
- if (!priv->introspect_proxy) {
- char *reason = g_strdup_printf ("bluez error creating dbus proxy: %s", error->message);
+static void
+name_owner_changed_cb (GDBusConnection *connection,
+ const char *sender_name,
+ const char *object_path,
+ const char *interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ NMBluezManager *self = user_data;
+ const char *new_owner;
- check_bluez_and_try_setup_final_step (self, FALSE, reason);
- g_error_free (error);
- g_free (reason);
+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
return;
- }
- g_dbus_proxy_call (priv->introspect_proxy,
- "Introspect",
- NULL,
- G_DBUS_CALL_FLAGS_NO_AUTO_START,
- 3000,
- priv->async_cancellable,
- check_bluez_and_try_setup_do_introspect,
- async_data_pack (self));
+ g_variant_get (parameters,
+ "(&s&s&s)",
+ NULL,
+ NULL,
+ &new_owner);
+
+ name_owner_changed (self, new_owner);
}
static void
-check_bluez_and_try_setup (NMBluezManager *self)
+name_owner_get_cb (const char *name_owner,
+ GError *error,
+ gpointer user_data)
+{
+ if ( name_owner
+ || !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ name_owner_changed (user_data, name_owner);
+}
+
+/*****************************************************************************/
+
+static void
+_cleanup_all (NMBluezManager *self)
{
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
- /* there should be no ongoing detection. Anyway, cleanup_checking. */
- cleanup_checking (self, FALSE);
+ priv->settings_registered = FALSE;
+
+ g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
+ g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
+ g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self);
+
+ g_hash_table_remove_all (priv->conn_data_elems);
+ g_hash_table_remove_all (priv->conn_data_heads);
- priv->async_cancellable = g_cancellable_new ();
+ _cleanup_for_name_owner (self);
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
- NULL,
- NM_BLUEZ_SERVICE,
- "/",
- DBUS_INTERFACE_INTROSPECTABLE,
- priv->async_cancellable,
- check_bluez_and_try_setup_on_new_proxy,
- async_data_pack (self));
+ nm_clear_g_cancellable (&priv->name_owner_get_cancellable);
+
+ nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+ &priv->name_owner_changed_id);
}
static void
start (NMDeviceFactory *factory)
{
- check_bluez_and_try_setup (NM_BLUEZ_MANAGER (factory));
+ NMBluezManager *self;
+ NMBluezManagerPrivate *priv;
+ NMSettingsConnection *const*sett_conns;
+ guint n_sett_conns;
+ guint i;
+
+ g_return_if_fail (NM_IS_BLUEZ_MANAGER (factory));
+
+ self = NM_BLUEZ_MANAGER (factory);
+ priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ _cleanup_all (self);
+
+ if (!priv->dbus_connection) {
+ _LOGI ("no D-Bus connection available");
+ return;
+ }
+
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, G_CALLBACK (cp_connection_added), self);
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), self);
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
+
+ priv->settings_registered = TRUE;
+
+ sett_conns = nm_settings_get_connections (priv->settings, &n_sett_conns);
+ for (i = 0; i < n_sett_conns; i++)
+ _conn_track_update (self, sett_conns[i], TRUE, NULL, NULL, NULL);
+
+ priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
+ NM_BLUEZ_SERVICE,
+ name_owner_changed_cb,
+ self,
+ NULL);
+
+ priv->name_owner_get_cancellable = g_cancellable_new ();
+
+ nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
+ NM_BLUEZ_SERVICE,
+ 10000,
+ priv->name_owner_get_cancellable,
+ name_owner_get_cb,
+ self);
+}
+
+/*****************************************************************************/
+
+static void
+_connect_returned (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ NMBluetoothCapabilities bt_type,
+ const char *device_name,
+ NMBluez5DunContext *dun_context,
+ GError *error)
+{
+ char sbuf_cap[100];
+
+ if (error) {
+ nm_assert (!device_name);
+ nm_assert (!dun_context);
+
+ _LOGI ("%s [%s]: connect failed: %s",
+ nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->object_path,
+ error->message);
+
+ _device_connect_req_data_complete (g_steal_pointer (&bzobj->x_device.c_req_data),
+ self,
+ NULL,
+ error);
+ _connect_disconnect (self, bzobj, "cleanup after connect failure");
+ return;
+ }
+
+ nm_assert (bzobj->x_device_connect_bt_type == bt_type);
+ nm_assert (device_name);
+ nm_assert ((bt_type == NM_BT_CAPABILITY_DUN) == (!!dun_context));
+ nm_assert (bzobj->x_device.c_req_data);
+
+ g_clear_object (&bzobj->x_device.c_req_data->int_cancellable);
+
+ bzobj->x_device.connect_dun_context = dun_context;
+
+ _LOGD ("%s [%s]: connect successful to device %s",
+ nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->object_path,
+ device_name);
+
+ /* we already have another over-all timer running. But after we connected the device,
+ * we still need to wait for bluez to acknowledge the connected state (via D-Bus, for NAP).
+ * For DUN profiles we likely are already fully connected by now.
+ *
+ * Anyway, schedule another timeout that is possibly shorter than the overall, original
+ * timeout. Now this should go down fast. */
+ bzobj->x_device.c_req_data->timeout_wait_connect_id = g_timeout_add (5000,
+ _connect_timeout_wait_connected_cb,
+ bzobj),
+ bzobj->x_device.c_req_data->device_name = g_strdup (device_name);
+
+ if ( _bzobjs_device_is_usable (bzobj, NULL, NULL)
+ && _bzobjs_device_is_connected (bzobj)) {
+ /* We are now connected. Schedule the task that completes the state. */
+ _process_change_idle_schedule (self, bzobj);
+ }
+}
+
+#if WITH_BLUEZ5_DUN
+static void
+_connect_dun_notify_tty_hangup_cb (NMBluez5DunContext *context,
+ gpointer user_data)
+{
+ BzDBusObj *bzobj = user_data;
+
+ _connect_disconnect (bzobj->self,
+ bzobj,
+ "DUN connection hung up");
+}
+
+static void
+_connect_dun_step2_cb (NMBluez5DunContext *context,
+ const char *rfcomm_dev,
+ GError *error,
+ gpointer user_data)
+{
+ BzDBusObj *bzobj;
+
+ if (nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ bzobj = user_data;
+
+ if (rfcomm_dev) {
+ /* We want to early notifiy about the rfcomm path. That is because we might still delay
+ * to signal full activation longer (asynchronously). But the earliest time the callback
+ * is invoked with the rfcomm path, we just created the device synchronously.
+ *
+ * By already notifying the caller about the path early, it avoids a race where ModemManager
+ * would find the modem before the bluetooth code considers the profile fully activated. */
+
+ nm_assert (!error);
+ nm_assert (bzobj->x_device.c_req_data);
+
+ if (!g_cancellable_is_cancelled (bzobj->x_device.c_req_data->ext_cancellable))
+ bzobj->x_device.c_req_data->callback (bzobj->self, FALSE, rfcomm_dev, NULL, bzobj->x_device.c_req_data->callback_user_data);
+
+ if (!context) {
+ /* No context set. This means, we just got notified about the rfcomm path and need to wait
+ * longer, for the next callback. */
+ return;
+ }
+ }
+
+ _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, rfcomm_dev, context, error);
+}
+
+static void
+_connect_dun_step1_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gs_unref_variant GVariant *ret = NULL;
+ gs_free_error GError *error = NULL;
+ BzDBusObj *bzobj_adapter;
+ BzDBusObj *bzobj;
+
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+
+ if ( !ret
+ && nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ bzobj = user_data;
+
+ if (error) {
+ _LOGT ("DUN: [%s]: bluetooth device connect failed: %s", bzobj->object_path, error->message);
+ /* we actually ignore this error. Let's try, maybe we still can connect via DUN. */
+ g_clear_error (&error);
+ } else
+ _LOGT ("DUN: [%s]: bluetooth device connected successfully", bzobj->object_path);
+
+ if (!_bzobjs_device_is_usable (bzobj, &bzobj_adapter, NULL)) {
+ nm_utils_error_set (&error, NM_UTILS_ERROR_UNKNOWN,
+ "device %s is not usable for DUN after connect",
+ bzobj->object_path);
+ _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, NULL, NULL, error);
+ return;
+ }
+
+ if (!nm_bluez5_dun_connect (bzobj_adapter->d_adapter.address,
+ bzobj->d_device.address,
+ bzobj->x_device.c_req_data->int_cancellable,
+ _connect_dun_step2_cb,
+ bzobj,
+ _connect_dun_notify_tty_hangup_cb,
+ bzobj,
+ &error)) {
+ _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, NULL, NULL, error);
+ return;
+ }
+}
+#endif
+
+static void
+_connect_nap_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gs_unref_variant GVariant *ret = NULL;
+ const char *network_iface_name = NULL;
+ gs_free_error GError *error = NULL;
+ BzDBusObj *bzobj;
+
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+
+ if ( !ret
+ && nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ if (ret)
+ g_variant_get (ret, "(&s)", &network_iface_name);
+
+ bzobj = user_data;
+
+ _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_NAP, network_iface_name, NULL, error);
+}
+
+static void
+_connect_cancelled_cb (GCancellable *cancellable,
+ BzDBusObj *bzobj)
+{
+ _connect_disconnect (bzobj->self, bzobj, "connect cancelled");
+}
+
+static gboolean
+_connect_timeout_wait_connected_cb (gpointer user_data)
+{
+ BzDBusObj *bzobj = user_data;
+
+ bzobj->x_device.c_req_data->timeout_wait_connect_id = 0;
+ _connect_disconnect (bzobj->self, bzobj, "timeout waiting for connected");
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_timeout_cb (gpointer user_data)
+{
+ BzDBusObj *bzobj = user_data;
+
+ bzobj->x_device.c_req_data->timeout_id = 0;
+ _connect_disconnect (bzobj->self, bzobj, "timeout connecting");
+ return G_SOURCE_REMOVE;
}
+static void
+_connect_disconnect (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *reason)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ DeviceConnectReqData *c_req_data;
+ char sbuf_cap[100];
+ gboolean bt_type;
+
+ if (bzobj->x_device_connect_bt_type == NM_BT_CAPABILITY_NONE) {
+ nm_assert (!bzobj->x_device.c_req_data);
+ return;
+ }
+
+ bt_type = bzobj->x_device_connect_bt_type;
+ nm_assert (NM_IN_SET (bt_type, NM_BT_CAPABILITY_DUN, NM_BT_CAPABILITY_NAP));
+ bzobj->x_device_connect_bt_type = NM_BT_CAPABILITY_NONE;
+
+ c_req_data = g_steal_pointer (&bzobj->x_device.c_req_data);
+
+ _LOGD ("%s [%s]: disconnect due to %s",
+ nm_bluetooth_capability_to_string (bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->object_path,
+ reason);
+
+ if (c_req_data)
+ nm_clear_g_cancellable (&c_req_data->int_cancellable);
+
+ if (bt_type == NM_BT_CAPABILITY_DUN) {
+ /* For DUN devices, we also called org.bluez.Device1.Connect() (because in order
+ * for nm_bluez5_dun_connect() to succeed, we need to be already connected *why??).
+ *
+ * But upon disconnect we don't call Disconnect() because we don't know whether somebody
+ * else also uses the bluetooth device for other purposes. During disconnect we only
+ * terminate the DUN connection, but don't disconnect entirely. I think that's the
+ * best we can do. */
+#if WITH_BLUEZ5_DUN
+ nm_clear_pointer (&bzobj->x_device.connect_dun_context, nm_bluez5_dun_disconnect);
+#else
+ nm_assert_not_reached ();
+#endif
+ } else {
+ if (priv->name_owner) {
+ gs_unref_object GCancellable *cancellable = NULL;
+
+ cancellable = g_cancellable_new ();
+
+ nm_shutdown_wait_obj_register_cancellable_full (cancellable,
+ g_strdup_printf ("bt-disconnect-nap[%s]", bzobj->object_path),
+ TRUE);
+
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_NETWORK_INTERFACE,
+ "Disconnect",
+ g_variant_new("()"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ cancellable,
+ _dbus_call_complete_cb_nop,
+ NULL);
+ }
+ }
+
+ if (c_req_data) {
+ gs_free_error GError *error = NULL;
+
+ nm_utils_error_set (&error,
+ NM_UTILS_ERROR_UNKNOWN,
+ "connect aborted due to %s",
+ reason);
+ _device_connect_req_data_complete (c_req_data, self, NULL, error);
+ }
+}
+
+gboolean
+nm_bluez_manager_connect (NMBluezManager *self,
+ const char *object_path,
+ NMBluetoothCapabilities connection_bt_type,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMBluezManagerConnectCb callback,
+ gpointer callback_user_data,
+ GError **error)
+{
+ gs_unref_object GCancellable *int_cancellable = NULL;
+ DeviceConnectReqData *c_req_data;
+ NMBluezManagerPrivate *priv;
+ BzDBusObj *bzobj;
+ char sbuf_cap[100];
+
+ g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (self), FALSE);
+ g_return_val_if_fail (NM_IN_SET (connection_bt_type, NM_BT_CAPABILITY_DUN,
+ NM_BT_CAPABILITY_NAP), FALSE);
+ g_return_val_if_fail (callback, FALSE);
+
+ nm_assert (timeout_msec > 0);
+
+ priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ bzobj = _bzobjs_get (self, object_path);
+
+ if (!bzobj) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "device %s does not exist",
+ object_path);
+ return FALSE;
+ }
+
+ if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "device %s is not usable",
+ object_path);
+ return FALSE;
+ }
+
+ if (!NM_FLAGS_ALL (bzobj->d_device_capabilities, connection_bt_type)) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "device %s has not the required capabilities",
+ object_path);
+ return FALSE;
+ }
+
+#if !WITH_BLUEZ5_DUN
+ if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "DUN is not supported");
+ return FALSE;
+ }
+#endif
+
+ _connect_disconnect (self, bzobj, "new activation");
+
+ _LOGD ("%s [%s]: connecting...",
+ nm_bluetooth_capability_to_string (connection_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->object_path);
+
+ int_cancellable = g_cancellable_new();
+
+#if WITH_BLUEZ5_DUN
+ if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_DEVICE_INTERFACE,
+ "Connect",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ timeout_msec,
+ int_cancellable,
+ _connect_dun_step1_cb,
+ bzobj);
+ } else
+#endif
+ {
+ nm_assert (connection_bt_type == NM_BT_CAPABILITY_NAP);
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_NETWORK_INTERFACE,
+ "Connect",
+ g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ timeout_msec,
+ int_cancellable,
+ _connect_nap_cb,
+ bzobj);
+ }
+
+ c_req_data = g_slice_new (DeviceConnectReqData);
+ *c_req_data = (DeviceConnectReqData) {
+ .int_cancellable = g_steal_pointer (&int_cancellable),
+ .ext_cancellable = g_object_ref (cancellable),
+ .callback = callback,
+ .callback_user_data = callback_user_data,
+ .ext_cancelled_id = g_signal_connect (cancellable,
+ "cancelled",
+ G_CALLBACK (_connect_cancelled_cb),
+ bzobj),
+ .timeout_id = g_timeout_add (timeout_msec,
+ _connect_timeout_cb,
+ bzobj),
+ };
+
+ bzobj->x_device_connect_bt_type = connection_bt_type;
+ bzobj->x_device.c_req_data = c_req_data;
+
+ return TRUE;
+}
+
+void
+nm_bluez_manager_disconnect (NMBluezManager *self,
+ const char *object_path)
+{
+ BzDBusObj *bzobj;
+
+ g_return_if_fail (NM_IS_BLUEZ_MANAGER (self));
+ g_return_if_fail (object_path);
+
+ bzobj = _bzobjs_get (self, object_path);
+ if (!bzobj)
+ return;
+
+ _connect_disconnect (self, bzobj, "disconnected by user");
+}
+
+/*****************************************************************************/
+
static NMDevice *
create_device (NMDeviceFactory *factory,
const char *iface,
@@ -334,8 +2790,8 @@ create_device (NMDeviceFactory *factory,
NMConnection *connection,
gboolean *out_ignore)
{
- g_warn_if_fail (plink->type == NM_LINK_TYPE_BNEP);
*out_ignore = TRUE;
+ g_return_val_if_fail (plink->type == NM_LINK_TYPE_BNEP, NULL);
return NULL;
}
@@ -360,7 +2816,25 @@ nm_bluez_manager_init (NMBluezManager *self)
{
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ priv->vtable_network_server = (NMBtVTableNetworkServer) {
+ .is_available = _network_server_vt_is_available,
+ .register_bridge = _network_server_vt_register_bridge,
+ .unregister_bridge = _network_server_vt_unregister_bridge,
+ };
+
+ c_list_init (&priv->network_server_lst_head);
+ c_list_init (&priv->process_change_lst_head);
+
+ priv->conn_data_heads = g_hash_table_new_full (_conn_data_head_hash, _conn_data_head_equal, g_free, NULL);
+ priv->conn_data_elems = g_hash_table_new_full (nm_pdirect_hash, nm_pdirect_equal, nm_g_slice_free_fcn (ConnDataElem), NULL);
+
+ priv->bzobjs = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, (GDestroyNotify) _bz_dbus_obj_free, NULL);
+
+ priv->manager = g_object_ref (NM_MANAGER_GET);
priv->settings = g_object_ref (NM_SETTINGS_GET);
+ priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
+
+ g_atomic_pointer_compare_and_exchange (&nm_bt_vtable_network_server, NULL, &priv->vtable_network_server);
}
static void
@@ -369,16 +2843,25 @@ dispose (GObject *object)
NMBluezManager *self = NM_BLUEZ_MANAGER (object);
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
- if (priv->manager5) {
- g_signal_handlers_disconnect_by_data (priv->manager5, self);
- g_clear_object (&priv->manager5);
- }
+ /* FIXME(shutdown): we need a nm_device_factory_stop() hook to first unregister all
+ * BzDBusObj instances and do necessary cleanup actions (like disconnecting devices
+ * or deleting panu_connection). */
+
+ nm_assert (c_list_is_empty (&priv->network_server_lst_head));
+ nm_assert (c_list_is_empty (&priv->process_change_lst_head));
+ nm_assert (priv->process_change_idle_id == 0);
- cleanup_checking (self, TRUE);
+ g_atomic_pointer_compare_and_exchange (&nm_bt_vtable_network_server, &priv->vtable_network_server, NULL);
+
+ _cleanup_all (self);
G_OBJECT_CLASS (nm_bluez_manager_parent_class)->dispose (object);
g_clear_object (&priv->settings);
+ g_clear_object (&priv->manager);
+ g_clear_object (&priv->dbus_connection);
+
+ nm_clear_pointer (&priv->bzobjs, g_hash_table_destroy);
}
static void
@@ -387,10 +2870,10 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS (klass);
- object_class->dispose = dispose;
+ object_class->dispose = dispose;
factory_class->get_supported_types = get_supported_types;
- factory_class->create_device = create_device;
- factory_class->match_connection = match_connection;
- factory_class->start = start;
+ factory_class->create_device = create_device;
+ factory_class->match_connection = match_connection;
+ factory_class->start = start;
}
diff --git a/src/devices/bluetooth/nm-bluez-manager.h b/src/devices/bluetooth/nm-bluez-manager.h
new file mode 100644
index 0000000000..85dbaa838a
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez-manager.h
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2009 - 2019 Red Hat, Inc.
+ */
+
+#ifndef __NM_BLUEZ_MANAGER_H__
+#define __NM_BLUEZ_MANAGER_H__
+
+#define NM_TYPE_BLUEZ_MANAGER (nm_bluez_manager_get_type ())
+#define NM_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager))
+#define NM_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+#define NM_IS_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER))
+#define NM_IS_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ_MANAGER))
+#define NM_BLUEZ_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+
+typedef struct _NMBluezManager NMBluezManager;
+typedef struct _NMBluezManagerClass NMBluezManagerClass;
+
+GType nm_bluez_manager_get_type (void);
+
+typedef void (*NMBluezManagerConnectCb) (NMBluezManager *self,
+ gboolean is_completed /* or else is early notification with DUN path */,
+ const char *device_name,
+ GError *error,
+ gpointer user_data);
+
+gboolean nm_bluez_manager_connect (NMBluezManager *self,
+ const char *object_path,
+ NMBluetoothCapabilities connection_bt_type,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMBluezManagerConnectCb callback,
+ gpointer callback_user_data,
+ GError **error);
+
+void nm_bluez_manager_disconnect (NMBluezManager *self,
+ const char *object_path);
+
+#endif /* __NM_BLUEZ_MANAGER_H__ */
diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c
index 1338bcb811..3038e2635f 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.c
+++ b/src/devices/bluetooth/nm-bluez5-dun.c
@@ -19,145 +19,407 @@
#include "nm-bt-error.h"
#include "NetworkManagerUtils.h"
+#define RFCOMM_FMT "/dev/rfcomm%d"
+
+/*****************************************************************************/
+
+typedef struct {
+ GCancellable *cancellable;
+ NMBluez5DunConnectCb callback;
+ gpointer callback_user_data;
+
+ sdp_session_t *sdp_session;
+
+ GError *rfcomm_sdp_search_error;
+
+ gint64 connect_open_tty_started_at;
+
+ gulong cancelled_id;
+
+ guint source_id;
+
+ guint8 sdp_session_try_count;
+} ConnectData;
+
struct _NMBluez5DunContext {
+ const char *dst_str;
+
+ ConnectData *cdat;
+
+ NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb;
+ gpointer notify_tty_hangup_user_data;
+
+ char *rfcomm_tty_path;
+
+ int rfcomm_sock_fd;
+ int rfcomm_tty_fd;
+ int rfcomm_tty_no;
+ int rfcomm_channel;
+
+ guint rfcomm_tty_poll_id;
+
bdaddr_t src;
bdaddr_t dst;
- char *src_str;
- char *dst_str;
- int rfcomm_channel;
- int rfcomm_fd;
- int rfcomm_tty_fd;
- int rfcomm_id;
- NMBluez5DunFunc callback;
- gpointer user_data;
- sdp_session_t *sdp_session;
- guint sdp_watch_id;
+
+ char src_str[];
};
-static void
-dun_connect (NMBluez5DunContext *context)
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN LOGD_BT
+#define _NMLOG_PREFIX_NAME "bluez"
+#define _NMLOG(level, context, ...) \
+ G_STMT_START { \
+ if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
+ const NMBluez5DunContext *const _context = (context); \
+ \
+ _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
+ "%s: DUN[%s] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ _NMLOG_PREFIX_NAME, \
+ _context->src_str \
+ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
+ } G_STMT_END
+
+/*****************************************************************************/
+
+static void _context_invoke_callback_success (NMBluez5DunContext *context);
+static void _context_invoke_callback_fail_and_free (NMBluez5DunContext *context,
+ GError *error);
+static void _context_free (NMBluez5DunContext *context);
+static int _connect_open_tty (NMBluez5DunContext *context);
+static gboolean _connect_sdp_session_start (NMBluez5DunContext *context,
+ GError **error);
+
+/*****************************************************************************/
+
+NM_AUTO_DEFINE_FCN0 (NMBluez5DunContext *, _nm_auto_free_context, _context_free)
+#define nm_auto_free_context nm_auto(_nm_auto_free_context)
+
+/*****************************************************************************/
+
+const char *
+nm_bluez5_dun_context_get_adapter (const NMBluez5DunContext *context)
{
- struct sockaddr_rc sa;
- int devid, try = 30;
- char tty[100];
- const int ttylen = sizeof (tty) - 1;
- GError *error = NULL;
- int errsv;
+ return context->src_str;
+}
- struct rfcomm_dev_req req = {
- .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
- .dev_id = -1,
- .channel = context->rfcomm_channel
- };
+const char *
+nm_bluez5_dun_context_get_remote (const NMBluez5DunContext *context)
+{
+ return context->dst_str;
+}
- context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM | SOCK_CLOEXEC, BTPROTO_RFCOMM);
- if (context->rfcomm_fd < 0) {
- errsv = errno;
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to create RFCOMM socket: (%d) %s",
- errsv, nm_strerror_native (errsv));
- goto done;
- }
+const char *
+nm_bluez5_dun_context_get_rfcomm_dev (const NMBluez5DunContext *context)
+{
+ return context->rfcomm_tty_path;
+}
- /* Connect to the remote device */
- sa.rc_family = AF_BLUETOOTH;
- sa.rc_channel = 0;
- memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN);
- if (bind (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof(sa))) {
- errsv = errno;
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to bind socket: (%d) %s",
- errsv, nm_strerror_native (errsv));
- goto done;
+/*****************************************************************************/
+
+static gboolean
+_rfcomm_tty_poll_cb (GIOChannel *stream,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+
+ _LOGD (context, "receive %s%s%s signal on rfcomm file descriptor",
+ NM_FLAGS_HAS (condition, G_IO_ERR) ? "ERR" : "",
+ NM_FLAGS_ALL (condition, G_IO_HUP | G_IO_ERR) ? "," : "",
+ NM_FLAGS_HAS (condition, G_IO_HUP) ? "HUP" : "");
+
+ context->rfcomm_tty_poll_id = 0;
+ context->notify_tty_hangup_cb (context,
+ context->notify_tty_hangup_user_data);
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_open_tty_retry_cb (gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ int r;
+
+ r = _connect_open_tty (context);
+ if (r >= 0)
+ return G_SOURCE_REMOVE;
+
+ if (nm_utils_get_monotonic_timestamp_ns () > context->cdat->connect_open_tty_started_at + (30 * 100 * NM_UTILS_NS_PER_MSEC)) {
+ gs_free_error GError *error = NULL;
+
+ context->cdat->source_id = 0;
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "give up waiting to open %s device: %s (%d)",
+ context->rfcomm_tty_path,
+ nm_strerror_native (r),
+ -r);
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
}
- sa.rc_channel = context->rfcomm_channel;
- memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN);
- if (connect (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof (sa)) ) {
- errsv = errno;
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to connect to remote device: (%d) %s",
- errsv, nm_strerror_native (errsv));
- goto done;
+ return G_SOURCE_CONTINUE;
+}
+
+static int
+_connect_open_tty (NMBluez5DunContext *context)
+{
+ nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
+ int fd;
+ int errsv;
+
+ fd = open (context->rfcomm_tty_path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+ if (fd < 0) {
+ errsv = NM_ERRNO_NATIVE (errno);
+
+ if (context->cdat->source_id == 0) {
+ _LOGD (context, "failed opening tty "RFCOMM_FMT": %s (%d). Start polling...",
+ context->rfcomm_tty_no,
+ nm_strerror_native (errsv),
+ errsv);
+ context->cdat->connect_open_tty_started_at = nm_utils_get_monotonic_timestamp_ns ();
+ context->cdat->source_id = g_timeout_add (100,
+ _connect_open_tty_retry_cb,
+ context);
+ }
+ return -errsv;
}
- nm_log_dbg (LOGD_BT, "(%s): connected to %s on channel %d",
- context->src_str, context->dst_str, context->rfcomm_channel);
+ context->rfcomm_tty_fd = fd;
+
+ io_channel = g_io_channel_unix_new (context->rfcomm_tty_fd);
+ context->rfcomm_tty_poll_id = g_io_add_watch (io_channel,
+ G_IO_ERR | G_IO_HUP,
+ _rfcomm_tty_poll_cb,
+ context);
+
+ _context_invoke_callback_success (context);
+ return 0;
+}
+
+static void
+_connect_create_rfcomm (NMBluez5DunContext *context)
+{
+ gs_free_error GError *error = NULL;
+ struct rfcomm_dev_req req;
+ int devid;
+ int errsv;
+ int r;
+
+ _LOGD (context, "connected to %s on channel %d",
+ context->dst_str, context->rfcomm_channel);
/* Create an RFCOMM kernel device for the DUN channel */
+ memset (&req, 0, sizeof (req));
+ req.dev_id = -1;
+ req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+ req.channel = context->rfcomm_channel;
memcpy (&req.src, &context->src, ETH_ALEN);
memcpy (&req.dst, &context->dst, ETH_ALEN);
- devid = ioctl (context->rfcomm_fd, RFCOMMCREATEDEV, &req);
+ devid = ioctl (context->rfcomm_sock_fd, RFCOMMCREATEDEV, &req);
if (devid < 0) {
- errsv = errno;
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to create rfcomm device: (%d) %s",
- errsv, nm_strerror_native (errsv));
- goto done;
+ errsv = NM_ERRNO_NATIVE (errno);
+ if (errsv == EBADFD) {
+ /* hm. We use a non-blocking socket to connect. Above getsockopt(SOL_SOCKET,SO_ERROR) indicated
+ * success, but still now we fail with EBADFD. I think that is a bug and we should get the
+ * failure during connect().
+ *
+ * Anyway, craft a less confusing error message than
+ * "failed to create rfcomm device: File descriptor in bad state (77)". */
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "unknown failure to connect to DUN device");
+ } else {
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to create rfcomm device: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ }
+ _context_invoke_callback_fail_and_free (context, error);
+ return;
}
- context->rfcomm_id = devid;
- snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
- while ((context->rfcomm_tty_fd = open (tty, O_RDONLY | O_NOCTTY | O_CLOEXEC)) < 0 && try--) {
- if (try) {
- g_usleep (100 * 1000);
- continue;
- }
+ context->rfcomm_tty_no = devid;
+ context->rfcomm_tty_path = g_strdup_printf (RFCOMM_FMT, devid);
+
+ r = _connect_open_tty (context);
+ if (r < 0) {
+ /* we created the rfcomm device, but cannot yet open it. That means, we are
+ * not yet fully connected. However, we notify the caller about "what we learned
+ * so far". Note that this happens synchronously.
+ *
+ * The purpose is that once we proceed synchrnously, modem-manager races with
+ * the detection of the modem. We want to notify the caller first about the
+ * device name. */
+ context->cdat->callback (NULL,
+ context->rfcomm_tty_path,
+ NULL,
+ context->cdat->callback_user_data);
+ }
+}
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to find rfcomm device: %s",
- tty);
- break;
+static gboolean
+_connect_socket_connect_cb (GIOChannel *stream,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ gs_free GError *error = NULL;
+ int errsv = 0;
+ socklen_t slen = sizeof(errsv);
+ int r;
+
+ context->cdat->source_id = 0;
+
+ r = getsockopt (context->rfcomm_sock_fd, SOL_SOCKET, SO_ERROR, &errsv, &slen);
+
+ if (r < 0) {
+ errsv = errno;
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to complete connecting RFCOMM socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
}
-done:
- context->callback (context, tty, error, context->user_data);
+ if (errsv != 0) {
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to connect RFCOMM socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
+ }
+
+ _connect_create_rfcomm (context);
+ return G_SOURCE_REMOVE;
}
static void
-sdp_search_cleanup (NMBluez5DunContext *context)
+_connect_socket_connect (NMBluez5DunContext *context)
{
- if (context->sdp_session) {
- sdp_close (context->sdp_session);
- context->sdp_session = NULL;
+ gs_free_error GError *error = NULL;
+ struct sockaddr_rc sa;
+ int errsv;
+
+ context->rfcomm_sock_fd = socket (AF_BLUETOOTH, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_RFCOMM);
+ if (context->rfcomm_sock_fd < 0) {
+ errsv = errno;
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to create RFCOMM socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return;
+ }
+
+ /* Connect to the remote device */
+ memset (&sa, 0, sizeof (sa));
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = 0;
+ memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN);
+ if (bind (context->rfcomm_sock_fd,
+ (struct sockaddr *) &sa,
+ sizeof(sa)) != 0) {
+ errsv = errno;
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to bind socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return;
+ }
+
+ memset (&sa, 0, sizeof (sa));
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = context->rfcomm_channel;
+ memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN);
+ if (connect (context->rfcomm_sock_fd,
+ (struct sockaddr *) &sa,
+ sizeof (sa)) != 0) {
+ nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
+
+ errsv = errno;
+ if (errsv != EINPROGRESS) {
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to connect to remote device: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return;
+ }
+
+ _LOGD (context, "connecting to %s on channel %d...",
+ context->dst_str,
+ context->rfcomm_channel);
+
+ io_channel = g_io_channel_unix_new (context->rfcomm_sock_fd);
+ context->cdat->source_id = g_io_add_watch (io_channel,
+ G_IO_OUT,
+ _connect_socket_connect_cb,
+ context);
+ return;
}
- nm_clear_g_source (&context->sdp_watch_id);
+ _connect_create_rfcomm (context);
}
static void
-sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data)
+_connect_sdp_search_cb (uint8_t type,
+ uint16_t status,
+ uint8_t *rsp,
+ size_t size,
+ void *user_data)
{
NMBluez5DunContext *context = user_data;
- int scanned, seqlen = 0, bytesleft = size;
+ int scanned;
+ int seqlen = 0;
+ int bytesleft = size;
uint8_t dataType;
int channel = -1;
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished with type=%d status=%d",
- context->src_str, context->dst_str, status, type);
+ if ( context->cdat->rfcomm_sdp_search_error
+ || context->rfcomm_channel >= 0)
+ return;
+
+ _LOGD (context, "SDP search finished with type=%d status=%d",
+ status, type);
/* SDP response received */
- if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
- GError *error = g_error_new (NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Did not get a Service Discovery response");
- context->callback (context, NULL, error, context->user_data);
- goto done;
+ if ( status
+ || type != SDP_SVC_SEARCH_ATTR_RSP) {
+ g_set_error (&context->cdat->rfcomm_sdp_search_error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "did not get a Service Discovery response");
+ return;
}
scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP sequence type scanned=%d length=%d",
- context->src_str, context->dst_str, scanned, seqlen);
+ _LOGD (context, "SDP sequence type scanned=%d length=%d",
+ scanned, seqlen);
scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
- if (!scanned || !seqlen) {
+ if ( !scanned
+ || !seqlen) {
/* Short read or unknown sequence type */
- GError *error = g_error_new (NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Improper Service Discovery response");
- context->callback (context, NULL, error, context->user_data);
- goto done;
+ g_set_error (&context->cdat->rfcomm_sdp_search_error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "improper Service Discovery response");
+ return;
}
rsp += scanned;
@@ -181,90 +443,153 @@ sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t siz
channel = sdp_get_proto_port (protos, RFCOMM_UUID);
sdp_list_free (protos, NULL);
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP channel=%d",
- context->src_str, context->dst_str, channel);
+ _LOGD (context, "SDP channel=%d",
+ channel);
}
sdp_record_free (rec);
scanned += recsize;
rsp += recsize;
bytesleft -= recsize;
- } while ((scanned < (ssize_t) size) && (bytesleft > 0) && (channel < 0));
-
-done:
- if (channel != -1) {
- context->rfcomm_channel = channel;
- dun_connect (context);
+ } while ( scanned < (ssize_t) size
+ && bytesleft > 0
+ && channel < 0);
+
+ if (channel == -1) {
+ g_set_error (&context->cdat->rfcomm_sdp_search_error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "did not receive rfcomm-channel");
+ return;
}
- sdp_search_cleanup (context);
+ context->rfcomm_channel = channel;
}
static gboolean
-sdp_search_process_cb (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+_connect_sdp_search_io_cb (GIOChannel *io_channel,
+ GIOCondition condition,
+ gpointer user_data)
{
NMBluez5DunContext *context = user_data;
-
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search progressed with condition=%d",
- context->src_str, context->dst_str, condition);
+ gs_free GError *error = NULL;
+ int errsv;
if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
- GError *error = g_error_new (NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Service Discovery interrupted");
- context->callback (context, NULL, error, context->user_data);
- sdp_search_cleanup (context);
- return FALSE;
+ _LOGD (context, "SDP search returned with invalid IO condition 0x%x",
+ (guint) condition);
+ error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Service Discovery interrupted");
+ context->cdat->source_id = 0;
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
}
- if (sdp_process (context->sdp_session) < 0) {
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished",
- context->src_str, context->dst_str);
+ if (sdp_process (context->cdat->sdp_session) == 0) {
+ _LOGD (context, "SDP search still not finished");
+ return G_SOURCE_CONTINUE;
+ }
- /* Search finished successfully. */
- return FALSE;
+ context->cdat->source_id = 0;
+
+ if ( context->rfcomm_channel < 0
+ && !context->cdat->rfcomm_sdp_search_error) {
+ errsv = sdp_get_error (context->cdat->sdp_session);
+ _LOGD (context, "SDP search failed: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Service Discovery failed with %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
}
- /* Search progressed successfully. */
- return TRUE;
+ if (context->cdat->rfcomm_sdp_search_error) {
+ _LOGD (context, "SDP search failed to complete: %s", context->cdat->rfcomm_sdp_search_error->message);
+ _context_invoke_callback_fail_and_free (context, context->cdat->rfcomm_sdp_search_error);
+ return G_SOURCE_REMOVE;
+ }
+
+ nm_clear_pointer (&context->cdat->sdp_session, sdp_close);
+
+ _connect_socket_connect (context);
+
+ return G_SOURCE_REMOVE;
}
static gboolean
-sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+_connect_sdp_session_start_on_idle_cb (gpointer user_data)
{
NMBluez5DunContext *context = user_data;
- sdp_list_t *search, *attrs;
+ gs_free_error GError *error = NULL;
+
+ context->cdat->source_id = 0;
+
+ _LOGD (context, "retry starting sdp-session...");
+
+ if (!_connect_sdp_session_start (context, &error))
+ _context_invoke_callback_fail_and_free (context, error);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_sdp_io_cb (GIOChannel *io_channel,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ sdp_list_t *search;
+ sdp_list_t *attrs;
uuid_t svclass;
uint16_t attr;
- int fd, fd_err = 0;
- int err;
+ int fd;
+ int errsv;
+ int fd_err = 0;
+ int r;
socklen_t len = sizeof (fd_err);
- GError *error = NULL;
+ gs_free_error GError *error = NULL;
+
+ context->cdat->source_id = 0;
- context->sdp_watch_id = 0;
+ fd = g_io_channel_unix_get_fd (io_channel);
+
+ _LOGD (context, "sdp-session ready to connect with fd=%d", fd);
- fd = g_io_channel_unix_get_fd (channel);
if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &fd_err, &len) < 0) {
- err = errno;
- nm_log_dbg (LOGD_BT, "(%s -> %s): getsockopt error=%d",
- context->src_str, context->dst_str, err);
- } else {
- err = fd_err;
- nm_log_dbg (LOGD_BT, "(%s -> %s): SO_ERROR error=%d",
- context->src_str, context->dst_str, fd_err);
+ errsv = NM_ERRNO_NATIVE (errno);
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "error for getsockopt on Service Discovery socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ goto done;
}
- if (err != 0) {
+ if (fd_err != 0) {
+ errsv = nm_errno_native (fd_err);
+
+ if ( NM_IN_SET (errsv, ECONNREFUSED, EHOSTDOWN)
+ && --context->cdat->sdp_session_try_count > 0) {
+ /* *sigh* */
+ _LOGD (context, "sdp-session failed with %s (%d). Retry in a bit", nm_strerror_native (errsv), errsv);
+ nm_clear_g_source (&context->cdat->source_id);
+ context->cdat->source_id = g_timeout_add (1000,
+ _connect_sdp_session_start_on_idle_cb,
+ context);
+ return G_SOURCE_REMOVE;
+ }
+
error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Error on Service Discovery socket: (%d) %s",
- err, nm_strerror_native (err));
+ "error on Service Discovery socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
goto done;
}
- if (sdp_set_notify (context->sdp_session, sdp_search_completed_cb, context) < 0) {
+ if (sdp_set_notify (context->cdat->sdp_session, _connect_sdp_search_cb, context) < 0) {
/* Should not be reached, only can fail if we passed bad sdp_session. */
error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Could not request Service Discovery notification");
+ "could not set Service Discovery notification");
goto done;
}
@@ -273,124 +598,261 @@ sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_da
attr = SDP_ATTR_PROTO_DESC_LIST;
attrs = sdp_list_append (NULL, &attr);
- if (!sdp_service_search_attr_async (context->sdp_session, search, SDP_ATTR_REQ_INDIVIDUAL, attrs)) {
- /* Set callback responsible for update the internal SDP transaction */
- context->sdp_watch_id = g_io_add_watch (channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- sdp_search_process_cb,
- context);
- } else {
- err = sdp_get_error (context->sdp_session);
- error = g_error_new (NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Error starting Service Discovery: (%d) %s",
- err, nm_strerror_native (err));
- }
+ r = sdp_service_search_attr_async (context->cdat->sdp_session,
+ search,
+ SDP_ATTR_REQ_INDIVIDUAL,
+ attrs);
sdp_list_free (attrs, NULL);
sdp_list_free (search, NULL);
-done:
- if (error) {
- context->callback (context, NULL, error, context->user_data);
- sdp_search_cleanup (context);
+ if (r < 0) {
+ errsv = nm_errno_native (sdp_get_error (context->cdat->sdp_session));
+ error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "error starting Service Discovery: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ goto done;
}
+ /* Set callback responsible for update the internal SDP transaction */
+ context->cdat->source_id = g_io_add_watch (io_channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ _connect_sdp_search_io_cb,
+ context);
+
+done:
+ if (error)
+ _context_invoke_callback_fail_and_free (context, error);
return G_SOURCE_REMOVE;
}
-NMBluez5DunContext *
-nm_bluez5_dun_new (const char *adapter,
- const char *remote)
+/*****************************************************************************/
+static void
+_connect_cancelled_cb (GCancellable *cancellable,
+ NMBluez5DunContext *context)
{
- NMBluez5DunContext *context;
-
- context = g_slice_new0 (NMBluez5DunContext);
- str2ba (adapter, &context->src);
- str2ba (remote, &context->dst);
- context->src_str = g_strdup (adapter);
- context->dst_str = g_strdup (remote);
- context->rfcomm_channel = -1;
- context->rfcomm_id = -1;
- context->rfcomm_fd = -1;
- return context;
+ gs_free_error GError *error = NULL;
+
+ if (!g_cancellable_set_error_if_cancelled (cancellable, &error))
+ g_return_if_reached ();
+
+ _context_invoke_callback_fail_and_free (context, error);
}
-void
-nm_bluez5_dun_connect (NMBluez5DunContext *context,
- NMBluez5DunFunc callback,
- gpointer user_data)
+static gboolean
+_connect_sdp_session_start (NMBluez5DunContext *context,
+ GError **error)
{
- GIOChannel *channel;
+ nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
- context->callback = callback;
- context->user_data = user_data;
+ nm_assert (context->cdat);
- if (context->rfcomm_channel != -1) {
- nm_log_dbg (LOGD_BT, "(%s): channel number on device %s cached: %d",
- context->src_str, context->dst_str, context->rfcomm_channel);
- /* FIXME: don't invoke the callback synchronously. */
- dun_connect (context);
- return;
+ nm_clear_g_source (&context->cdat->source_id);
+ nm_clear_pointer (&context->cdat->sdp_session, sdp_close);
+
+ context->cdat->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
+ if (!context->cdat->sdp_session) {
+ int errsv = nm_errno_native (errno);
+
+ g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to connect to the SDP server: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ return FALSE;
}
- nm_log_dbg (LOGD_BT, "(%s): starting channel number discovery for device %s",
- context->src_str, context->dst_str);
+ io_channel = g_io_channel_unix_new (sdp_get_socket (context->cdat->sdp_session));
+ context->cdat->source_id = g_io_add_watch (io_channel,
+ G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ _connect_sdp_io_cb,
+ context);
+ return TRUE;
+}
- context->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
- if (!context->sdp_session) {
- int err = errno;
- GError *error;
+/*****************************************************************************/
+
+gboolean
+nm_bluez5_dun_connect (const char *adapter,
+ const char *remote,
+ GCancellable *cancellable,
+ NMBluez5DunConnectCb callback,
+ gpointer callback_user_data,
+ NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb,
+ gpointer notify_tty_hangup_user_data,
+ GError **error)
+{
+ nm_auto_free_context NMBluez5DunContext *context = NULL;
+ ConnectData *cdat;
+ gsize src_l;
+ gsize dst_l;
+
+ g_return_val_if_fail (adapter, FALSE);
+ g_return_val_if_fail (remote, FALSE);
+ g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (callback, FALSE);
+ g_return_val_if_fail (notify_tty_hangup_cb, FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+ nm_assert (!g_cancellable_is_cancelled (cancellable));
+
+ src_l = strlen (adapter) + 1;
+ dst_l = strlen (remote) + 1;
+
+ cdat = g_slice_new (ConnectData);
+ *cdat = (ConnectData) {
+ .callback = callback,
+ .callback_user_data = callback_user_data,
+ .cancellable = g_object_ref (cancellable),
+ .sdp_session_try_count = 5,
+ };
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to connect to the SDP server: (%d) %s",
- err, nm_strerror_native (err));
- /* FIXME: don't invoke the callback synchronously. */
- context->callback (context, NULL, error, context->user_data);
- return;
+ context = g_malloc (sizeof (NMBluez5DunContext) + src_l + dst_l);
+ *context = (NMBluez5DunContext) {
+ .cdat = cdat,
+ .notify_tty_hangup_cb = notify_tty_hangup_cb,
+ .notify_tty_hangup_user_data = notify_tty_hangup_user_data,
+ .rfcomm_tty_no = -1,
+ .rfcomm_sock_fd = -1,
+ .rfcomm_tty_fd = -1,
+ .rfcomm_channel = -1,
+ };
+ memcpy (&context->src_str[0], adapter, src_l);
+ context->dst_str = &context->src_str[src_l];
+ memcpy ((char *) context->dst_str, remote, dst_l);
+
+ if (str2ba (adapter, &context->src) < 0) {
+ g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "invalid source");
+ return FALSE;
}
- /* FIXME(shutdown): make connect cancellable. */
- channel = g_io_channel_unix_new (sdp_get_socket (context->sdp_session));
- context->sdp_watch_id = g_io_add_watch (channel,
- G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- sdp_connect_watch,
- context);
- g_io_channel_unref (channel);
+ if (str2ba (remote, &context->dst) < 0) {
+ g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "invalid remote");
+ return FALSE;
+ }
+
+ context->cdat->cancelled_id = g_signal_connect (context->cdat->cancellable,
+ "cancelled",
+ G_CALLBACK (_connect_cancelled_cb),
+ context);
+
+ if (!_connect_sdp_session_start (context, error))
+ return FALSE;
+
+ _LOGD (context, "starting channel number discovery for device %s",
+ context->dst_str);
+
+ g_steal_pointer (&context);
+ return TRUE;
}
-/* Only clean up connection-related stuff to allow reconnect */
+/*****************************************************************************/
+
void
-nm_bluez5_dun_cleanup (NMBluez5DunContext *context)
+nm_bluez5_dun_disconnect (NMBluez5DunContext *context)
{
- g_return_if_fail (context != NULL);
+ nm_assert (context);
+ nm_assert (!context->cdat);
- sdp_search_cleanup (context);
+ _LOGD (context, "disconnecting DUN connection");
- if (context->rfcomm_fd >= 0) {
- if (context->rfcomm_id >= 0) {
- struct rfcomm_dev_req req = { 0 };
+ _context_free (context);
+}
- req.dev_id = context->rfcomm_id;
- (void) ioctl (context->rfcomm_fd, RFCOMMRELEASEDEV, &req);
- context->rfcomm_id = -1;
- }
- nm_close (context->rfcomm_fd);
- context->rfcomm_fd = -1;
- }
+/*****************************************************************************/
- nm_close (context->rfcomm_tty_fd);
- context->rfcomm_tty_fd = -1;
+static void
+_context_cleanup_connect_data (NMBluez5DunContext *context)
+{
+ ConnectData *cdat;
+
+ cdat = g_steal_pointer (&context->cdat);
+ if (!cdat)
+ return;
+
+ nm_clear_g_signal_handler (cdat->cancellable, &cdat->cancelled_id);
+
+ nm_clear_g_source (&cdat->source_id);
+
+ nm_clear_pointer (&cdat->sdp_session, sdp_close);
+
+ g_clear_object (&cdat->cancellable);
+
+ g_clear_error (&cdat->rfcomm_sdp_search_error);
+
+ nm_g_slice_free (cdat);
}
-void
-nm_bluez5_dun_free (NMBluez5DunContext *context)
+static void
+_context_invoke_callback (NMBluez5DunContext *context,
+ GError *error)
+{
+ NMBluez5DunConnectCb callback;
+ gpointer callback_user_data;
+
+ nm_assert (context);
+ nm_assert (context->cdat);
+ nm_assert (context->cdat->callback);
+ nm_assert (error || context->rfcomm_tty_path);
+
+ if (!error)
+ _LOGD (context, "connected via \"%s\"", context->rfcomm_tty_path);
+ else if (nm_utils_error_is_cancelled (error, FALSE))
+ _LOGD (context, "cancelled");
+ else
+ _LOGD (context, "failed to connect: %s", error->message);
+
+ callback = context->cdat->callback;
+ callback_user_data = context->cdat->callback_user_data;
+
+ _context_cleanup_connect_data (context);
+
+ callback (error ? NULL : context,
+ error ? NULL : context->rfcomm_tty_path,
+ error,
+ callback_user_data);
+}
+
+static void
+_context_invoke_callback_success (NMBluez5DunContext *context)
{
- g_return_if_fail (context != NULL);
+ nm_assert (context->rfcomm_tty_path);
+ _context_invoke_callback (context, NULL);
+}
+
+static void
+_context_invoke_callback_fail_and_free (NMBluez5DunContext *context,
+ GError *error)
+{
+ nm_assert (error);
+ _context_invoke_callback (context, error);
+ _context_free (context);
+}
+
+static void
+_context_free (NMBluez5DunContext *context)
+{
+ nm_assert (context);
+
+ _context_cleanup_connect_data (context);
+
+ nm_clear_g_source (&context->rfcomm_tty_poll_id);
+
+ if (context->rfcomm_sock_fd >= 0) {
+ if (context->rfcomm_tty_no >= 0) {
+ struct rfcomm_dev_req req;
+
+ memset (&req, 0, sizeof (struct rfcomm_dev_req));
+ req.dev_id = context->rfcomm_tty_no;
+ context->rfcomm_tty_no = -1;
+ (void) ioctl (context->rfcomm_sock_fd, RFCOMMRELEASEDEV, &req);
+ }
+ nm_close (nm_steal_fd (&context->rfcomm_sock_fd));
+ }
- nm_bluez5_dun_cleanup (context);
- g_clear_pointer (&context->src_str, g_free);
- g_clear_pointer (&context->dst_str, g_free);
- g_slice_free (NMBluez5DunContext, context);
+ if (context->rfcomm_tty_fd >= 0)
+ nm_close (nm_steal_fd (&context->rfcomm_tty_fd));
+ nm_clear_g_free (&context->rfcomm_tty_path);
+ g_free (context);
}
diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h
index d09046fa3d..6142eac26c 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.h
+++ b/src/devices/bluetooth/nm-bluez5-dun.h
@@ -4,27 +4,36 @@
* Copyright (C) 2014 Red Hat, Inc.
*/
-#ifndef _NM_BLUEZ5_UTILS_H_
-#define _NM_BLUEZ5_UTILS_H_
+#ifndef __NM_BLUEZ5_DUN_H__
+#define __NM_BLUEZ5_DUN_H__
typedef struct _NMBluez5DunContext NMBluez5DunContext;
-typedef void (*NMBluez5DunFunc) (NMBluez5DunContext *context,
- const char *rfcomm_dev,
- GError *error,
- gpointer user_data);
+#if WITH_BLUEZ5_DUN
-NMBluez5DunContext *nm_bluez5_dun_new (const char *adapter,
- const char *remote);
+typedef void (*NMBluez5DunConnectCb) (NMBluez5DunContext *context,
+ const char *rfcomm_dev,
+ GError *error,
+ gpointer user_data);
-void nm_bluez5_dun_connect (NMBluez5DunContext *context,
- NMBluez5DunFunc callback,
- gpointer user_data);
+typedef void (*NMBluez5DunNotifyTtyHangupCb) (NMBluez5DunContext *context,
+ gpointer user_data);
-/* Clean up connection resources */
-void nm_bluez5_dun_cleanup (NMBluez5DunContext *context);
+gboolean nm_bluez5_dun_connect (const char *adapter,
+ const char *remote,
+ GCancellable *cancellable,
+ NMBluez5DunConnectCb callback,
+ gpointer callback_user_data,
+ NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb,
+ gpointer notify_tty_hangup_user_data,
+ GError **error);
-/* Clean up and dispose all resources */
-void nm_bluez5_dun_free (NMBluez5DunContext *context);
+void nm_bluez5_dun_disconnect (NMBluez5DunContext *context);
-#endif /* _NM_BLUEZ5_UTILS_H_ */
+const char *nm_bluez5_dun_context_get_adapter (const NMBluez5DunContext *context);
+const char *nm_bluez5_dun_context_get_remote (const NMBluez5DunContext *context);
+const char *nm_bluez5_dun_context_get_rfcomm_dev (const NMBluez5DunContext *context);
+
+#endif /* WITH_BLUEZ5_DUN */
+
+#endif /* __NM_BLUEZ5_DUN_H__ */
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
deleted file mode 100644
index 7ac7e4c99a..0000000000
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ /dev/null
@@ -1,584 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* NetworkManager -- Network link manager
- *
- * Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2017 Red Hat, Inc.
- * Copyright (C) 2013 Intel Corporation.
- */
-
-#include "nm-default.h"
-
-#include "nm-bluez5-manager.h"
-
-#include <signal.h>
-#include <stdlib.h>
-
-#include "nm-core-internal.h"
-
-#include "c-list/src/c-list.h"
-#include "nm-bluez-device.h"
-#include "nm-bluez-common.h"
-#include "devices/nm-device-bridge.h"
-#include "settings/nm-settings.h"
-
-/*****************************************************************************/
-
-enum {
- BDADDR_ADDED,
- NETWORK_SERVER_ADDED,
- LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
- NMSettings *settings;
-
- GDBusProxy *proxy;
-
- GHashTable *devices;
-
- CList network_servers;
-} NMBluez5ManagerPrivate;
-
-struct _NMBluez5Manager {
- GObject parent;
- NMBtVTableNetworkServer network_server_vtable;
- NMBluez5ManagerPrivate _priv;
-};
-
-struct _NMBluez5ManagerClass {
- GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMBluez5Manager, nm_bluez5_manager, G_TYPE_OBJECT)
-
-#define NM_BLUEZ5_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluez5Manager, NM_IS_BLUEZ5_MANAGER)
-
-#define NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE(self) (&(self)->network_server_vtable)
-
-#define NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER(vtable) \
- NM_BLUEZ5_MANAGER(((char *)(vtable)) - offsetof (struct _NMBluez5Manager, network_server_vtable))
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN LOGD_BT
-#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez5", __VA_ARGS__)
-
-/*****************************************************************************/
-
-static void device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self);
-static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self);
-
-/*****************************************************************************/
-
-typedef struct {
- char *path;
- char *addr;
- NMDevice *device;
- CList lst_ns;
-} NetworkServer;
-
-static NetworkServer *
-_find_network_server (NMBluez5Manager *self, const char *path, NMDevice *device)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NetworkServer *network_server;
-
- nm_assert (path || NM_IS_DEVICE (device));
-
- c_list_for_each_entry (network_server, &priv->network_servers, lst_ns) {
- if (path && !nm_streq (network_server->path, path))
- continue;
- if (device && network_server->device != device)
- continue;
- return network_server;
- }
- return NULL;
-}
-
-static NetworkServer *
-_find_network_server_for_addr (NMBluez5Manager *self, const char *addr)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NetworkServer *network_server;
-
- c_list_for_each_entry (network_server, &priv->network_servers, lst_ns) {
- /* The address lookups need a server not assigned to a device
- * and tolerate an empty address as a wildcard for "any". */
- if ( !network_server->device
- && (!addr || nm_streq (network_server->addr, addr)))
- return network_server;
- }
- return NULL;
-}
-
-static void
-_network_server_unregister (NMBluez5Manager *self, NetworkServer *network_server)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- if (!network_server->device) {
- /* Not connected. */
- return;
- }
-
- _LOGI ("NAP: unregistering %s from %s",
- nm_device_get_iface (network_server->device),
- network_server->addr);
-
- g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
- NM_BLUEZ_SERVICE,
- network_server->path,
- NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
- "Unregister",
- g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1, NULL, NULL, NULL);
-
- g_clear_object (&network_server->device);
-}
-
-static void
-_network_server_free (NMBluez5Manager *self, NetworkServer *network_server)
-{
- _network_server_unregister (self, network_server);
- c_list_unlink_stale (&network_server->lst_ns);
- g_free (network_server->path);
- g_free (network_server->addr);
- g_slice_free (NetworkServer, network_server);
-}
-
-static gboolean
-network_server_is_available (const NMBtVTableNetworkServer *vtable,
- const char *addr)
-{
- NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
-
- return !!_find_network_server_for_addr (self, addr);
-}
-
-static gboolean
-network_server_register_bridge (const NMBtVTableNetworkServer *vtable,
- const char *addr,
- NMDevice *device)
-{
- NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NetworkServer *network_server = _find_network_server_for_addr (self, addr);
-
- nm_assert (NM_IS_DEVICE (device));
- nm_assert (!_find_network_server (self, NULL, device));
-
- if (!network_server) {
- /* The device checked that a network server is available, before
- * starting the activation, but for some reason it no longer is.
- * Indicate that the activation should not proceed. */
- _LOGI ("NAP: %s is not available for %s", addr, nm_device_get_iface (device));
- return FALSE;
- }
-
- _LOGI ("NAP: registering %s on %s", nm_device_get_iface (device), network_server->addr);
-
- g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
- NM_BLUEZ_SERVICE,
- network_server->path,
- NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
- "Register",
- g_variant_new ("(ss)", BLUETOOTH_CONNECT_NAP, nm_device_get_iface (device)),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1, NULL, NULL, NULL);
-
- network_server->device = g_object_ref (device);
-
- return TRUE;
-}
-
-static gboolean
-network_server_unregister_bridge (const NMBtVTableNetworkServer *vtable,
- NMDevice *device)
-{
- NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
- NetworkServer *network_server = _find_network_server (self, NULL, device);
-
- if (network_server)
- _network_server_unregister (self, network_server);
-
- return TRUE;
-}
-
-static void
-network_server_removed (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
- NetworkServer *network_server;
-
- network_server = _find_network_server (self, path, NULL);
- if (!network_server)
- return;
-
- if (network_server->device) {
- nm_device_queue_state (network_server->device, NM_DEVICE_STATE_DISCONNECTED,
- NM_DEVICE_STATE_REASON_BT_FAILED);
- }
- _LOGI ("NAP: removed interface %s", network_server->addr);
- _network_server_free (self, network_server);
-}
-
-static void
-network_server_added (GDBusProxy *proxy, const char *path, const char *addr, NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NetworkServer *network_server;
-
- /* If BlueZ messes up and announces a single network server twice,
- * make sure we get rid of the older instance first. */
- network_server_removed (proxy, path, self);
-
- network_server = g_slice_new0 (NetworkServer);
- network_server->path = g_strdup (path);
- network_server->addr = g_strdup (addr);
- c_list_link_before (&priv->network_servers, &network_server->lst_ns);
-
- _LOGI ("NAP: added interface %s", addr);
-
- g_signal_emit (self, signals[NETWORK_SERVER_ADDED], 0);
-}
-
-/*****************************************************************************/
-
-static void
-emit_bdaddr_added (NMBluez5Manager *self, NMBluezDevice *device)
-{
- g_signal_emit (self, signals[BDADDR_ADDED], 0,
- device,
- nm_bluez_device_get_address (device),
- nm_bluez_device_get_name (device),
- nm_bluez_device_get_path (device),
- nm_bluez_device_get_capabilities (device));
-}
-
-void
-nm_bluez5_manager_query_devices (NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NMBluezDevice *device;
- GHashTableIter iter;
-
- g_hash_table_iter_init (&iter, priv->devices);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
- if (nm_bluez_device_get_usable (device))
- emit_bdaddr_added (self, device);
- }
-}
-
-static void
-remove_device (NMBluez5Manager *self, NMBluezDevice *device)
-{
- g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_initialized), self);
- g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_usable), self);
- if (nm_bluez_device_get_usable (device))
- g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
-}
-
-static void
-remove_all_devices (NMBluez5Manager *self)
-{
- GHashTableIter iter;
- NMBluezDevice *device;
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- g_hash_table_iter_init (&iter, priv->devices);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
- g_hash_table_iter_steal (&iter);
- remove_device (self, device);
- g_object_unref (device);
- }
-}
-
-static void
-device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self)
-{
- gboolean usable = nm_bluez_device_get_usable (device);
-
- _LOGD ("(%s): bluez device now %s",
- nm_bluez_device_get_path (device),
- usable ? "usable" : "unusable");
-
- if (usable) {
- _LOGD ("(%s): bluez device address %s",
- nm_bluez_device_get_path (device),
- nm_bluez_device_get_address (device));
- emit_bdaddr_added (self, device);
- } else
- g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
-}
-
-static void
-device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- _LOGD ("(%s): bluez device %s",
- nm_bluez_device_get_path (device),
- success ? "initialized" : "failed to initialize");
- if (!success)
- g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device));
-}
-
-static void
-device_added (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NMBluezDevice *device;
-
- device = nm_bluez_device_new (g_dbus_proxy_get_connection (proxy), path, priv->settings);
- g_signal_connect (device, NM_BLUEZ_DEVICE_INITIALIZED, G_CALLBACK (device_initialized), self);
- g_signal_connect (device, "notify::" NM_BLUEZ_DEVICE_USABLE, G_CALLBACK (device_usable), self);
- g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
-
- _LOGD ("(%s): new bluez device found", path);
-}
-
-static void
-device_removed (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NMBluezDevice *device;
-
- _LOGD ("(%s): bluez device removed", path);
-
- device = g_hash_table_lookup (priv->devices, path);
- if (device) {
- g_hash_table_steal (priv->devices, nm_bluez_device_get_path (device));
- remove_device (NM_BLUEZ5_MANAGER (self), device);
- g_object_unref (device);
- }
-}
-
-static void
-object_manager_interfaces_added (GDBusProxy *proxy,
- const char *path,
- GVariant *dict,
- NMBluez5Manager *self)
-{
- if (g_variant_lookup (dict, NM_BLUEZ5_DEVICE_INTERFACE, "a{sv}", NULL))
- device_added (proxy, path, self);
- if (g_variant_lookup (dict, NM_BLUEZ5_NETWORK_SERVER_INTERFACE, "a{sv}", NULL)) {
- gs_unref_variant GVariant *adapter = g_variant_lookup_value (dict, NM_BLUEZ5_ADAPTER_INTERFACE, G_VARIANT_TYPE_DICTIONARY);
- const char *address;
-
- if ( adapter
- && g_variant_lookup (adapter, "Address", "&s", &address))
- network_server_added (proxy, path, address, self);
- }
-}
-
-static void
-object_manager_interfaces_removed (GDBusProxy *proxy,
- const char *path,
- const char **ifaces,
- NMBluez5Manager *self)
-{
- if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_DEVICE_INTERFACE))
- device_removed (proxy, path, self);
- if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_NETWORK_SERVER_INTERFACE))
- network_server_removed (proxy, path, self);
-}
-
-static void
-get_managed_objects_cb (GDBusProxy *proxy,
- GAsyncResult *res,
- NMBluez5Manager *self)
-{
- gs_unref_variant GVariant *variant0 = NULL;
- GVariant *variant, *ifaces;
- GVariantIter i;
- GError *error = NULL;
- const char *path;
-
- variant = _nm_dbus_proxy_call_finish (proxy, res,
- G_VARIANT_TYPE ("(a{oa{sa{sv}}})"),
- &error);
- if (!variant) {
- if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
- _LOGW ("Couldn't get managed objects: not running Bluez5?");
- else {
- g_dbus_error_strip_remote_error (error);
- _LOGW ("Couldn't get managed objects: %s", error->message);
- }
- g_clear_error (&error);
- return;
- }
- variant0 = g_variant_get_child_value (variant, 0);
- g_variant_iter_init (&i, variant0);
- while ((g_variant_iter_next (&i, "{&o*}", &path, &ifaces))) {
- object_manager_interfaces_added (proxy, path, ifaces, self);
- g_variant_unref (ifaces);
- }
-
- g_variant_unref (variant);
-}
-
-static void name_owner_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data);
-
-static void
-on_proxy_acquired (GObject *object,
- GAsyncResult *res,
- NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- GError *error = NULL;
-
- priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-
- if (!priv->proxy) {
- _LOGW ("Couldn't acquire object manager proxy: %s", error->message);
- g_clear_error (&error);
- return;
- }
-
- g_signal_connect (priv->proxy, "notify::g-name-owner",
- G_CALLBACK (name_owner_changed_cb), self);
-
- /* Get already managed devices. */
- g_dbus_proxy_call (priv->proxy, "GetManagedObjects",
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- NULL,
- (GAsyncReadyCallback) get_managed_objects_cb,
- self);
-
- _nm_dbus_signal_connect (priv->proxy, "InterfacesAdded", G_VARIANT_TYPE ("(oa{sa{sv}})"),
- G_CALLBACK (object_manager_interfaces_added), self);
- _nm_dbus_signal_connect (priv->proxy, "InterfacesRemoved", G_VARIANT_TYPE ("(oas)"),
- G_CALLBACK (object_manager_interfaces_removed), self);
-}
-
-static void
-bluez_connect (NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- g_return_if_fail (priv->proxy == NULL);
-
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- NM_BLUEZ_SERVICE,
- NM_BLUEZ_MANAGER_PATH,
- NM_OBJECT_MANAGER_INTERFACE,
- NULL,
- (GAsyncReadyCallback) on_proxy_acquired,
- self);
-}
-
-static void
-name_owner_changed_cb (GObject *object,
- GParamSpec *pspec,
- gpointer user_data)
-{
- NMBluez5Manager *self = NM_BLUEZ5_MANAGER (user_data);
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- char *owner;
-
- if (priv->devices) {
- owner = g_dbus_proxy_get_name_owner (priv->proxy);
- if (!owner)
- remove_all_devices (self);
- g_free (owner);
- }
-}
-
-/*****************************************************************************/
-
-static void
-nm_bluez5_manager_init (NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NMBtVTableNetworkServer *network_server_vtable = NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE (self);
-
- bluez_connect (self);
-
- priv->devices = g_hash_table_new_full (nm_str_hash, g_str_equal,
- NULL, g_object_unref);
-
- c_list_init (&priv->network_servers);
-
- nm_assert (!nm_bt_vtable_network_server);
- network_server_vtable->is_available = network_server_is_available;
- network_server_vtable->register_bridge = network_server_register_bridge;
- network_server_vtable->unregister_bridge = network_server_unregister_bridge;
- nm_bt_vtable_network_server = network_server_vtable;
-}
-
-NMBluez5Manager *
-nm_bluez5_manager_new (NMSettings *settings)
-{
- NMBluez5Manager *instance = NULL;
-
- g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
-
- instance = g_object_new (NM_TYPE_BLUEZ5_MANAGER, NULL);
- NM_BLUEZ5_MANAGER_GET_PRIVATE (instance)->settings = g_object_ref (settings);
- return instance;
-}
-
-static void
-dispose (GObject *object)
-{
- NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- CList *iter, *safe;
-
- c_list_for_each_safe (iter, safe, &priv->network_servers)
- _network_server_free (self, c_list_entry (iter, NetworkServer, lst_ns));
-
- if (priv->proxy) {
- g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (name_owner_changed_cb), self);
- g_clear_object (&priv->proxy);
- }
-
- g_hash_table_remove_all (priv->devices);
-
- G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->dispose (object);
-}
-
-static void
-finalize (GObject *object)
-{
- NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- g_hash_table_destroy (priv->devices);
-
- G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->finalize (object);
-
- g_object_unref (priv->settings);
-}
-
-static void
-nm_bluez5_manager_class_init (NMBluez5ManagerClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->dispose = dispose;
- object_class->finalize = finalize;
-
- signals[BDADDR_ADDED] =
- g_signal_new (NM_BLUEZ_MANAGER_BDADDR_ADDED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
-
- signals[NETWORK_SERVER_ADDED] =
- g_signal_new (NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-}
diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h
deleted file mode 100644
index a3f81a8ce9..0000000000
--- a/src/devices/bluetooth/nm-bluez5-manager.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* NetworkManager -- Network link manager
- *
- * Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2013 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_BLUEZ5_MANAGER_H__
-#define __NETWORKMANAGER_BLUEZ5_MANAGER_H__
-
-#define NM_TYPE_BLUEZ5_MANAGER (nm_bluez5_manager_get_type ())
-#define NM_BLUEZ5_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ5_MANAGER, NMBluez5Manager))
-#define NM_BLUEZ5_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerClass))
-#define NM_IS_BLUEZ5_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ5_MANAGER))
-#define NM_IS_BLUEZ5_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ5_MANAGER))
-#define NM_BLUEZ5_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerClass))
-
-typedef struct _NMBluez5Manager NMBluez5Manager;
-typedef struct _NMBluez5ManagerClass NMBluez5ManagerClass;
-
-GType nm_bluez5_manager_get_type (void);
-
-NMBluez5Manager *nm_bluez5_manager_new (NMSettings *settings);
-
-void nm_bluez5_manager_query_devices (NMBluez5Manager *manager);
-
-#endif /* __NETWORKMANAGER_BLUEZ5_MANAGER_H__ */
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index b8acc90577..61a7759e11 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -10,8 +10,9 @@
#include <stdio.h>
+#include "nm-core-internal.h"
#include "nm-bluez-common.h"
-#include "nm-bluez-device.h"
+#include "nm-bluez-manager.h"
#include "devices/nm-device-private.h"
#include "ppp/nm-ppp-manager.h"
#include "nm-setting-connection.h"
@@ -35,10 +36,12 @@ _LOG_DECLARE_SELF(NMDeviceBt);
/*****************************************************************************/
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
- PROP_BT_NAME,
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceBt,
+ PROP_BT_BDADDR,
+ PROP_BT_BZ_MGR,
PROP_BT_CAPABILITIES,
- PROP_BT_DEVICE,
+ PROP_BT_DBUS_PATH,
+ PROP_BT_NAME,
);
enum {
@@ -51,24 +54,38 @@ static guint signals[LAST_SIGNAL] = { 0 };
typedef struct {
NMModemManager *modem_manager;
- gboolean mm_running;
+ NMBluezManager *bz_mgr;
- NMBluezDevice *bt_device;
+ char *dbus_path;
char *bdaddr;
char *name;
- guint32 capabilities;
- gboolean connected;
- gboolean have_iface;
+ char *connect_rfcomm_iface;
+
+ GSList *connect_modem_candidates;
- char *rfcomm_iface;
NMModem *modem;
- guint timeout_id;
- GCancellable *cancellable;
+ GCancellable *connect_bz_cancellable;
+
+ gulong connect_watch_link_id;
+
+ guint connect_watch_link_idle_id;
+
+ guint connect_wait_modem_id;
+
+ NMBluetoothCapabilities capabilities:6;
+
+ NMBluetoothCapabilities connect_bt_type:6; /* BT type of the current connection */
+
+ NMDeviceStageState stage1_bt_state:3;
+ NMDeviceStageState stage1_modem_prepare_state:3;
+
+ bool is_connected:1;
+
+ bool mm_running:1;
- guint32 bt_type; /* BT type of the current connection */
} NMDeviceBtPrivate;
struct _NMDeviceBt {
@@ -82,42 +99,65 @@ struct _NMDeviceBtClass {
G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
-#define NM_DEVICE_BT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceBt, NM_IS_DEVICE_BT)
-
-/*****************************************************************************/
-
-static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *out_failure_reason);
+#define NM_DEVICE_BT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceBt, NM_IS_DEVICE_BT, NMDevice)
/*****************************************************************************/
-guint32 nm_device_bt_get_capabilities (NMDeviceBt *self)
+NMBluetoothCapabilities nm_device_bt_get_capabilities (NMDeviceBt *self)
{
g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE);
return NM_DEVICE_BT_GET_PRIVATE (self)->capabilities;
}
-static guint32
+static NMBluetoothCapabilities
get_connection_bt_type (NMConnection *connection)
{
NMSettingBluetooth *s_bt;
const char *bt_type;
s_bt = nm_connection_get_setting_bluetooth (connection);
- if (!s_bt)
- return NM_BT_CAPABILITY_NONE;
- bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
- g_assert (bt_type);
-
- if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
- return NM_BT_CAPABILITY_DUN;
- else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
- return NM_BT_CAPABILITY_NAP;
+ if (s_bt) {
+ bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
+ if (bt_type) {
+ if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+ return NM_BT_CAPABILITY_DUN;
+ else if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+ return NM_BT_CAPABILITY_NAP;
+ }
+ }
return NM_BT_CAPABILITY_NONE;
}
+static gboolean
+get_connection_bt_type_check (NMDeviceBt *self,
+ NMConnection *connection,
+ NMBluetoothCapabilities *out_bt_type,
+ GError **error)
+{
+ NMBluetoothCapabilities bt_type;
+
+ bt_type = get_connection_bt_type (connection);
+
+ NM_SET_OUT (out_bt_type, bt_type);
+
+ if (bt_type == NM_BT_CAPABILITY_NONE) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "profile is not a PANU/DUN bluetooth type");
+ return FALSE;
+ }
+
+ if (!NM_FLAGS_ALL (NM_DEVICE_BT_GET_PRIVATE (self)->capabilities, bt_type)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "device does not support bluetooth type");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static NMDeviceCapabilities
get_generic_capabilities (NMDevice *device)
{
@@ -129,17 +169,24 @@ can_auto_connect (NMDevice *device,
NMSettingsConnection *sett_conn,
char **specific_object)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
- guint32 bt_type;
+ NMDeviceBt *self = NM_DEVICE_BT (device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ NMBluetoothCapabilities bt_type;
nm_assert (!specific_object || !*specific_object);
if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->can_auto_connect (device, sett_conn, NULL))
return FALSE;
+ if (!get_connection_bt_type_check (self,
+ nm_settings_connection_get_connection (sett_conn),
+ &bt_type,
+ NULL))
+ return FALSE;
+
/* Can't auto-activate a DUN connection without ModemManager */
- bt_type = get_connection_bt_type (nm_settings_connection_get_connection (sett_conn));
- if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
+ if ( bt_type == NM_BT_CAPABILITY_DUN
+ && priv->mm_running == FALSE)
return FALSE;
return TRUE;
@@ -148,20 +195,16 @@ can_auto_connect (NMDevice *device,
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBt *self = NM_DEVICE_BT (device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
NMSettingBluetooth *s_bt;
const char *bdaddr;
- guint32 bt_type;
if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->check_connection_compatible (device, connection, error))
return FALSE;
- bt_type = get_connection_bt_type (connection);
- if (!NM_FLAGS_ALL (priv->capabilities, bt_type)) {
- nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "device does not support bluetooth type of profile");
+ if (!get_connection_bt_type_check (self, connection, NULL, error))
return FALSE;
- }
s_bt = nm_connection_get_setting_bluetooth (connection);
@@ -187,18 +230,15 @@ check_connection_available (NMDevice *device,
const char *specific_object,
GError **error)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
- guint32 bt_type;
+ NMDeviceBt *self = NM_DEVICE_BT (device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ NMBluetoothCapabilities bt_type;
- bt_type = get_connection_bt_type (connection);
- if (!(bt_type & priv->capabilities)) {
- nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "device does not support bluetooth type");
+ if (!get_connection_bt_type_check (self, connection, &bt_type, error))
return FALSE;
- }
- /* DUN connections aren't available without ModemManager */
- if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE) {
+ if ( bt_type == NM_BT_CAPABILITY_DUN
+ && !priv->mm_running) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"ModemManager missing for DUN profile");
return FALSE;
@@ -214,11 +254,12 @@ complete_connection (NMDevice *device,
NMConnection *const*existing_connections,
GError **error)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
NMSettingBluetooth *s_bt;
const char *setting_bdaddr;
const char *ctype;
- gboolean is_dun = FALSE, is_pan = FALSE;
+ gboolean is_dun = FALSE;
+ gboolean is_pan = FALSE;
NMSettingGsm *s_gsm;
NMSettingCdma *s_cdma;
NMSettingSerial *s_serial;
@@ -261,7 +302,10 @@ complete_connection (NMDevice *device,
}
/* PAN can't use any DUN-related settings */
- if (s_gsm || s_cdma || s_serial || s_ppp) {
+ if ( s_gsm
+ || s_cdma
+ || s_serial
+ || s_ppp) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_SETTING,
@@ -291,7 +335,8 @@ complete_connection (NMDevice *device,
}
/* Need at least a GSM or a CDMA setting */
- if (!s_gsm && !s_cdma) {
+ if ( !s_gsm
+ && !s_cdma) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_SETTING,
@@ -428,20 +473,19 @@ static void
modem_auth_result (NMModem *modem, GError *error, gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+
+ g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
if (error) {
nm_device_state_changed (device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_NO_SECRETS);
- } else {
- NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
-
- /* Otherwise, on success for GSM/CDMA secrets we need to schedule modem stage1 again */
- g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
- if (!modem_stage1 (NM_DEVICE_BT (device), priv->modem, &failure_reason))
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, failure_reason);
+ return;
}
+
+ priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_INIT;
+ nm_device_activate_schedule_stage1_device_prepare (device);
}
static void
@@ -450,45 +494,33 @@ modem_prepare_result (NMModem *modem,
guint i_reason,
gpointer user_data)
{
- NMDeviceBt *self = NM_DEVICE_BT (user_data);
- NMDevice *device = NM_DEVICE (self);
+ NMDeviceBt *self = user_data;
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
NMDeviceStateReason reason = i_reason;
NMDeviceState state;
- state = nm_device_get_state (device);
- g_return_if_fail (state == NM_DEVICE_STATE_CONFIG || state == NM_DEVICE_STATE_NEED_AUTH);
-
- if (success) {
- NMActRequest *req;
- NMActStageReturn ret;
- NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
-
- req = nm_device_get_act_request (device);
- g_return_if_fail (req);
-
- ret = nm_modem_act_stage2_config (modem, req, &failure_reason);
- switch (ret) {
- case NM_ACT_STAGE_RETURN_POSTPONE:
- break;
- case NM_ACT_STAGE_RETURN_SUCCESS:
- nm_device_activate_schedule_stage3_ip_config_start (device);
- break;
- case NM_ACT_STAGE_RETURN_FAILURE:
- default:
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, failure_reason);
- break;
- }
- } else {
+ state = nm_device_get_state (NM_DEVICE (self));
+
+ g_return_if_fail (NM_IN_SET (state, NM_DEVICE_STATE_PREPARE,
+ NM_DEVICE_STATE_NEED_AUTH));
+
+ nm_assert (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_PENDING);
+
+ if (!success) {
if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) {
/* If the connect failed because the SIM PIN was wrong don't allow
* the device to be auto-activated anymore, which would risk locking
* the SIM if the incorrect PIN continues to be used.
*/
- nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN);
+ nm_device_autoconnect_blocked_set (NM_DEVICE (self), NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN);
}
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+ nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason);
+ return;
}
+
+ priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+ nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
}
static void
@@ -497,7 +529,7 @@ device_state_changed (NMDevice *device,
NMDeviceState old_state,
NMDeviceStateReason reason)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
if (priv->modem)
nm_modem_device_state_changed (priv->modem, new_state, old_state);
@@ -506,7 +538,8 @@ device_state_changed (NMDevice *device,
* since the device could be both DUN and NAP capable and thus may not
* change state (which rechecks available connections) when MM comes and goes.
*/
- if (priv->mm_running && (priv->capabilities & NM_BT_CAPABILITY_DUN))
+ if ( priv->mm_running
+ && NM_FLAGS_HAS (priv->capabilities, NM_BT_CAPABILITY_DUN))
nm_device_recheck_available_connections (device);
}
@@ -528,8 +561,10 @@ modem_ip4_config_result (NMModem *modem,
nm_device_ip_method_failed (device,
AF_INET,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
- } else
- nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config));
+ return;
+ }
+
+ nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config));
}
static void
@@ -545,29 +580,6 @@ ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
}
}
-static gboolean
-modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *out_failure_reason)
-{
- NMActRequest *req;
- NMActStageReturn ret;
-
- req = nm_device_get_act_request (NM_DEVICE (self));
- g_return_val_if_fail (req, FALSE);
-
- ret = nm_modem_act_stage1_prepare (modem, req, out_failure_reason);
- switch (ret) {
- case NM_ACT_STAGE_RETURN_POSTPONE:
- case NM_ACT_STAGE_RETURN_SUCCESS:
- /* Success, wait for the 'prepare-result' signal */
- return TRUE;
- case NM_ACT_STAGE_RETURN_FAILURE:
- default:
- break;
- }
-
- return FALSE;
-}
-
/*****************************************************************************/
static void
@@ -577,7 +589,7 @@ modem_cleanup (NMDeviceBt *self)
if (priv->modem) {
g_signal_handlers_disconnect_matched (priv->modem, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
- g_clear_object (&priv->modem);
+ nm_clear_pointer (&priv->modem, nm_modem_unclaim);
}
}
@@ -592,11 +604,13 @@ modem_state_cb (NMModem *modem,
NMDevice *device = NM_DEVICE (user_data);
NMDeviceState dev_state = nm_device_get_state (device);
- if (new_state <= NM_MODEM_STATE_DISABLING && old_state > NM_MODEM_STATE_DISABLING) {
+ if ( new_state <= NM_MODEM_STATE_DISABLING
+ && old_state > NM_MODEM_STATE_DISABLING) {
/* Will be called whenever something external to NM disables the
* modem directly through ModemManager.
*/
- if (nm_device_is_activating (device) || dev_state == NM_DEVICE_STATE_ACTIVATED) {
+ if ( nm_device_is_activating (device)
+ || dev_state == NM_DEVICE_STATE_ACTIVATED) {
nm_device_state_changed (device,
NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_USER_REQUESTED);
@@ -604,13 +618,15 @@ modem_state_cb (NMModem *modem,
}
}
- if (new_state < NM_MODEM_STATE_CONNECTING &&
- old_state >= NM_MODEM_STATE_CONNECTING &&
- dev_state >= NM_DEVICE_STATE_NEED_AUTH &&
- dev_state <= NM_DEVICE_STATE_ACTIVATED) {
+ if ( new_state < NM_MODEM_STATE_CONNECTING
+ && old_state >= NM_MODEM_STATE_CONNECTING
+ && dev_state >= NM_DEVICE_STATE_NEED_AUTH
+ && dev_state <= NM_DEVICE_STATE_ACTIVATED) {
/* Fail the device if the modem disconnects unexpectedly while the
* device is activating/activated. */
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
return;
}
}
@@ -621,66 +637,56 @@ modem_removed_cb (NMModem *modem, gpointer user_data)
NMDeviceBt *self = NM_DEVICE_BT (user_data);
NMDeviceState state;
- /* Fail the device if the modem was removed while active */
state = nm_device_get_state (NM_DEVICE (self));
- if ( state == NM_DEVICE_STATE_ACTIVATED
- || nm_device_is_activating (NM_DEVICE (self))) {
+ if ( nm_device_is_activating (NM_DEVICE (self))
+ || state == NM_DEVICE_STATE_ACTIVATED) {
nm_device_state_changed (NM_DEVICE (self),
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_BT_FAILED);
- } else
- modem_cleanup (self);
+ return;
+ }
+
+ modem_cleanup (self);
}
static gboolean
-component_added (NMDevice *device, GObject *component)
+modem_try_claim (NMDeviceBt *self,
+ NMModem *modem)
{
- NMDeviceBt *self = NM_DEVICE_BT (device);
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- NMModem *modem;
+ gs_free char *rfcomm_base_name = NULL;
NMDeviceState state;
- NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
- if ( !component
- || !NM_IS_MODEM (component))
+ if (priv->modem) {
+ if (priv->modem == modem)
+ return TRUE;
return FALSE;
+ }
- modem = NM_MODEM (component);
- if (!priv->rfcomm_iface)
+ if (nm_modem_is_claimed (modem))
return FALSE;
- {
- gs_free char *base = NULL;
-
- base = g_path_get_basename (priv->rfcomm_iface);
- if (!nm_streq (base, nm_modem_get_control_port (modem)))
- return FALSE;
- }
+ if (!priv->connect_rfcomm_iface)
+ return FALSE;
- /* Got the modem */
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ rfcomm_base_name = g_path_get_basename (priv->connect_rfcomm_iface);
+ if (!nm_streq0 (rfcomm_base_name, nm_modem_get_control_port (modem)))
+ return FALSE;
- /* Can only accept the modem in stage2, but since the interface matched
+ /* Can only accept the modem in stage1, but since the interface matched
* what we were expecting, don't let anything else claim the modem either.
*/
state = nm_device_get_state (NM_DEVICE (self));
- if (state != NM_DEVICE_STATE_CONFIG) {
- _LOGW (LOGD_BT | LOGD_MB,
+ if (state != NM_DEVICE_STATE_PREPARE) {
+ _LOGD (LOGD_BT | LOGD_MB,
"modem found but device not in correct state (%d)",
nm_device_get_state (NM_DEVICE (self)));
- return TRUE;
+ return FALSE;
}
- _LOGI (LOGD_BT | LOGD_MB,
- "Activation: (bluetooth) Stage 2 of 5 (Device Configure) modem found.");
+ priv->modem = nm_modem_claim (modem);
+ priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_INIT;
- if (priv->modem) {
- g_warn_if_reached ();
- modem_cleanup (self);
- }
-
- priv->modem = g_object_ref (modem);
g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self);
g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
@@ -689,92 +695,187 @@ component_added (NMDevice *device, GObject *component)
g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self);
g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
-
g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self);
- /* Kick off the modem connection */
- if (!modem_stage1 (self, modem, &failure_reason))
- nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, failure_reason);
+ _LOGD (LOGD_BT | LOGD_MB,
+ "modem found");
return TRUE;
}
-static gboolean
-modem_find_timeout (gpointer user_data)
+static void
+mm_modem_added_cb (NMModemManager *manager,
+ NMModem *modem,
+ gpointer user_data)
+{
+ NMDeviceBt *self = user_data;
+ NMDeviceBtPrivate *priv;
+
+ if (!modem_try_claim (user_data, modem))
+ return;
+
+ priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+ if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_COMPLETED)
+ nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
+}
+
+/*****************************************************************************/
+
+void
+_nm_device_bt_notify_set_connected (NMDeviceBt *self,
+ gboolean connected)
{
- NMDeviceBt *self = NM_DEVICE_BT (user_data);
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- priv->timeout_id = 0;
- nm_clear_g_cancellable (&priv->cancellable);
+ connected = !!connected;
+ if (priv->is_connected == connected)
+ return;
+
+ priv->is_connected = connected;
+ if ( connected
+ || priv->stage1_bt_state != NM_DEVICE_STAGE_STATE_COMPLETED
+ || nm_device_get_state (NM_DEVICE (self)) > NM_DEVICE_STATE_ACTIVATED) {
+ _LOGT (LOGD_BT, "set-connected: %d", connected);
+ return;
+ }
+
+ _LOGT (LOGD_BT, "set-connected: %d (disconnecting device...)", connected);
nm_device_state_changed (NM_DEVICE (self),
NM_DEVICE_STATE_FAILED,
- NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND);
- return FALSE;
+ NM_DEVICE_STATE_REASON_CARRIER);
}
-static void
-check_connect_continue (NMDeviceBt *self)
+static gboolean
+connect_watch_link_idle_cb (gpointer user_data)
{
- NMDevice *device = NM_DEVICE (self);
+ NMDeviceBt *self = user_data;
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- gboolean pan = (priv->bt_type == NM_BT_CAPABILITY_NAP);
- gboolean dun = (priv->bt_type == NM_BT_CAPABILITY_DUN);
+ int ifindex;
- if (!priv->connected || !priv->have_iface)
- return;
+ priv->connect_watch_link_idle_id = 0;
+
+ if (nm_device_get_state (NM_DEVICE (self)) <= NM_DEVICE_STATE_ACTIVATED) {
+ ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self));
+ if ( ifindex > 0
+ && !nm_platform_link_get (nm_device_get_platform (NM_DEVICE (self)), ifindex)) {
+ _LOGT (LOGD_BT, "device disappeared");
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ }
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+connect_watch_link_cb (NMPlatform *platform,
+ int obj_type_i,
+ int ifindex,
+ NMPlatformLink *info,
+ int change_type_i,
+ NMDevice *self)
+{
+ const NMPlatformSignalChangeType change_type = change_type_i;
+ NMDeviceBtPrivate *priv;
+
+ /* bluez doesn't notify us when the connection disconnects.
+ * Neither does NMManager (or NMDevice) tell us when the ip-ifindex goes away.
+ * This is horrible, and should be improved. For now, watch the link ourself... */
+
+ if (NM_IN_SET (change_type, NM_PLATFORM_SIGNAL_CHANGED,
+ NM_PLATFORM_SIGNAL_REMOVED)) {
+ priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ if (priv->connect_watch_link_idle_id == 0)
+ priv->connect_watch_link_idle_id = g_idle_add (connect_watch_link_idle_cb, self);
+ }
+}
- _LOGI (LOGD_BT,
- "Activation: (bluetooth) Stage 2 of 5 (Device Configure) successful. Will connect via %s.",
- dun ? "DUN" : (pan ? "PAN" : "unknown"));
+static gboolean
+connect_wait_modem_timeout (gpointer user_data)
+{
+ NMDeviceBt *self = NM_DEVICE_BT (user_data);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ /* since this timeout is longer than the connect timeout, we must have already
+ * hit the connect-timeout first or being connected. */
+ nm_assert (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_COMPLETED);
- if (pan) {
- /* Bluez says we're connected now. Start IP config. */
- nm_device_activate_schedule_stage3_ip_config_start (device);
- } else if (dun) {
- /* Wait for ModemManager to find the modem */
- priv->timeout_id = g_timeout_add_seconds (30, modem_find_timeout, self);
+ priv->connect_wait_modem_id = 0;
+ nm_clear_g_cancellable (&priv->connect_bz_cancellable);
- _LOGI (LOGD_BT | LOGD_MB,
- "Activation: (bluetooth) Stage 2 of 5 (Device Configure) waiting for modem to appear.");
- } else
- g_assert_not_reached ();
+ if (priv->modem)
+ _LOGD (LOGD_BT, "timeout connecting modem for DUN connection");
+ else
+ _LOGD (LOGD_BT, "timeout finding modem for DUN connection");
+
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND);
+ return G_SOURCE_REMOVE;
}
static void
-bluez_connect_cb (NMBluezDevice *bt_device,
- const char *device_name,
- GError *error,
- gpointer user_data)
+connect_bz_cb (NMBluezManager *bz_mgr,
+ gboolean is_complete,
+ const char *device_name,
+ GError *error,
+ gpointer user_data)
{
- gs_unref_object NMDeviceBt *self = user_data;
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ NMDeviceBt *self;
+ NMDeviceBtPrivate *priv;
+ char sbuf[100];
if (nm_utils_error_is_cancelled (error, FALSE))
return;
- nm_clear_g_source (&priv->timeout_id);
- g_clear_object (&priv->cancellable);
+ self = user_data;
+ priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+ nm_assert (nm_device_is_activating (NM_DEVICE (self)));
+ nm_assert (NM_IN_SET ((NMBluetoothCapabilities) priv->connect_bt_type, NM_BT_CAPABILITY_DUN,
+ NM_BT_CAPABILITY_NAP));
- if (!nm_device_is_activating (NM_DEVICE (self)))
+ if (!is_complete) {
+ nm_assert (priv->connect_bt_type == NM_BT_CAPABILITY_DUN);
+ nm_assert (device_name);
+ nm_assert (!error);
+
+ if (!nm_streq0 (priv->connect_rfcomm_iface, device_name)) {
+ nm_assert (!priv->connect_rfcomm_iface);
+ _LOGD (LOGD_BT, "DUN is still connecting but got serial port \"%s\" to claim modem", device_name);
+ g_free (priv->connect_rfcomm_iface);
+ priv->connect_rfcomm_iface = g_strdup (device_name);
+ }
return;
+ }
+
+ g_clear_object (&priv->connect_bz_cancellable);
if (!device_name) {
- _LOGW (LOGD_BT, "Error connecting with bluez: %s", error->message);
+ _LOGW (LOGD_BT, "%s connect request failed: %s",
+ nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)),
+ error->message);
nm_device_state_changed (NM_DEVICE (self),
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_BT_FAILED);
return;
}
- if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
- g_free (priv->rfcomm_iface);
- priv->rfcomm_iface = g_strdup (device_name);
- } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) {
+ _LOGD (LOGD_BT, "%s connect request successful (%s)",
+ nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)),
+ device_name);
+
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
+ if (!nm_streq0 (priv->connect_rfcomm_iface, device_name)) {
+ nm_assert_not_reached ();
+ g_free (priv->connect_rfcomm_iface);
+ priv->connect_rfcomm_iface = g_strdup (device_name);
+ }
+ } else {
+ nm_assert (priv->connect_bt_type == NM_BT_CAPABILITY_NAP);
if (!nm_device_set_ip_iface (NM_DEVICE (self), device_name)) {
_LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device_name);
nm_device_state_changed (NM_DEVICE (self),
@@ -782,107 +883,121 @@ bluez_connect_cb (NMBluezDevice *bt_device,
NM_DEVICE_STATE_REASON_BT_FAILED);
return;
}
+ priv->connect_watch_link_id = g_signal_connect (nm_device_get_platform (NM_DEVICE (self)),
+ NM_PLATFORM_SIGNAL_LINK_CHANGED,
+ G_CALLBACK (connect_watch_link_cb),
+ self);
}
- _LOGD (LOGD_BT, "connect request successful");
-
- /* Stage 3 gets scheduled when Bluez says we're connected */
- priv->have_iface = TRUE;
- check_connect_continue (self);
-}
-
-static void
-bluez_connected_changed (NMBluezDevice *bt_device,
- GParamSpec *pspec,
- NMDevice *device)
-{
- NMDeviceBt *self = NM_DEVICE_BT (device);
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- gboolean connected;
- NMDeviceState state;
-
- state = nm_device_get_state (device);
- connected = nm_bluez_device_get_connected (bt_device);
- if (connected) {
- if (state == NM_DEVICE_STATE_CONFIG) {
- _LOGD (LOGD_BT, "connected to the device");
-
- priv->connected = TRUE;
- check_connect_continue (self);
- }
- } else {
- gboolean fail = FALSE;
-
- /* Bluez says we're disconnected from the device. Suck. */
-
- if (nm_device_is_activating (device)) {
- _LOGI (LOGD_BT, "Activation: (bluetooth) bluetooth link disconnected.");
- fail = TRUE;
- } else if (state == NM_DEVICE_STATE_ACTIVATED) {
- _LOGI (LOGD_BT, "bluetooth link disconnected.");
- fail = TRUE;
- }
-
- if (fail) {
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CARRIER);
- priv->connected = FALSE;
- }
+ if (!priv->is_connected) {
+ /* we got the callback from NMBluezManager with succes. We actually should be
+ * connected and this line shouldn't be reached. */
+ nm_assert_not_reached ();
+ _LOGE (LOGD_BT, "bluetooth is unexpectedly not in connected state");
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ return;
}
-}
-static gboolean
-bt_connect_timeout (gpointer user_data)
-{
- NMDeviceBt *self = NM_DEVICE_BT (user_data);
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-
- _LOGD (LOGD_BT, "initial connection timed out");
-
- priv->timeout_id = 0;
- nm_clear_g_cancellable (&priv->cancellable);
-
- nm_device_state_changed (NM_DEVICE (self),
- NM_DEVICE_STATE_FAILED,
- NM_DEVICE_STATE_REASON_BT_FAILED);
- return FALSE;
+ priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+ nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
}
static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+act_stage1_prepare (NMDevice *device,
+ NMDeviceStateReason *out_failure_reason)
{
NMDeviceBt *self = NM_DEVICE_BT (device);
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ gs_free_error GError *error = NULL;
NMConnection *connection;
connection = nm_device_get_applied_connection (device);
g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
- priv->bt_type = get_connection_bt_type (connection);
- if (priv->bt_type == NM_BT_CAPABILITY_NONE) {
- // FIXME: set a reason code
+ priv->connect_bt_type = get_connection_bt_type (connection);
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_NONE) {
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_BT_FAILED);
return NM_ACT_STAGE_RETURN_FAILURE;
}
- if (priv->bt_type == NM_BT_CAPABILITY_DUN && !priv->mm_running) {
+ if ( priv->connect_bt_type == NM_BT_CAPABILITY_DUN
+ && !priv->mm_running) {
NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
return NM_ACT_STAGE_RETURN_FAILURE;
}
- _LOGD (LOGD_BT, "requesting connection to the device");
+ if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_PENDING)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ else if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_INIT) {
+ gs_unref_object GCancellable *cancellable = NULL;
+ char sbuf[100];
+
+ _LOGD (LOGD_BT, "connecting to %s bluetooth device",
+ nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)));
+
+ cancellable = g_cancellable_new ();
+
+ if (!nm_bluez_manager_connect (priv->bz_mgr,
+ priv->dbus_path,
+ priv->connect_bt_type,
+ 30000,
+ cancellable,
+ connect_bz_cb,
+ self,
+ &error)) {
+ _LOGD (LOGD_BT, "cannot connect to bluetooth device: %s", error->message);
+ *out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+ priv->connect_bz_cancellable = g_steal_pointer (&cancellable);
+ priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_PENDING;
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ }
+
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
+ if (!priv->modem) {
+ gs_free NMModem **modems = NULL;
+ guint i, n;
+
+ if (priv->connect_wait_modem_id == 0)
+ priv->connect_wait_modem_id = g_timeout_add_seconds (30, connect_wait_modem_timeout, self);
+
+ modems = nm_modem_manager_get_modems (priv->modem_manager, &n);
+ for (i = 0; i < n; i++) {
+ if (modem_try_claim (self, modems[i]))
+ break;
+ }
+ if (!priv->modem)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ }
+
+ if (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_PENDING)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ if (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_INIT) {
+ priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_PENDING;
+ return nm_modem_act_stage1_prepare (priv->modem,
+ nm_device_get_act_request (NM_DEVICE (self)),
+ out_failure_reason);
+ }
+ }
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ return NM_ACT_STAGE_RETURN_SUCCESS;
+}
- priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device);
- priv->cancellable = g_cancellable_new ();
+static NMActStageReturn
+act_stage2_config (NMDevice *device,
+ NMDeviceStateReason *out_failure_reason)
+{
+ NMDeviceBt *self = NM_DEVICE_BT (device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- nm_bluez_device_connect_async (priv->bt_device,
- priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP),
- priv->cancellable,
- bluez_connect_cb,
- g_object_ref (self));
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN)
+ nm_modem_act_stage2_config (priv->modem);
- return NM_ACT_STAGE_RETURN_POSTPONE;
+ return NM_ACT_STAGE_RETURN_SUCCESS;
}
static NMActStageReturn
@@ -891,11 +1006,11 @@ act_stage3_ip_config_start (NMDevice *device,
gpointer *out_config,
NMDeviceStateReason *out_failure_reason)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
nm_assert_addr_family (addr_family);
- if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
if (addr_family == AF_INET) {
return nm_modem_stage3_ip4_config_start (priv->modem,
device,
@@ -914,15 +1029,17 @@ act_stage3_ip_config_start (NMDevice *device,
static void
deactivate (NMDevice *device)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
- priv->have_iface = FALSE;
- priv->connected = FALSE;
+ nm_clear_g_signal_handler (nm_device_get_platform (device), &priv->connect_watch_link_id);
+ nm_clear_g_source (&priv->connect_watch_link_idle_id);
+ priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_INIT;
+ nm_clear_g_source (&priv->connect_wait_modem_id);
+ nm_clear_g_cancellable (&priv->connect_bz_cancellable);
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_INIT;
- if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
if (priv->modem) {
nm_modem_deactivate (priv->modem, device);
@@ -936,22 +1053,53 @@ deactivate (NMDevice *device)
}
}
- if (priv->bt_type != NM_BT_CAPABILITY_NONE)
- nm_bluez_device_disconnect (priv->bt_device);
-
- priv->bt_type = NM_BT_CAPABILITY_NONE;
+ if (priv->connect_bt_type != NM_BT_CAPABILITY_NONE) {
+ priv->connect_bt_type = NM_BT_CAPABILITY_NONE;
+ nm_bluez_manager_disconnect (priv->bz_mgr, priv->dbus_path);
+ }
- g_free (priv->rfcomm_iface);
- priv->rfcomm_iface = NULL;
+ nm_clear_g_free (&priv->connect_rfcomm_iface);
if (NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate)
NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate (device);
}
-static void
-bluez_device_removed (NMBluezDevice *bdev, gpointer user_data)
+void
+_nm_device_bt_notify_removed (NMDeviceBt *self)
+{
+ g_signal_emit_by_name (self, NM_DEVICE_REMOVED);
+}
+
+/*****************************************************************************/
+
+gboolean
+_nm_device_bt_for_same_device (NMDeviceBt *self,
+ const char *dbus_path,
+ const char *bdaddr,
+ const char *name,
+ NMBluetoothCapabilities capabilities)
{
- g_signal_emit_by_name (NM_DEVICE_BT (user_data), NM_DEVICE_REMOVED);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+ return nm_streq (priv->dbus_path, dbus_path)
+ && nm_streq (priv->bdaddr, bdaddr)
+ && capabilities == priv->capabilities
+ && (!name || nm_streq (priv->name, name));
+}
+
+void
+_nm_device_bt_notify_set_name (NMDeviceBt *self, const char *name)
+{
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+ nm_assert (name);
+
+ if (!nm_streq (priv->name, name)) {
+ _LOGT (LOGD_BT, "set-name: %s", name);
+ g_free (priv->name);
+ priv->name = g_strdup (name);
+ _notify (self, PROP_BT_NAME);
+ }
}
/*****************************************************************************/
@@ -1012,9 +1160,6 @@ get_property (GObject *object, guint prop_id,
case PROP_BT_CAPABILITIES:
g_value_set_uint (value, priv->capabilities);
break;
- case PROP_BT_DEVICE:
- g_value_set_object (value, priv->bt_device);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1028,19 +1173,32 @@ set_property (GObject *object, guint prop_id,
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
switch (prop_id) {
+ case PROP_BT_BZ_MGR:
+ /* construct-only */
+ priv->bz_mgr = g_object_ref (g_value_get_pointer (value));
+ nm_assert (NM_IS_BLUEZ_MANAGER (priv->bz_mgr));
+ break;
+ case PROP_BT_DBUS_PATH:
+ /* construct-only */
+ priv->dbus_path = g_value_dup_string (value);
+ nm_assert (priv->dbus_path);
+ break;
+ case PROP_BT_BDADDR:
+ /* construct-only */
+ priv->bdaddr = g_value_dup_string (value);
+ nm_assert (priv->bdaddr);
+ break;
case PROP_BT_NAME:
/* construct-only */
priv->name = g_value_dup_string (value);
+ nm_assert (priv->name);
break;
case PROP_BT_CAPABILITIES:
/* construct-only */
priv->capabilities = g_value_get_uint (value);
- break;
- case PROP_BT_DEVICE:
- /* construct-only */
- priv->bt_device = g_value_dup_object (value);
- if (!priv->bt_device)
- g_return_if_reached ();
+ nm_assert (NM_IN_SET ((NMBluetoothCapabilities) priv->capabilities, NM_BT_CAPABILITY_DUN,
+ NM_BT_CAPABILITY_NAP,
+ NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1060,7 +1218,6 @@ constructed (GObject *object)
{
NMDeviceBt *self = NM_DEVICE_BT (object);
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- const char *my_hwaddr;
G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object);
@@ -1069,74 +1226,69 @@ constructed (GObject *object)
nm_modem_manager_name_owner_ref (priv->modem_manager);
g_signal_connect (priv->modem_manager,
+ NM_MODEM_MANAGER_MODEM_ADDED,
+ G_CALLBACK (mm_modem_added_cb),
+ self);
+
+ g_signal_connect (priv->modem_manager,
"notify::"NM_MODEM_MANAGER_NAME_OWNER,
G_CALLBACK (mm_name_owner_changed_cb),
self);
- if (priv->bt_device) {
- /* Watch for BT device property changes */
- g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED,
- G_CALLBACK (bluez_connected_changed),
- object);
- g_signal_connect (priv->bt_device, NM_BLUEZ_DEVICE_REMOVED,
- G_CALLBACK (bluez_device_removed), object);
- }
-
- my_hwaddr = nm_device_get_hw_address (NM_DEVICE (object));
- if (my_hwaddr)
- priv->bdaddr = g_strdup (my_hwaddr);
- else
- g_warn_if_reached ();
-
set_mm_running (self);
}
-NMDevice *
-nm_device_bt_new (NMBluezDevice *bt_device,
- const char *udi,
+NMDeviceBt *
+nm_device_bt_new (NMBluezManager *bz_mgr,
+ const char *dbus_path,
const char *bdaddr,
const char *name,
- guint32 capabilities)
+ NMBluetoothCapabilities capabilities)
{
- g_return_val_if_fail (udi != NULL, NULL);
- g_return_val_if_fail (bdaddr != NULL, NULL);
- g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (bz_mgr), NULL);
+ g_return_val_if_fail (dbus_path, NULL);
+ g_return_val_if_fail (bdaddr, NULL);
+ g_return_val_if_fail (name, NULL);
g_return_val_if_fail (capabilities != NM_BT_CAPABILITY_NONE, NULL);
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (bt_device), NULL);
-
- return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT,
- NM_DEVICE_UDI, udi,
- NM_DEVICE_IFACE, bdaddr,
- NM_DEVICE_DRIVER, "bluez",
- NM_DEVICE_PERM_HW_ADDRESS, bdaddr,
- NM_DEVICE_BT_DEVICE, bt_device,
- NM_DEVICE_BT_NAME, name,
- NM_DEVICE_BT_CAPABILITIES, capabilities,
- NM_DEVICE_TYPE_DESC, "Bluetooth",
- NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
- NULL);
+
+ return g_object_new (NM_TYPE_DEVICE_BT,
+ NM_DEVICE_UDI, dbus_path,
+ NM_DEVICE_IFACE, bdaddr,
+ NM_DEVICE_DRIVER, "bluez",
+ NM_DEVICE_PERM_HW_ADDRESS, bdaddr,
+ NM_DEVICE_BT_BDADDR, bdaddr,
+ NM_DEVICE_BT_BZ_MGR, bz_mgr,
+ NM_DEVICE_BT_CAPABILITIES, (guint) capabilities,
+ NM_DEVICE_BT_DBUS_PATH, dbus_path,
+ NM_DEVICE_BT_NAME, name,
+ NM_DEVICE_TYPE_DESC, "Bluetooth",
+ NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
+ NULL);
}
static void
dispose (GObject *object)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
+ NMDeviceBt *self = NM_DEVICE_BT (object);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ nm_clear_g_signal_handler (nm_device_get_platform (NM_DEVICE (self)), &priv->connect_watch_link_id);
+ nm_clear_g_source (&priv->connect_watch_link_idle_id);
- g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
+ nm_clear_g_source (&priv->connect_wait_modem_id);
+ nm_clear_g_cancellable (&priv->connect_bz_cancellable);
if (priv->modem_manager) {
- g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), object);
+ g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), self);
nm_modem_manager_name_owner_unref (priv->modem_manager);
g_clear_object (&priv->modem_manager);
}
- modem_cleanup (NM_DEVICE_BT (object));
- g_clear_object (&priv->bt_device);
+ modem_cleanup (self);
G_OBJECT_CLASS (nm_device_bt_parent_class)->dispose (object);
+
+ g_clear_object (&priv->bz_mgr);
}
static void
@@ -1144,7 +1296,8 @@ finalize (GObject *object)
{
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
- g_free (priv->rfcomm_iface);
+ g_free (priv->connect_rfcomm_iface);
+ g_free (priv->dbus_path);
g_free (priv->name);
g_free (priv->bdaddr);
@@ -1186,17 +1339,34 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->can_auto_connect = can_auto_connect;
device_class->deactivate = deactivate;
+ device_class->act_stage1_prepare = act_stage1_prepare;
device_class->act_stage2_config = act_stage2_config;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->check_connection_compatible = check_connection_compatible;
device_class->check_connection_available = check_connection_available;
device_class->complete_connection = complete_connection;
device_class->is_available = is_available;
- device_class->component_added = component_added;
device_class->get_configured_mtu = nm_modem_get_configured_mtu;
device_class->state_changed = device_state_changed;
+ obj_properties[PROP_BT_BZ_MGR] =
+ g_param_spec_pointer (NM_DEVICE_BT_BZ_MGR, "", "",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_BT_BDADDR] =
+ g_param_spec_string (NM_DEVICE_BT_BDADDR, "", "",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_BT_DBUS_PATH] =
+ g_param_spec_string (NM_DEVICE_BT_DBUS_PATH, "", "",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
obj_properties[PROP_BT_NAME] =
g_param_spec_string (NM_DEVICE_BT_NAME, "", "",
NULL,
@@ -1209,12 +1379,6 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
- obj_properties[PROP_BT_DEVICE] =
- g_param_spec_object (NM_DEVICE_BT_DEVICE, "", "",
- NM_TYPE_BLUEZ_DEVICE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS);
-
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
signals[PPP_STATS] =
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index 2a0eb1de2c..d889aefefb 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -8,7 +8,6 @@
#define __NETWORKMANAGER_DEVICE_BT_H__
#include "devices/nm-device.h"
-#include "nm-bluez-device.h"
#define NM_TYPE_DEVICE_BT (nm_device_bt_get_type ())
#define NM_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt))
@@ -17,9 +16,11 @@
#define NM_IS_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BT))
#define NM_DEVICE_BT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BT, NMDeviceBtClass))
-#define NM_DEVICE_BT_NAME "name"
+#define NM_DEVICE_BT_BDADDR "bt-bdaddr"
+#define NM_DEVICE_BT_BZ_MGR "bt-bz-mgr"
#define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
-#define NM_DEVICE_BT_DEVICE "bt-device"
+#define NM_DEVICE_BT_DBUS_PATH "bt-dbus-path"
+#define NM_DEVICE_BT_NAME "bt-name"
#define NM_DEVICE_BT_PPP_STATS "ppp-stats"
@@ -28,13 +29,21 @@ typedef struct _NMDeviceBtClass NMDeviceBtClass;
GType nm_device_bt_get_type (void);
-NMDevice *nm_device_bt_new (NMBluezDevice *bt_device,
- const char *udi,
- const char *bdaddr,
- const char *name,
- guint32 capabilities);
+struct _NMBluezManager;
-guint32 nm_device_bt_get_capabilities (NMDeviceBt *device);
+NMDeviceBt *nm_device_bt_new (struct _NMBluezManager *bz_mgr,
+ const char *dbus_path,
+ const char *bdaddr,
+ const char *name,
+ NMBluetoothCapabilities capabilities);
+
+gboolean _nm_device_bt_for_same_device (NMDeviceBt *device,
+ const char *dbus_path,
+ const char *bdaddr,
+ const char *name,
+ NMBluetoothCapabilities capabilities);
+
+NMBluetoothCapabilities nm_device_bt_get_capabilities (NMDeviceBt *device);
struct _NMModem;
@@ -42,4 +51,11 @@ gboolean nm_device_bt_modem_added (NMDeviceBt *device,
struct _NMModem *modem,
const char *driver);
+void _nm_device_bt_notify_removed (NMDeviceBt *self);
+
+void _nm_device_bt_notify_set_name (NMDeviceBt *self, const char *name);
+
+void _nm_device_bt_notify_set_connected (NMDeviceBt *self,
+ gboolean connected);
+
#endif /* __NETWORKMANAGER_DEVICE_BT_H__ */
diff --git a/src/devices/bluetooth/tests/nm-bt-test.c b/src/devices/bluetooth/tests/nm-bt-test.c
new file mode 100644
index 0000000000..02cfd2288f
--- /dev/null
+++ b/src/devices/bluetooth/tests/nm-bt-test.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#include "nm-default.h"
+
+#include <glib-unix.h>
+
+#include "devices/bluetooth/nm-bluez5-dun.h"
+
+#include "nm-test-utils-core.h"
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN LOGD_BT
+#define _NMLOG(level, ...) \
+ nm_log ((level), _NMLOG_DOMAIN, \
+ NULL, NULL, \
+ "bt%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+ NM_PRINT_FMT_QUOTED (gl.argv_cmd, "[", gl.argv_cmd, "]", "") \
+ _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+struct {
+ int argc;
+ const char *const*argv;
+ const char *argv_cmd;
+ GMainLoop *loop;
+} gl;
+
+typedef struct _MainCmdInfo {
+ const char *name;
+ int (*main_func) (const struct _MainCmdInfo *main_cmd_info);
+} MainCmdInfo;
+
+/*****************************************************************************/
+
+#if WITH_BLUEZ5_DUN
+
+typedef struct {
+ NMBluez5DunContext *dun_context;
+ GCancellable *cancellable;
+ guint timeout_id;
+ guint sig_term_id;
+ guint sig_int_id;
+} DunConnectData;
+
+static void
+_dun_connect_cb (NMBluez5DunContext *context,
+ const char *rfcomm_dev,
+ GError *error,
+ gpointer user_data)
+{
+ DunConnectData *dun_connect_data = user_data;
+
+ g_assert (dun_connect_data);
+ g_assert (!dun_connect_data->dun_context);
+ g_assert ((!!error) != (!!rfcomm_dev));
+
+ if (rfcomm_dev && !context) {
+ _LOGI ("dun-connect notifies path \"%s\". Wait longer...", rfcomm_dev);
+ return;
+ }
+
+ if (rfcomm_dev) {
+ g_assert (context);
+ _LOGI ("dun-connect completed with path \"%s\"", rfcomm_dev);
+ } else {
+ g_assert (!context);
+ _LOGI ("dun-connect failed with error: %s", error->message);
+ }
+
+ dun_connect_data->dun_context = context;
+
+ g_main_loop_quit (gl.loop);
+}
+
+static void
+_dun_notify_tty_hangup_cb (NMBluez5DunContext *context,
+ gpointer user_data)
+{
+ _LOGI ("dun-connect: notified TTY hangup");
+}
+
+static gboolean
+_timeout_cb (gpointer user_data)
+{
+ DunConnectData *dun_connect_data = user_data;
+
+ _LOGI ("timeout");
+ dun_connect_data->timeout_id = 0;
+ if (dun_connect_data->cancellable)
+ g_cancellable_cancel (dun_connect_data->cancellable);
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_sig_xxx_cb (DunConnectData *dun_connect_data, int sigid)
+{
+ _LOGI ("signal %s received", sigid == SIGTERM ? "SIGTERM" : "SIGINT");
+ g_main_loop_quit (gl.loop);
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+_sig_term_cb (gpointer user_data)
+{
+ return _sig_xxx_cb (user_data, SIGTERM);
+}
+
+static gboolean
+_sig_int_cb (gpointer user_data)
+{
+ return _sig_xxx_cb (user_data, SIGINT);
+}
+#endif
+
+static int
+do_dun_connect (const MainCmdInfo *main_cmd_info)
+{
+#if WITH_BLUEZ5_DUN
+ gs_unref_object GCancellable *cancellable = NULL;
+ gs_free_error GError *error = NULL;
+ const char *adapter;
+ const char *remote;
+ DunConnectData dun_connect_data = { };
+
+ if (gl.argc < 4) {
+ _LOGE ("missing arguments \"adapter\" and \"remote\"");
+ return -1;
+ }
+
+ adapter = gl.argv[2];
+ remote = gl.argv[3];
+
+ cancellable = g_cancellable_new ();
+ dun_connect_data.cancellable = cancellable;
+
+ if (!nm_bluez5_dun_connect (adapter,
+ remote,
+ cancellable,
+ _dun_connect_cb,
+ &dun_connect_data,
+ _dun_notify_tty_hangup_cb,
+ &dun_connect_data,
+ &error)) {
+ _LOGE ("connect failed to start: %s", error->message);
+ return -1;
+ }
+
+ dun_connect_data.timeout_id = g_timeout_add (60000, _timeout_cb, &dun_connect_data);
+
+ g_main_loop_run (gl.loop);
+
+ nm_clear_g_source (&dun_connect_data.timeout_id);
+
+ if (dun_connect_data.dun_context) {
+
+ dun_connect_data.sig_term_id = g_unix_signal_add (SIGTERM, _sig_term_cb, &dun_connect_data);
+ dun_connect_data.sig_int_id = g_unix_signal_add (SIGINT, _sig_int_cb, &dun_connect_data);
+
+ g_main_loop_run (gl.loop);
+
+ nm_clear_g_source (&dun_connect_data.sig_term_id);
+ nm_clear_g_source (&dun_connect_data.sig_int_id);
+
+ nm_bluez5_dun_disconnect (g_steal_pointer (&dun_connect_data.dun_context));
+ }
+
+ return 0;
+#else
+ _LOGE ("compiled without bluetooth DUN support");
+ return 1;
+#endif
+}
+
+/*****************************************************************************/
+
+NMTST_DEFINE ();
+
+int
+main (int argc, char **argv)
+{
+ static const MainCmdInfo main_cmd_infos[] = {
+ { .name = "dun-connect", .main_func = do_dun_connect, },
+ };
+ int exit_code = 0;
+ guint i;
+
+ if (!g_getenv ("G_MESSAGES_DEBUG"))
+ g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+
+ nmtst_init_with_logging (&argc, &argv, "DEBUG", "ALL");
+
+ nm_logging_init (NULL, TRUE);
+
+ gl.argv = (const char *const*) argv;
+ gl.argc = argc;
+ gl.loop = g_main_loop_new (NULL, FALSE);
+
+ _LOGI ("bluetooth test util start");
+
+ gl.argv_cmd = argc >= 2 ? argv[1] : NULL;
+
+ for (i = 0; i < G_N_ELEMENTS (main_cmd_infos); i++) {
+ if (nm_streq0 (main_cmd_infos[i].name, gl.argv_cmd)) {
+ _LOGD ("start \"%s\"", gl.argv_cmd);
+ exit_code = main_cmd_infos[i].main_func (&main_cmd_infos[i]);
+ _LOGD ("completed with %d", exit_code);
+ break;
+ }
+ }
+ if (gl.argv_cmd && i >= G_N_ELEMENTS (main_cmd_infos)) {
+ nm_log_err (LOGD_BT, "invalid command \"%s\"", gl.argv_cmd);
+ exit_code = -1;
+ }
+
+ nm_clear_pointer (&gl.loop, g_main_loop_unref);
+
+ return exit_code;
+}
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index 66dc6dc809..aadaa7faee 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -23,7 +23,9 @@ _LOG_DECLARE_SELF(NMDeviceBridge);
struct _NMDeviceBridge {
NMDevice parent;
+ GCancellable *bt_cancellable;
bool vlan_configured:1;
+ bool bt_registered:1;
};
struct _NMDeviceBridgeClass {
@@ -51,6 +53,7 @@ check_connection_available (NMDevice *device,
const char *specific_object,
GError **error)
{
+ NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
NMSettingBluetooth *s_bt;
if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_available (device, connection, flags, specific_object, error))
@@ -67,13 +70,18 @@ check_connection_available (NMDevice *device,
}
bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
- if (!nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server, bdaddr)) {
+ if (!nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server,
+ bdaddr,
+ ( self->bt_cancellable
+ || self->bt_registered)
+ ? device
+ : NULL)) {
if (bdaddr)
nm_utils_error_set (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "not suitable NAP device \"%s\" available", bdaddr);
+ "no suitable NAP device \"%s\" available", bdaddr);
else
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "not suitable NAP device available");
+ "no suitable NAP device available");
return FALSE;
}
}
@@ -505,9 +513,53 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
return NM_ACT_STAGE_RETURN_SUCCESS;
}
+static void
+_bt_register_bridge_cb (GError *error,
+ gpointer user_data)
+{
+ NMDeviceBridge *self;
+
+ if (nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ self = user_data;
+
+ g_clear_object (&self->bt_cancellable);
+
+ if (error) {
+ _LOGD (LOGD_DEVICE, "bluetooth NAP server failed to register bridge: %s", error->message);
+ nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ return;
+ }
+
+ nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self));
+}
+
+void
+_nm_device_bridge_notify_unregister_bt_nap (NMDevice *device,
+ const char *reason)
+{
+ NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+
+ _LOGD (LOGD_DEVICE, "bluetooth NAP server unregistered from bridge: %s%s",
+ reason,
+ self->bt_registered ? "" : " (was no longer registered)");
+
+ nm_clear_g_cancellable (&self->bt_cancellable);
+
+ if (self->bt_registered) {
+ self->bt_registered = FALSE;
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ }
+}
+
static NMActStageReturn
act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
+ NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
NMConnection *connection;
NMSettingBluetooth *s_bt;
@@ -515,14 +567,32 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
s_bt = _nm_connection_get_setting_bluetooth_for_nap (connection);
if (s_bt) {
- if ( !nm_bt_vtable_network_server
- || !nm_bt_vtable_network_server->register_bridge (nm_bt_vtable_network_server,
- nm_setting_bluetooth_get_bdaddr (s_bt),
- device)) {
- /* The HCI we could use is no longer present. */
- *out_failure_reason = NM_DEVICE_STATE_REASON_REMOVED;
+ gs_free_error GError *error = NULL;
+
+ if (!nm_bt_vtable_network_server) {
+ _LOGD (LOGD_DEVICE, "bluetooth NAP server failed because bluetooth plugin not available");
+ *out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+ if (self->bt_cancellable)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+
+ self->bt_cancellable = g_cancellable_new ();
+ if (!nm_bt_vtable_network_server->register_bridge (nm_bt_vtable_network_server,
+ nm_setting_bluetooth_get_bdaddr (s_bt),
+ device,
+ self->bt_cancellable,
+ _bt_register_bridge_cb,
+ device,
+ &error)) {
+ _LOGD (LOGD_DEVICE, "bluetooth NAP server failed to register bridge: %s", error->message);
+ *out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
return NM_ACT_STAGE_RETURN_FAILURE;
}
+
+ self->bt_registered = TRUE;
+ return NM_ACT_STAGE_RETURN_POSTPONE;
}
return NM_ACT_STAGE_RETURN_SUCCESS;
@@ -533,11 +603,15 @@ deactivate (NMDevice *device)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+ _LOGD (LOGD_DEVICE, "deactivate bridge%s",
+ self->bt_registered ? " (registered as NAP bluetooth device)" : "");
+
self->vlan_configured = FALSE;
- if (nm_bt_vtable_network_server) {
- /* always call unregister. It does nothing if the device
- * isn't registered as a hotspot bridge. */
+ nm_clear_g_cancellable (&self->bt_cancellable);
+
+ if (self->bt_registered) {
+ self->bt_registered = FALSE;
nm_bt_vtable_network_server->unregister_bridge (nm_bt_vtable_network_server,
device);
}
diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h
index 6830a84a13..1caf0f1f01 100644
--- a/src/devices/nm-device-bridge.h
+++ b/src/devices/nm-device-bridge.h
@@ -23,4 +23,7 @@ GType nm_device_bridge_get_type (void);
extern const NMBtVTableNetworkServer *nm_bt_vtable_network_server;
+void _nm_device_bridge_notify_unregister_bt_nap (NMDevice *device,
+ const char *reason);
+
#endif /* __NETWORKMANAGER_DEVICE_BRIDGE_H__ */
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index bf4d490d52..6633c35fb0 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -23,7 +23,6 @@
enum {
DEVICE_ADDED,
- COMPONENT_ADDED,
LAST_SIGNAL
};
@@ -33,17 +32,6 @@ G_DEFINE_ABSTRACT_TYPE (NMDeviceFactory, nm_device_factory, G_TYPE_OBJECT)
/*****************************************************************************/
-gboolean
-nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component)
-{
- gboolean consumed = FALSE;
-
- g_return_val_if_fail (NM_IS_DEVICE_FACTORY (factory), FALSE);
-
- g_signal_emit (factory, signals[COMPONENT_ADDED], 0, component, &consumed);
- return consumed;
-}
-
static void
nm_device_factory_get_supported_types (NMDeviceFactory *factory,
const NMLinkType **out_link_types,
@@ -182,16 +170,8 @@ nm_device_factory_class_init (NMDeviceFactoryClass *klass)
signals[DEVICE_ADDED] = g_signal_new (NM_DEVICE_FACTORY_DEVICE_ADDED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (NMDeviceFactoryClass, device_added),
- NULL, NULL, NULL,
+ 0, NULL, NULL, NULL,
G_TYPE_NONE, 1, NM_TYPE_DEVICE);
-
- signals[COMPONENT_ADDED] = g_signal_new (NM_DEVICE_FACTORY_COMPONENT_ADDED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMDeviceFactoryClass, component_added),
- g_signal_accumulator_true_handled, NULL, NULL,
- G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
}
/*****************************************************************************/
diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h
index ac3bb1979c..11e83e9508 100644
--- a/src/devices/nm-device-factory.h
+++ b/src/devices/nm-device-factory.h
@@ -23,7 +23,6 @@
#define NM_IS_DEVICE_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_FACTORY))
#define NM_DEVICE_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_FACTORY, NMDeviceFactoryClass))
-#define NM_DEVICE_FACTORY_COMPONENT_ADDED "component-added"
#define NM_DEVICE_FACTORY_DEVICE_ADDED "device-added"
typedef struct {
@@ -118,36 +117,6 @@ typedef struct {
NMConnection *connection,
gboolean *out_ignore);
- /* Signals */
-
- /**
- * device_added:
- * @factory: the #NMDeviceFactory
- * @device: the new #NMDevice subclass
- *
- * The factory emits this signal if it finds a new device by itself.
- */
- void (*device_added) (NMDeviceFactory *factory, NMDevice *device);
-
- /**
- * component_added:
- * @factory: the #NMDeviceFactory
- * @component: a new component which existing devices may wish to claim
- *
- * The factory emits this signal when an appearance of some component
- * native to it could be interesting to some of the already existing devices.
- * The devices then indicate if they took interest in claiming the component.
- *
- * For example, the WWAN factory may indicate that a new modem is available,
- * which an existing Bluetooth device may wish to claim. It emits a signal
- * passing the modem instance around to see if any device claims it.
- * If no device claims the component, the plugin is allowed to create a new
- * #NMDevice instance for that component and emit the "device-added" signal.
- *
- * Returns: %TRUE if the component was claimed by a device, %FALSE if not
- */
- gboolean (*component_added) (NMDeviceFactory *factory, GObject *component);
-
} NMDeviceFactoryClass;
GType nm_device_factory_get_type (void);
@@ -189,10 +158,6 @@ NMDevice * nm_device_factory_create_device (NMDeviceFactory *factory,
gboolean *out_ignore,
GError **error);
-/* For use by implementations */
-gboolean nm_device_factory_emit_component_added (NMDeviceFactory *factory,
- GObject *component);
-
#define NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(...) \
{ static NMLinkType const _link_types_declared[] = { __VA_ARGS__, NM_LINK_TYPE_NONE }; _link_types = _link_types_declared; }
#define NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(...) \
diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c
index d6f6185062..94add02e0e 100644
--- a/src/devices/nm-device-ppp.c
+++ b/src/devices/nm-device-ppp.c
@@ -81,7 +81,7 @@ ppp_ifindex_set (NMPPPManager *ppp_manager,
}
if (old_name)
- nm_manager_remove_device (nm_manager_get (), old_name, NM_DEVICE_TYPE_PPP);
+ nm_manager_remove_device (NM_MANAGER_GET, old_name, NM_DEVICE_TYPE_PPP);
nm_device_activate_schedule_stage3_ip_config_start (device);
}
diff --git a/src/devices/nm-device-wpan.c b/src/devices/nm-device-wpan.c
index e25e169d07..0c4cd67a3b 100644
--- a/src/devices/nm-device-wpan.c
+++ b/src/devices/nm-device-wpan.c
@@ -145,7 +145,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
hwaddr_len);
if ( lowpan_plink
&& NM_FLAGS_HAS (lowpan_plink->n_ifi_flags, IFF_UP)) {
- lowpan_device = nm_manager_get_device_by_ifindex (nm_manager_get (),
+ lowpan_device = nm_manager_get_device_by_ifindex (NM_MANAGER_GET,
lowpan_plink->ifindex);
}
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 02963b52c2..bcbf5c27cd 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -1717,7 +1717,7 @@ _parent_set_ifindex (NMDevice *self,
}
if (parent_ifindex > 0) {
- parent_device = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
+ parent_device = nm_manager_get_device_by_ifindex (NM_MANAGER_GET, parent_ifindex);
if (parent_device == self)
parent_device = NULL;
} else
@@ -2210,7 +2210,7 @@ nm_device_get_route_metric (NMDevice *self,
if (route_metric >= 0)
goto out;
- route_metric = nm_manager_device_route_metric_reserve (nm_manager_get (),
+ route_metric = nm_manager_device_route_metric_reserve (NM_MANAGER_GET,
nm_device_get_ip_ifindex (self),
nm_device_get_device_type (self));
out:
@@ -3676,7 +3676,7 @@ device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink)
if (plink->master <= 0)
return;
- master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master);
+ master = nm_manager_get_device_by_ifindex (NM_MANAGER_GET, plink->master);
plink_master = nm_platform_link_get (nm_device_get_platform (self), plink->master);
plink_master_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink_master));
@@ -4785,42 +4785,24 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
return TRUE;
}
-/**
- * nm_device_notify_component_added():
- * @self: the #NMDevice
- * @component: the component being added by a plugin
- *
- * Called by the manager to notify the device that a new component has
- * been found. The device implementation should return %TRUE if it
- * wishes to claim the component, or %FALSE if it cannot.
- *
- * Returns: %TRUE to claim the component, %FALSE if the component cannot be
- * claimed.
- */
-gboolean
-nm_device_notify_component_added (NMDevice *self, GObject *component)
+void
+nm_device_notify_availability_maybe_changed (NMDevice *self)
{
- NMDeviceClass *klass;
NMDevicePrivate *priv;
- g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+ g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
- klass = NM_DEVICE_GET_CLASS (self);
-
- if (priv->state == NM_DEVICE_STATE_DISCONNECTED) {
- /* A device could have stayed disconnected because it would
- * want to register with a network server that now become
- * available. */
- nm_device_recheck_available_connections (self);
- if (g_hash_table_size (priv->available_connections) > 0)
- nm_device_emit_recheck_auto_activate (self);
- }
- if (klass->component_added)
- return klass->component_added (self, component);
+ if (priv->state != NM_DEVICE_STATE_DISCONNECTED)
+ return;
- return FALSE;
+ /* A device could have stayed disconnected because it would
+ * want to register with a network server that now become
+ * available. */
+ nm_device_recheck_available_connections (self);
+ if (g_hash_table_size (priv->available_connections) > 0)
+ nm_device_emit_recheck_auto_activate (self);
}
/**
@@ -5895,7 +5877,7 @@ check_connection_compatible (NMDevice *self, NMConnection *connection, GError **
return FALSE;
}
- conn_iface = nm_manager_get_connection_iface (nm_manager_get (),
+ conn_iface = nm_manager_get_connection_iface (NM_MANAGER_GET,
connection,
NULL,
&local);
@@ -10658,7 +10640,7 @@ start_sharing (NMDevice *self, NMIP4Config *config, GError **error)
* the announced setting without restarting dnsmasq. That means, if the default
* route changes w.r.t. being metered, then the shared connection does not get
* updated before reactivating. */
- announce_android_metered = NM_IN_SET (nm_manager_get_metered (nm_manager_get ()),
+ announce_android_metered = NM_IN_SET (nm_manager_get_metered (NM_MANAGER_GET),
NM_METERED_YES,
NM_METERED_GUESS_YES);
break;
@@ -14668,7 +14650,7 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type)
priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_INIT;
if (cleanup_type != CLEANUP_TYPE_KEEP) {
- nm_manager_device_route_metric_clear (nm_manager_get (),
+ nm_manager_device_route_metric_clear (NM_MANAGER_GET,
nm_device_get_ip_ifindex (self));
}
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 72c269c653..eaeac620d2 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -416,23 +416,6 @@ typedef struct _NMDeviceClass {
int new_ifindex,
NMDevice *new_parent);
- /**
- * component_added:
- * @self: the #NMDevice
- * @component: the component (device, modem, etc) which was added
- *
- * Notifies @self that a new component that a device might be interested
- * in was detected by some device factory. It may include an object of
- * %GObject subclass to help the devices decide whether it claims that
- * particular object itself and the emitting factory should not.
- *
- * Returns: %TRUE if the component was claimed exclusively and no further
- * devices should be notified of the new component. %FALSE to indicate
- * that the component was not exclusively claimed and other devices should
- * be notified.
- */
- gboolean (* component_added) (NMDevice *self, GObject *component);
-
gboolean (* owns_iface) (NMDevice *self, const char *iface);
NMConnection * (* new_default_connection) (NMDevice *self);
@@ -809,7 +792,7 @@ gboolean nm_device_check_connection_available (NMDevice *device,
const char *specific_object,
GError **error);
-gboolean nm_device_notify_component_added (NMDevice *device, GObject *component);
+void nm_device_notify_availability_maybe_changed (NMDevice *self);
gboolean nm_device_owns_iface (NMDevice *device, const char *iface);
@@ -869,12 +852,22 @@ void nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle);
NMConnectivityState nm_device_get_connectivity_state (NMDevice *self, int addr_family);
typedef struct _NMBtVTableNetworkServer NMBtVTableNetworkServer;
+
+typedef void (*NMBtVTableRegisterCallback) (GError *error,
+ gpointer user_data);
+
struct _NMBtVTableNetworkServer {
gboolean (*is_available) (const NMBtVTableNetworkServer *vtable,
- const char *addr);
+ const char *addr,
+ NMDevice *device_accept_busy);
+
gboolean (*register_bridge) (const NMBtVTableNetworkServer *vtable,
const char *addr,
- NMDevice *device);
+ NMDevice *device,
+ GCancellable *cancellable,
+ NMBtVTableRegisterCallback callback,
+ gpointer callback_user_data,
+ GError **error);
gboolean (*unregister_bridge) (const NMBtVTableNetworkServer *vtable,
NMDevice *device);
};
diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c
index 88e3b8be2d..0280ec4220 100644
--- a/src/devices/ovs/nm-ovs-factory.c
+++ b/src/devices/ovs/nm-ovs-factory.c
@@ -70,7 +70,7 @@ new_device_from_type (const char *name, NMDeviceType device_type)
const char *type_desc;
NMLinkType link_type = NM_LINK_TYPE_NONE;
- if (nm_manager_get_device (nm_manager_get (), name, device_type))
+ if (nm_manager_get_device (NM_MANAGER_GET, name, device_type))
return NULL;
if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE) {
@@ -117,7 +117,7 @@ ovsdb_device_removed (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type
NMDevice *device;
NMDeviceState device_state;
- device = nm_manager_get_device (nm_manager_get (), name, device_type);
+ device = nm_manager_get_device (NM_MANAGER_GET, name, device_type);
if (!device)
return;
@@ -145,7 +145,7 @@ ovsdb_interface_failed (NMOvsdb *ovsdb,
_LOGI (name, connection_uuid, "ovs interface \"%s\" (%s) failed: %s", name, connection_uuid, error);
- device = nm_manager_get_device (nm_manager_get (), name, NM_DEVICE_TYPE_OVS_INTERFACE);
+ device = nm_manager_get_device (NM_MANAGER_GET, name, NM_DEVICE_TYPE_OVS_INTERFACE);
if (!device)
return;
diff --git a/src/devices/team/nm-team-factory.c b/src/devices/team/nm-team-factory.c
index 1533085820..a297156439 100644
--- a/src/devices/team/nm-team-factory.c
+++ b/src/devices/team/nm-team-factory.c
@@ -45,7 +45,7 @@ NM_DEVICE_FACTORY_DECLARE_TYPES (
G_MODULE_EXPORT NMDeviceFactory *
nm_device_factory_create (GError **error)
{
- nm_manager_set_capability (nm_manager_get (), NM_CAPABILITY_TEAM);
+ nm_manager_set_capability (NM_MANAGER_GET, NM_CAPABILITY_TEAM);
return (NMDeviceFactory *) g_object_new (NM_TYPE_TEAM_FACTORY, NULL);
}
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index 3e262b0334..e8436bf73d 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -451,7 +451,7 @@ constructed (GObject *object)
G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->constructed (object);
- priv->manager = g_object_ref (nm_manager_get ());
+ priv->manager = g_object_ref (NM_MANAGER_GET);
g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK (device_added_cb), self);
g_signal_connect (priv->manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK (device_removed_cb), self);
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index a8472cbc4e..e8a6560ee1 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -881,7 +881,7 @@ nm_iwd_manager_init (NMIwdManager *self)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
- priv->manager = g_object_ref (nm_manager_get ());
+ priv->manager = g_object_ref (NM_MANAGER_GET);
g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED,
G_CALLBACK (device_added), self);
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver
index 7ccebcb5ee..c368a5907d 100644
--- a/src/devices/wwan/libnm-wwan.ver
+++ b/src/devices/wwan/libnm-wwan.ver
@@ -3,6 +3,7 @@ global:
nm_modem_act_stage1_prepare;
nm_modem_act_stage2_config;
nm_modem_check_connection_compatible;
+ nm_modem_claim;
nm_modem_complete_connection;
nm_modem_deactivate;
nm_modem_deactivate_async;
@@ -14,15 +15,17 @@ global:
nm_modem_get_device_id;
nm_modem_get_driver;
nm_modem_get_iid;
- nm_modem_get_path;
nm_modem_get_ip_ifindex;
nm_modem_get_operator_code;
+ nm_modem_get_path;
nm_modem_get_secrets;
nm_modem_get_state;
nm_modem_get_type;
nm_modem_get_uid;
nm_modem_ip4_pre_commit;
+ nm_modem_is_claimed;
nm_modem_manager_get;
+ nm_modem_manager_get_modems;
nm_modem_manager_get_type;
nm_modem_manager_name_owner_get;
nm_modem_manager_name_owner_ref;
@@ -32,6 +35,7 @@ global:
nm_modem_stage3_ip4_config_start;
nm_modem_stage3_ip6_config_start;
nm_modem_state_to_string;
+ nm_modem_unclaim;
local:
*;
};
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 9a8faeff23..e81115ca3d 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -605,12 +605,9 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
static NMActStageReturn
act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
- NMActRequest *req;
+ nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem);
- req = nm_device_get_act_request (device);
- g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
-
- return nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, req, out_failure_reason);
+ return NM_ACT_STAGE_RETURN_SUCCESS;
}
static NMActStageReturn
@@ -716,7 +713,7 @@ set_modem (NMDeviceModem *self, NMModem *modem)
g_return_if_fail (modem != NULL);
- priv->modem = g_object_ref (modem);
+ priv->modem = nm_modem_claim (modem);
g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
@@ -844,7 +841,7 @@ dispose (GObject *object)
if (priv->modem) {
g_signal_handlers_disconnect_by_data (priv->modem, NM_DEVICE_MODEM (object));
- g_clear_object (&priv->modem);
+ nm_clear_pointer (&priv->modem, nm_modem_unclaim);
}
g_clear_pointer (&priv->device_id, g_free);
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index 463e042ff9..63470148d4 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -136,6 +136,20 @@ remove_one_modem (gpointer key, gpointer value, gpointer user_data)
/*****************************************************************************/
+NMModem **
+nm_modem_manager_get_modems (NMModemManager *self,
+ guint *out_len)
+{
+ g_return_val_if_fail (NM_IS_MODEM_MANAGER (self), NULL);
+
+ return (NMModem **) nm_utils_hash_values_to_array (NM_MODEM_MANAGER_GET_PRIVATE (self)->modems,
+ NULL,
+ NULL,
+ out_len);
+}
+
+/*****************************************************************************/
+
static void
modm_clear_manager (NMModemManager *self)
{
diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h
index 503afc8edb..3dd3024e11 100644
--- a/src/devices/wwan/nm-modem-manager.h
+++ b/src/devices/wwan/nm-modem-manager.h
@@ -38,4 +38,7 @@ void nm_modem_manager_name_owner_unref (NMModemManager *self);
const char *nm_modem_manager_name_owner_get (NMModemManager *self);
+NMModem **nm_modem_manager_get_modems (NMModemManager *self,
+ guint *out_len);
+
#endif /* __NETWORKMANAGER_MODEM_MANAGER_H__ */
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index a21cebc350..e6174ccc49 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -97,6 +97,8 @@ typedef struct _NMModemPrivate {
/* PPP stats */
guint32 in_bytes;
guint32 out_bytes;
+
+ bool claimed:1;
} NMModemPrivate;
G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT)
@@ -174,6 +176,53 @@ nm_modem_state_to_string (NMModemState state)
return NULL;
}
+/*****************************************************************************/
+
+gboolean
+nm_modem_is_claimed (NMModem *self)
+{
+ g_return_val_if_fail (NM_IS_MODEM (self), FALSE);
+
+ return NM_MODEM_GET_PRIVATE (self)->claimed;
+}
+
+NMModem *
+nm_modem_claim (NMModem *self)
+{
+ NMModemPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_MODEM (self), NULL);
+
+ priv = NM_MODEM_GET_PRIVATE (self);
+
+ g_return_val_if_fail (!priv->claimed, NULL);
+
+ priv->claimed = TRUE;
+ return g_object_ref (self);
+}
+
+void
+nm_modem_unclaim (NMModem *self)
+{
+ NMModemPrivate *priv;
+
+ g_return_if_fail (NM_IS_MODEM (self));
+
+ priv = NM_MODEM_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->claimed);
+
+ /* we don't actually unclaim the instance. This instance should not be re-used
+ * by another owner, that is because we only claim modems as we receive them.
+ * There is no mechanism that somebody else would later re-use them again.
+ *
+ * // priv->claimed = FALSE; */
+
+ g_object_unref (self);
+}
+
+/*****************************************************************************/
+
NMModemState
nm_modem_get_state (NMModem *self)
{
@@ -980,6 +1029,8 @@ nm_modem_act_stage1_prepare (NMModem *self,
NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
NMConnection *connection;
+ g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
+
if (priv->act_request)
g_object_unref (priv->act_request);
priv->act_request = g_object_ref (req);
@@ -1014,19 +1065,18 @@ nm_modem_act_stage1_prepare (NMModem *self,
/*****************************************************************************/
-NMActStageReturn
-nm_modem_act_stage2_config (NMModem *self,
- NMActRequest *req,
- NMDeviceStateReason *out_failure_reason)
+void
+nm_modem_act_stage2_config (NMModem *self)
{
- NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+ NMModemPrivate *priv;
+
+ g_return_if_fail (NM_IS_MODEM (self));
+ priv = NM_MODEM_GET_PRIVATE (self);
/* Clear secrets tries counter since secrets were successfully used
* already if we get here.
*/
priv->secrets_tries = 0;
-
- return NM_ACT_STAGE_RETURN_SUCCESS;
}
/*****************************************************************************/
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index c29630c025..37f8b42fe9 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -154,6 +154,10 @@ typedef struct {
GType nm_modem_get_type (void);
+gboolean nm_modem_is_claimed (NMModem *modem);
+NMModem *nm_modem_claim (NMModem *modem);
+void nm_modem_unclaim (NMModem *modem);
+
const char *nm_modem_get_path (NMModem *modem);
const char *nm_modem_get_uid (NMModem *modem);
const char *nm_modem_get_control_port (NMModem *modem);
@@ -209,9 +213,7 @@ NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem,
NMActRequest *req,
NMDeviceStateReason *out_failure_reason);
-NMActStageReturn nm_modem_act_stage2_config (NMModem *modem,
- NMActRequest *req,
- NMDeviceStateReason *out_failure_reason);
+void nm_modem_act_stage2_config (NMModem *modem);
NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem,
NMDevice *device,
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index 59512cf38e..c81a8175bd 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -64,11 +64,10 @@ modem_added_cb (NMModemManager *manager,
gpointer user_data)
{
NMWwanFactory *self = NM_WWAN_FACTORY (user_data);
- NMDevice *device;
+ gs_unref_object NMDevice *device = NULL;
const char *driver;
- /* Do nothing if the modem was consumed by some other plugin */
- if (nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (self), G_OBJECT (modem)))
+ if (nm_modem_is_claimed (modem))
return;
driver = nm_modem_get_driver (modem);
@@ -77,17 +76,16 @@ modem_added_cb (NMModemManager *manager,
* it. The rfcomm port (and thus the modem) gets created automatically
* by the Bluetooth code during the connection process.
*/
- if (driver && strstr (driver, "bluetooth")) {
- nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)",
- nm_modem_get_control_port (modem));
+ if ( driver
+ && strstr (driver, "bluetooth")) {
+ nm_log_dbg (LOGD_MB, "WWAN factory ignores bluetooth modem '%s' which should be handled by bluetooth plugin",
+ nm_modem_get_control_port (modem));
return;
}
/* Make the new modem device */
device = nm_device_modem_new (modem);
- g_assert (device);
g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
- g_object_unref (device);
}
static NMDevice *
diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c
index 7eee251dd0..f5cbe066fe 100644
--- a/src/nm-dbus-manager.c
+++ b/src/nm-dbus-manager.c
@@ -1449,7 +1449,7 @@ static const GDBusSignalInfo signal_info_objmgr_interfaces_removed = NM_DEFINE_G
);
static const GDBusInterfaceInfo interface_info_objmgr = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
- "org.freedesktop.DBus.ObjectManager",
+ DBUS_INTERFACE_OBJECT_MANAGER,
.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
NM_DEFINE_GDBUS_METHOD_INFO (
"GetManagedObjects",
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 6a01426753..b21d39a015 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -3188,22 +3188,6 @@ factory_device_added_cb (NMDeviceFactory *factory,
}
}
-static gboolean
-factory_component_added_cb (NMDeviceFactory *factory,
- GObject *component,
- gpointer user_data)
-{
- NMManager *self = user_data;
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
- NMDevice *device;
-
- c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
- if (nm_device_notify_component_added (device, component))
- return TRUE;
- }
- return FALSE;
-}
-
static void
_register_device_factory (NMDeviceFactory *factory, gpointer user_data)
{
@@ -3213,10 +3197,18 @@ _register_device_factory (NMDeviceFactory *factory, gpointer user_data)
NM_DEVICE_FACTORY_DEVICE_ADDED,
G_CALLBACK (factory_device_added_cb),
self);
- g_signal_connect (factory,
- NM_DEVICE_FACTORY_COMPONENT_ADDED,
- G_CALLBACK (factory_component_added_cb),
- self);
+}
+
+/*****************************************************************************/
+
+void
+nm_manager_notify_device_availibility_maybe_changed (NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMDevice *device;
+
+ c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst)
+ nm_device_notify_availability_maybe_changed (device);
}
/*****************************************************************************/
diff --git a/src/nm-manager.h b/src/nm-manager.h
index f7726873bf..187ce72863 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -64,6 +64,7 @@ GType nm_manager_get_type (void);
NMManager * nm_manager_setup (void);
NMManager * nm_manager_get (void);
+#define NM_MANAGER_GET (nm_manager_get ())
gboolean nm_manager_start (NMManager *manager,
GError **error);
@@ -190,4 +191,6 @@ void nm_manager_dbus_set_property_handle (NMDBusObject *obj,
NMMetered nm_manager_get_metered (NMManager *self);
+void nm_manager_notify_device_availibility_maybe_changed (NMManager *self);
+
#endif /* __NETWORKMANAGER_MANAGER_H__ */
diff --git a/src/nm-policy.c b/src/nm-policy.c
index bb0111c7a6..03c1a4bc55 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -166,7 +166,7 @@ static void
_clear_ip6_subnet (gpointer key, gpointer value, gpointer user_data)
{
NMPlatformIP6Address *subnet = value;
- NMDevice *device = nm_manager_get_device_by_ifindex (nm_manager_get (),
+ NMDevice *device = nm_manager_get_device_by_ifindex (NM_MANAGER_GET,
GPOINTER_TO_INT (key));
if (device) {
diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c
index ff22dbbed2..c294942286 100644
--- a/src/ppp/nm-ppp-manager.c
+++ b/src/ppp/nm-ppp-manager.c
@@ -1213,7 +1213,7 @@ _ppp_manager_stop (NMPPPManager *self,
/* No PID. There is nothing to kill, however, invoke the callback in
* an idle handler.
*
- * Note that we don't register nm_shutdown_wait_obj_register().
+ * Note that we don't register nm_shutdown_wait_obj_register_object().
* In order for shutdown to work properly, the caller must always
* explicitly cancel the action to go down. With the idle-handler,
* cancelling the handle completes the request. */
@@ -1225,7 +1225,7 @@ _ppp_manager_stop (NMPPPManager *self,
* until the process terminated. We do that, by registering an object
* that delays shutdown. */
handle->shutdown_waitobj = g_object_new (G_TYPE_OBJECT, NULL);
- nm_shutdown_wait_obj_register (handle->shutdown_waitobj, "ppp-manager-wait-kill-pppd");
+ nm_shutdown_wait_obj_register_object (handle->shutdown_waitobj, "ppp-manager-wait-kill-pppd");
nm_utils_kill_child_async (nm_steal_int (&priv->pid),
SIGTERM, LOGD_PPP, "pppd",
NM_SHUTDOWN_TIMEOUT_MS,
diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c
index 1831d89bbb..88e6a77b6e 100644
--- a/src/settings/nm-secret-agent.c
+++ b/src/settings/nm-secret-agent.c
@@ -160,7 +160,7 @@ _call_id_new (NMSecretAgent *self,
/* self has async requests (that keep self alive). As long as
* we have pending requests, shutdown is blocked. */
priv->shutdown_wait_obj_registered = TRUE;
- nm_shutdown_wait_obj_register (G_OBJECT (self), "secret-agent");
+ nm_shutdown_wait_obj_register_object (G_OBJECT (self), "secret-agent");
}
return call_id;
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index adb6636a43..f474324c20 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -3116,9 +3116,9 @@ add_plugin (NMSettings *self,
priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));
- nm_shutdown_wait_obj_register_full (G_OBJECT (plugin),
- g_strdup_printf ("%s-settings-plugin", pname),
- TRUE);
+ nm_shutdown_wait_obj_register_object_full (plugin,
+ g_strdup_printf ("%s-settings-plugin", pname),
+ TRUE);
_LOGI ("Loaded settings plugin: %s (%s%s%s)",
pname,
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 7dc054fb82..dea5efec3a 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -1925,7 +1925,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
* it into an interface name, so that legacy tooling is not confused. */
if (!nm_utils_get_testing ()) {
/* This is conditional for easier testing. */
- master_iface = nm_manager_iface_for_uuid (nm_manager_get (), master);
+ master_iface = nm_manager_iface_for_uuid (NM_MANAGER_GET, master);
}
if (!master_iface) {
master_iface = master;