From b911cc17d81eae81e5af796eb48895d7ed32726b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 20 Aug 2019 14:48:32 +0200 Subject: shared: support nm_g_slice_free_fcn() for sizes of 32 bytes --- shared/nm-glib-aux/nm-shared-utils.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index b5116a1290..d3ec5ceb7d 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; \ -- cgit v1.2.1 From 6a9ab7bb30ce4ca30cc144ea50417c4abd61b2d7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 24 Aug 2019 20:28:27 +0200 Subject: shared: add nm_auto_unref_io_channel cleanup macro --- shared/nm-glib-aux/nm-macros-internal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index 5c3b06717e..0597621b02 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -310,6 +310,9 @@ _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 (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) -- cgit v1.2.1 From 2667a46874b7b853af0be2645cc38a4e965c3b9e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 26 Aug 2019 12:37:50 +0200 Subject: shared: add nm_auto_remove_source cleanup macro --- shared/nm-glib-aux/nm-macros-internal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index 0597621b02..9292f3699d 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -310,6 +310,9 @@ _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) -- cgit v1.2.1 From e688e70b372a2661dcad17d277ca7233ffad4f6d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Aug 2019 18:08:44 +0200 Subject: shared: add nm_utils_hash_values_to_array() helper --- shared/nm-glib-aux/nm-shared-utils.c | 38 ++++++++++++++++++++++++++++++++++++ shared/nm-glib-aux/nm-shared-utils.h | 5 +++++ 2 files changed, 43 insertions(+) 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 d3ec5ceb7d..75b5d387c5 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -955,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, -- cgit v1.2.1 From c73b5c1be0c683f234ce7e4b2cdcefa1882e6262 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 11 Aug 2019 13:38:59 +0200 Subject: shared,all: add and use DBUS_INTERFACE_OBJECT_MANAGER define --- shared/nm-std-aux/nm-dbus-compat.h | 2 ++ src/devices/bluetooth/nm-bluez-common.h | 1 - src/devices/bluetooth/nm-bluez5-manager.c | 3 ++- src/nm-dbus-manager.c | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) 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/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-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c index 7ac7e4c99a..9ede4d709e 100644 --- a/src/devices/bluetooth/nm-bluez5-manager.c +++ b/src/devices/bluetooth/nm-bluez5-manager.c @@ -15,6 +15,7 @@ #include "nm-core-internal.h" +#include "nm-std-aux/nm-dbus-compat.h" #include "c-list/src/c-list.h" #include "nm-bluez-device.h" #include "nm-bluez-common.h" @@ -469,7 +470,7 @@ bluez_connect (NMBluez5Manager *self) NULL, NM_BLUEZ_SERVICE, NM_BLUEZ_MANAGER_PATH, - NM_OBJECT_MANAGER_INTERFACE, + DBUS_INTERFACE_OBJECT_MANAGER, NULL, (GAsyncReadyCallback) on_proxy_acquired, self); 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", -- cgit v1.2.1 From d048050c2d4545cc6914e23548424a5b0519d2b9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 11 Aug 2019 11:04:44 +0200 Subject: shared: add more nm_dbus_connection_call_*() and nm_dbus_connection_signal_subscribe_*() helpers --- shared/nm-glib-aux/nm-dbus-aux.c | 192 +++++++++++++++++++++++++++++++++++++++ shared/nm-glib-aux/nm-dbus-aux.h | 100 +++++++++++++++++++- 2 files changed, 287 insertions(+), 5 deletions(-) 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); /*****************************************************************************/ -- cgit v1.2.1 From 5131cc42455f0de7bb7538ecd2b9d811fb246d2c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Aug 2019 16:45:39 +0200 Subject: core: add and use NM_MANAGER_GET macro For our singleton getters we usually have such a macro. See NM_PLATFORM_GET and NM_SETTINGS_GET. Add such a macro for NMManager and use it. --- src/devices/nm-device-ppp.c | 2 +- src/devices/nm-device-wpan.c | 2 +- src/devices/nm-device.c | 12 ++++++------ src/devices/ovs/nm-ovs-factory.c | 6 +++--- src/devices/team/nm-team-factory.c | 2 +- src/devices/wifi/nm-device-olpc-mesh.c | 2 +- src/devices/wifi/nm-iwd-manager.c | 2 +- src/nm-manager.h | 1 + src/nm-policy.c | 2 +- src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 2 +- 10 files changed, 17 insertions(+), 16 deletions(-) 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..a30f588b27 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)); @@ -5895,7 +5895,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 +10658,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 +14668,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/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/nm-manager.h b/src/nm-manager.h index f7726873bf..bedd004e81 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); 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/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; -- cgit v1.2.1 From eae69e33dd1865f24544c135b366eb343c5a46e6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Aug 2019 07:45:41 +0200 Subject: core: extend nm_shutdown_wait_obj_*() to support notification via a GCancellable Now nm_shutdown_wait_obj_*() supports two styles: - NM_SHUTDOWN_WAIT_TYPE_OBJECT: this just registers a weak pointer on a source GObject. As long as the object is not destroyed (and the object is not unregistered), the shutdown gets blocked. - now new is NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE: this source object is a GCancellable, and during shutdown, the system will cancel the instances to notify about the shutdown. That aside, the GCancellable is tracked exactly like a regular NM_SHUTDOWN_WAIT_TYPE_OBJECT (meaning: a weak pointer is registered and shutdown gets delayed as long as the instance lives). As the rest of the shutdown, it's not yet implemented on the shutdown-side. What is now possible is to register such cancellables, so that users can make use of this API before we fix shutdown. We cannot fix it all at the same time, so first users must be ready for this approach. --- src/NetworkManagerUtils.c | 32 ++++++++++++++++++++++++++++---- src/NetworkManagerUtils.h | 34 ++++++++++++++++++++++++++++++++-- src/ppp/nm-ppp-manager.c | 4 ++-- src/settings/nm-secret-agent.c | 2 +- src/settings/nm-settings.c | 6 +++--- 5 files changed, 66 insertions(+), 12 deletions(-) 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/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, -- cgit v1.2.1 From 6d644c66a8baf0489188bcdd8137abcca79b0e1b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Aug 2019 17:01:18 +0200 Subject: wwan: mark modems that are taken by a NMDevice as "claimed" NMModem-s are either used by NMDeviceModem or by NMDeviceBt. The mechanism how that is coordinated it odd: - the factory emits component-added, and then NMDeviceBt might take the device (and claim it). In that case, component-added would return TRUE to indicate that the modem should not be also used by NMDeviceModem. - next, if the modem has a driver that looks like bluetooth, NMDeviceModem ignores it too. - finally, NMDeviceModem claims the modem (which is now considered to be non-bluetooth). I think the first problem is that the device factory tries to have this generic mechanism of "component-added". It's literally only used to cover this special case. Note that NMDeviceBt is aware of modems. So, abstracting this just adds lots of code that could be solved better by handling the case (of giving the modem to either NMDeviceBt or NMDeviceModem) specifically. NMWWanFactory itself registers to the NM_MODEM_MANAGER_MODEM_ADDED signal and emits nm_device_factory_emit_component_added(). We could just have NMWWanFactory and NMDeviceBt both register to that signal. Signals even support priorities, so we could have NMDeviceBt be called first to claim the device. Anyway, as the modem can only have one owner, the modem should have a flag that indicates whether it's claimed or not. That will allow multiple components all look at the same modem and moderate who is going to take ownership. --- src/devices/bluetooth/nm-device-bt.c | 13 +++++----- src/devices/wwan/libnm-wwan.ver | 5 +++- src/devices/wwan/nm-device-modem.c | 4 +-- src/devices/wwan/nm-modem.c | 49 ++++++++++++++++++++++++++++++++++++ src/devices/wwan/nm-modem.h | 4 +++ src/devices/wwan/nm-wwan-factory.c | 7 +++--- 6 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index b8acc90577..1511f42188 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -577,7 +577,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); } } @@ -646,6 +646,10 @@ component_added (NMDevice *device, GObject *component) return FALSE; modem = NM_MODEM (component); + + if (nm_modem_is_claimed (modem)) + return FALSE; + if (!priv->rfcomm_iface) return FALSE; @@ -675,12 +679,9 @@ component_added (NMDevice *device, GObject *component) _LOGI (LOGD_BT | LOGD_MB, "Activation: (bluetooth) Stage 2 of 5 (Device Configure) modem found."); - if (priv->modem) { - g_warn_if_reached (); - modem_cleanup (self); - } + modem_cleanup (self); - priv->modem = g_object_ref (modem); + priv->modem = nm_modem_claim (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); diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver index 7ccebcb5ee..b63a5066bc 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,14 +15,15 @@ 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_type; nm_modem_manager_name_owner_get; @@ -32,6 +34,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..b380612f63 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -716,7 +716,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 +844,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.c b/src/devices/wwan/nm-modem.c index a21cebc350..e8109628b3 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) { diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index c29630c025..5541c9363b 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); diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c index 59512cf38e..7a93b47c7b 100644 --- a/src/devices/wwan/nm-wwan-factory.c +++ b/src/devices/wwan/nm-wwan-factory.c @@ -64,13 +64,16 @@ 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))) return; + if (nm_modem_is_claimed (modem)) + return; + driver = nm_modem_get_driver (modem); /* If it was a Bluetooth modem and no bluetooth device claimed it, ignore @@ -85,9 +88,7 @@ modem_added_cb (NMModemManager *manager, /* 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 * -- cgit v1.2.1 From f796be3d7dcda422fd56fddf5d721f99e7e7a871 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 23 Aug 2019 17:23:09 +0200 Subject: wwan: add nm_modem_manager_get_modems() to iterate over modems Currently, we cannot ask which modems exist. NMDeviceBt may claim it via nm_device_factory_emit_component_added(), and NMWWanFactory may take it by listening to NM_MODEM_MANAGER_MODEM_ADDED. But that's it. We will drop nm_device_factory_emit_component_added() because it's only used for passing modems to NMDeviceBt. Instead, NMDeviceBt can directly subscribe to NM_MODEM_MANAGER_MODEM_ADDED. It already has a reference to NMModemManager. Anyway, the NM_MODEM_MANAGER_MODEM_ADDED signal is no enough, because sometimes when the mode appears, NMDeviceBt might not yet know whether it should take it (because the DUN connect call is not yet complete). Currently that never happens because dun_connect() blocks waiting for the device. That must be fixed, by not waiting. But this opens up a race, and NMDeviceBt might after NM_MODEM_MANAGER_MODEM_ADDED need to search for the suitable modem: by iterating the list of all modems. --- src/devices/wwan/libnm-wwan.ver | 1 + src/devices/wwan/nm-modem-manager.c | 14 ++++++++++++++ src/devices/wwan/nm-modem-manager.h | 3 +++ 3 files changed, 18 insertions(+) diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver index b63a5066bc..c368a5907d 100644 --- a/src/devices/wwan/libnm-wwan.ver +++ b/src/devices/wwan/libnm-wwan.ver @@ -25,6 +25,7 @@ global: 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; 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__ */ -- cgit v1.2.1 From d6df0339ff3d3129d0a36d2d82bef82a3088cb38 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 30 Aug 2019 07:47:53 +0200 Subject: wwan: simplify API of nm_modem_act_stage2_config() to never fail The previous function arguments of nm_modem_act_stage2_config() act as if the function could fail or even postpone the action. It never did. We cannot treat this generic. A caller needs to know whether nm_modem_act_stage2_config() can postpone the action, and when it does, which signal is emitted upon completion. That is, the caller needs to know how to proceed after postponing. In other words, since this function currently cannot fail or postpone the stage, so must all callers already rely on that. At this point it makes no sense to pretend that the function could be any different, if all callers assume it is not. Simplify the API. --- src/devices/bluetooth/nm-device-bt.c | 20 ++------------------ src/devices/wwan/nm-device-modem.c | 7 ++----- src/devices/wwan/nm-modem.c | 8 ++------ src/devices/wwan/nm-modem.h | 4 +--- 4 files changed, 7 insertions(+), 32 deletions(-) diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 1511f42188..e904fd44ea 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -459,25 +459,9 @@ modem_prepare_result (NMModem *modem, 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; + nm_modem_act_stage2_config (modem); - 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; - } + nm_device_activate_schedule_stage3_ip_config_start (device); } else { 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 diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index b380612f63..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 diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index e8109628b3..32490da1a6 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -1063,10 +1063,8 @@ 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); @@ -1074,8 +1072,6 @@ nm_modem_act_stage2_config (NMModem *self, * 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 5541c9363b..37f8b42fe9 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -213,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, -- cgit v1.2.1 From a5bc4cb785480755574a11b214cc615504966cc9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 30 Aug 2019 11:02:00 +0200 Subject: man: add examples how to configure bluetooth devices to `man nmcli-examples`. --- man/nmcli-examples.xml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) 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 + Bluetooth connection profiles + NetworkManger supports both connecting to NAP and DUN devices as a client. It also + supports sharing the network via a NAP server. + + 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: + +$ nmcli connection add type bluetooth con-name "Profile for My Bluetooth Device (NAP)" autoconnect no bluetooth.type panu bluetooth.bdaddr "$BDADDR" + 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, + +$ 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 + 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. + +$ nmcli connection add type bluetooth con-name "My Bluetooth Hotspot" autoconnect no ifname btnap0 bluetooth.type nap ipv4.method shared ipv6.method shared + + -- cgit v1.2.1 From 878d4963ed7e3058c13f03dc1b2978fce1bed9c6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 24 Aug 2019 14:27:11 +0200 Subject: bluetooth/tests: add "nm-bt-test helper" program for manual testing of bluetooth code Just add a stub implementation and let it build. More will be added later. --- .gitignore | 1 + Makefile.am | 64 +++++++++++++++++++++++++------- src/devices/bluetooth/tests/nm-bt-test.c | 36 ++++++++++++++++++ 3 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 src/devices/bluetooth/tests/nm-bt-test.c 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..e519cedd2f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3408,18 +3408,44 @@ 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-bluez-manager.c \ 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-device-bt.c \ src/devices/bluetooth/nm-device-bt.h \ $(NULL) @@ -3431,18 +3457,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/src/devices/bluetooth/tests/nm-bt-test.c b/src/devices/bluetooth/tests/nm-bt-test.c new file mode 100644 index 0000000000..42f3f2e553 --- /dev/null +++ b/src/devices/bluetooth/tests/nm-bt-test.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: LGPL-2.1+ + +#include "nm-default.h" + +#include "devices/bluetooth/nm-bluez5-dun.h" + +#include "nm-test-utils-core.h" + +/*****************************************************************************/ + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + NMBluez5DunContext *dun_context; + GMainLoop *loop; + + if (!g_getenv ("G_MESSAGES_DEBUG")) + g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); + + nmtst_init_with_logging (&argc, &argv, "DEBUG", "ALL"); + + nm_log_info (LOGD_BT, "bluetooth test util start"); + + dun_context = nm_bluez5_dun_new ("aa:bb:cc:dd:ee:ff", + "aa:bb:cc:dd:ee:fa"); + + loop = g_main_loop_new (NULL, FALSE); + + g_main_loop_unref (loop); + + nm_bluez5_dun_free (dun_context); + + return EXIT_SUCCESS; +} -- cgit v1.2.1 From 4154d9618c904c2286b332b56f3515806cb1bb3b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 11 Aug 2019 10:43:53 +0200 Subject: bluetooth: refactor BlueZ handling and let NMBluezManager cache ObjectManager data This is a complete refactoring of the bluetooth code. Now that BlueZ 4 support was dropped, the separation of NMBluezManager and NMBluez5Manager makes no sense. They should be merged. At that point, notice that BlueZ 5's D-Bus API is fully centered around D-Bus's ObjectManager interface. Using that interface, we basically only call GetManagedObjects() once and register to InterfacesAdded, InterfacesRemoved and PropertiesChanged signals. There is no need to fetch individual properties ever. Note how NMBluezDevice used to query the D-Bus properties itself by creating a GDBusProxy. This is redundant, because when using the ObjectManager interfaces, we have all information already. Instead, let NMBluezManager basically become the client-side cache of all of BlueZ's ObjectManager interface. NMBluezDevice was mostly concerned about caching the D-Bus interface's state, tracking suitable profiles (pan_connection), and moderate between bluez and NMDeviceBt. These tasks don't get simpler by moving them to a seprate file. Let them also be handled by NMBluezManager. I mean, just look how it was previously: NMBluez5Manager registers to ObjectManager interface and sees a device appearing. It creates a NMBluezDevice object and registers to its "initialized" and "notify:usable" signal. In the meantime, NMBluezDevice fetches the relevant information from D-Bus (although it was already present in the data provided by the ObjectManager) and eventually emits these usable and initialized signals. Then, NMBlue5Manager emits a "bdaddr-added" signal, for which NMBluezManager creates the NMDeviceBt instance. NMBluezManager, NMBluez5Manager and NMBluezDevice are strongly cooperating to the point that it is simpler to merge them. This is not mere refactoring. This patch aims to make everything asynchronously and always cancellable. Also, it aims to fix races and inconsistencies of the state. - Registering to a NAP server now waits for the response and delays activation of the NMDeviceBridge accordingly. - For NAP connections we now watch the bnep0 interface in platform, and tear down the device when it goes away. Bluez doesn't send us a notification on D-Bus in that case. - Rework establishing a DUN connection. It no longer uses blocking connect() and does not block until rfcomm device appears. It's all async now. It also watches the rfcomm file descriptor for POLLERR/POLLHUP to notice disconnect. - drop nm_device_factory_emit_component_added() and instead let NMDeviceBt directly register to the WWan factory's "added" signal. --- Makefile.am | 5 +- libnm-core/nm-core-internal.h | 4 + libnm-core/nm-utils.c | 8 + po/POTFILES.in | 2 +- src/devices/bluetooth/meson.build | 2 - src/devices/bluetooth/nm-bluez-device.c | 1207 ------------ src/devices/bluetooth/nm-bluez-device.h | 70 - src/devices/bluetooth/nm-bluez-manager.c | 2885 +++++++++++++++++++++++++++-- src/devices/bluetooth/nm-bluez-manager.h | 39 + src/devices/bluetooth/nm-bluez5-dun.c | 910 ++++++--- src/devices/bluetooth/nm-bluez5-dun.h | 41 +- src/devices/bluetooth/nm-bluez5-manager.c | 585 ------ src/devices/bluetooth/nm-bluez5-manager.h | 27 - src/devices/bluetooth/nm-device-bt.c | 855 +++++---- src/devices/bluetooth/nm-device-bt.h | 34 +- src/devices/bluetooth/tests/nm-bt-test.c | 202 +- src/devices/nm-device-bridge.c | 98 +- src/devices/nm-device-bridge.h | 3 + src/devices/nm-device-factory.c | 22 +- src/devices/nm-device-factory.h | 35 - src/devices/nm-device.c | 40 +- src/devices/nm-device.h | 33 +- src/devices/wwan/nm-modem.c | 7 +- src/devices/wwan/nm-wwan-factory.c | 11 +- src/nm-manager.c | 32 +- src/nm-manager.h | 2 + 26 files changed, 4321 insertions(+), 2838 deletions(-) delete mode 100644 src/devices/bluetooth/nm-bluez-device.c delete mode 100644 src/devices/bluetooth/nm-bluez-device.h create mode 100644 src/devices/bluetooth/nm-bluez-manager.h delete mode 100644 src/devices/bluetooth/nm-bluez5-manager.c delete mode 100644 src/devices/bluetooth/nm-bluez5-manager.h diff --git a/Makefile.am b/Makefile.am index e519cedd2f..55ad993927 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3441,11 +3441,8 @@ $(src_devices_bluetooth_libnm_bluetooth_utils_la_OBJECTS): $(libnm_core_lib_h_pu core_plugins += src/devices/bluetooth/libnm-device-plugin-bluetooth.la src_devices_bluetooth_libnm_device_plugin_bluetooth_la_SOURCES = \ - src/devices/bluetooth/nm-bluez-device.c \ - src/devices/bluetooth/nm-bluez-device.h \ src/devices/bluetooth/nm-bluez-manager.c \ - src/devices/bluetooth/nm-bluez5-manager.c \ - src/devices/bluetooth/nm-bluez5-manager.h \ + src/devices/bluetooth/nm-bluez-manager.h \ src/devices/bluetooth/nm-device-bt.c \ src/devices/bluetooth/nm-device-bt.h \ $(NULL) 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/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/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-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,327 +6,2783 @@ #include "nm-default.h" +#include "nm-bluez-manager.h" + #include #include #include +#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; + + 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; + + 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; + +struct _NMBluezManager { + NMDeviceFactory parent; + NMBluezManagerPrivate _priv; +}; + +struct _NMBluezManagerClass { + NMDeviceFactoryClass parent; +}; + +G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, NM_TYPE_DEVICE_FACTORY); + +#define NM_BLUEZ_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluezManager, NM_IS_BLUEZ_MANAGER) + +/*****************************************************************************/ + +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_BNEP) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BLUETOOTH_SETTING_NAME) +) + +G_MODULE_EXPORT NMDeviceFactory * +nm_device_factory_create (GError **error) +{ + return (NMDeviceFactory *) g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL); +} + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_BT +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez", __VA_ARGS__) + +/*****************************************************************************/ + +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); +} + +/*****************************************************************************/ + +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: "); + } + + 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; + 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; + +#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) +{ + _conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL); +} + +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 * +_network_server_get_bluez_manager (const NMBtVTableNetworkServer *vtable_network_server) +{ + 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); + + return self; +} + +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 +_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_shutdown_wait_obj_register_cancellable_full (cancellable, + g_strdup_printf ("bt-unregister-nap[%s]", bzobj->object_path), + TRUE); + + 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 (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 +_network_server_process_change (BzDBusObj *bzobj, + gboolean *out_emit_device_availability_changed) +{ + 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); + } + + 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); + } + + } + + 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; -typedef struct { + if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) { + /* the device got unusable? Need to revisit it... */ + _process_change_idle_schedule (bzobj->self, bzobj); + } +} - NMSettings *settings; - NMBluez5Manager *manager5; +static void +_device_process_change (BzDBusObj *bzobj) +{ + 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); + } + } + } + } - guint watch_name_id; + bzobj->x_device_is_connected = device_is_usable + && _bzobjs_device_is_connected (bzobj); - GDBusProxy *introspect_proxy; - GCancellable *async_cancellable; -} NMBluezManagerPrivate; + bzobj->x_device_is_usable = device_is_usable; -typedef struct { - NMDeviceFactory parent; - NMBluezManagerPrivate _priv; -} NMBluezManager; + if (bzobj->x_device.device_bt) { + const char *device_to_delete_msg; -typedef struct { - NMDeviceFactoryClass parent; -} NMBluezManagerClass; + 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; -static GType nm_bluez_manager_get_type (void); + if (device_to_delete_msg) { + nm_clear_g_signal_handler (bzobj->x_device.device_bt, &bzobj->x_device.device_bt_signal_id); -G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, NM_TYPE_DEVICE_FACTORY); + device_deleted = g_steal_pointer (&bzobj->x_device.device_bt); -#define NM_BLUEZ_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluezManager, NM_IS_BLUEZ_MANAGER) + _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); +} /*****************************************************************************/ -NM_DEVICE_FACTORY_DECLARE_TYPES ( - NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_BNEP) - NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BLUETOOTH_SETTING_NAME) -) +static void +_process_change_idle_all (NMBluezManager *self, + gboolean *out_emit_device_availability_changed) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + BzDBusObj *bzobj; -G_MODULE_EXPORT NMDeviceFactory * -nm_device_factory_create (GError **error) + while ((bzobj = c_list_first_entry (&priv->process_change_lst_head, BzDBusObj, process_change_lst))) { + + c_list_unlink (&bzobj->process_change_lst); + + _LOG_bzobj (bzobj, "before-processing"); + + _device_process_change (bzobj); + + _network_server_process_change (bzobj, out_emit_device_availability_changed); + + _LOG_bzobj (bzobj, "after-processing"); + + _bzobjs_del_if_dead (bzobj); + } + + nm_clear_g_source (&priv->process_change_idle_id); +} + +static gboolean +_process_change_idle_cb (gpointer user_data) { - return (NMDeviceFactory *) g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL); + 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 +_process_change_idle_schedule (NMBluezManager *self, + BzDBusObj *bzobj) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); -#define _NMLOG_DOMAIN LOGD_BT -#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez", __VA_ARGS__) + 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 +_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; + } + } + + 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); + } + } -static void check_bluez_and_try_setup (NMBluezManager *self); + _bzobjs_del_if_dead (bzobj); +} /*****************************************************************************/ -struct AsyncData { - NMBluezManager *self; - GCancellable *async_cancellable; -}; +#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 struct AsyncData * -async_data_pack (NMBluezManager *self) +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) { - struct AsyncData *data = g_new (struct AsyncData, 1); + 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 *); - data->self = self; - data->async_cancellable = g_object_ref (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->async_cancellable); - return data; + 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 NMBluezManager * -async_data_unpack (struct AsyncData *async_data) +static void +_dbus_handle_interface_added (NMBluezManager *self, + const char *object_path, + GVariant *ifaces, + gboolean initial_get_managed_objects) { - NMBluezManager *self = g_cancellable_is_cancelled (async_data->async_cancellable) - ? NULL : async_data->self; + BzDBusObj *bzobj = NULL; + gboolean changed = FALSE; + const char *interface_name; + GVariant *changed_properties; + GVariantIter iter_ifaces; - g_object_unref (async_data->async_cancellable); - g_free (async_data); - return self; + 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; } -/** - * Cancel any current attempt to detect the version and cleanup - * the related fields. - **/ static void -cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name) +_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; + } - nm_clear_g_cancellable (&priv->async_cancellable); + 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); +} - g_clear_object (&priv->introspect_proxy); +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 (do_unwatch_name && priv->watch_name_id) { - g_bus_unwatch_name (priv->watch_name_id); - priv->watch_name_id = 0; + 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 -manager_bdaddr_added_cb (GObject *manager, - NMBluezDevice *bt_device, - const char *bdaddr, - const char *name, - const char *object_path, - guint32 capabilities, - gpointer user_data) +_dbus_get_managed_objects_cb (GVariant *result, + GError *error, + gpointer user_data) { - 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; + NMBluezManagerPrivate *priv; + GVariantIter iter; + const char *object_path; + GVariant *ifaces; + + if ( !result + && nm_utils_error_is_cancelled (error, FALSE)) + return; + + self = user_data; + priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); - 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)); + g_clear_object (&priv->get_managed_objects_cancellable); - device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities); - if (!device) + if (!result) { + _LOGT ("initial GetManagedObjects() call failed: %s", error->message); + _cleanup_for_name_owner (self); return; + } + + _LOGT ("initial GetManagedObjects call succeeded"); - _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); + 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 -manager_network_server_added_cb (GObject *manager, - gpointer user_data) +_cleanup_for_name_owner (NMBluezManager *self) { - nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (user_data), 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 -setup_bluez5 (NMBluezManager *self) +name_owner_changed (NMBluezManager *self, + const char *owner) { - NMBluez5Manager *manager; + _nm_unused gs_unref_object NMBluezManager *self_keep_alive = g_object_ref (self); NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); - g_return_if_fail (!priv->manager5); + 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 (nm_streq0 (priv->name_owner, owner)) + return; + + _cleanup_for_name_owner (self); - cleanup_checking (self, TRUE); + if (!owner) + return; + + 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); +} + +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; - priv->manager5 = manager = nm_bluez5_manager_new (priv->settings); + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) + return; - 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); + g_variant_get (parameters, + "(&s&s&s)", + NULL, + NULL, + &new_owner); - nm_bluez5_manager_query_devices (manager); + name_owner_changed (self, new_owner); } static void -watch_name_on_appeared (GDBusConnection *connection, - const char *name, - const char *name_owner, - gpointer user_data) +name_owner_get_cb (const char *name_owner, + GError *error, + gpointer user_data) { - check_bluez_and_try_setup (NM_BLUEZ_MANAGER (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 -check_bluez_and_try_setup_final_step (NMBluezManager *self, gboolean ready, const char *reason) +_cleanup_all (NMBluezManager *self) { NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); - if (ready) { - setup_bluez5 (self); - return; - } + priv->settings_registered = FALSE; - _LOGD ("detecting BlueZ version failed: %s", reason); + 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); - /* 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); - } + g_hash_table_remove_all (priv->conn_data_elems); + g_hash_table_remove_all (priv->conn_data_heads); + + _cleanup_for_name_owner (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 -check_bluez_and_try_setup_do_introspect (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +start (NMDeviceFactory *factory) { - NMBluezManager *self = async_data_unpack (user_data); + NMBluezManager *self; NMBluezManagerPrivate *priv; - GError *error = NULL; - gs_unref_variant GVariant *result = NULL; - const char *reason = NULL; + NMSettingsConnection *const*sett_conns; + guint n_sett_conns; + guint i; - if (!self) - return; + g_return_if_fail (NM_IS_BLUEZ_MANAGER (factory)); + self = NM_BLUEZ_MANAGER (factory); 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)); + _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); - g_clear_object (&priv->async_cancellable); + priv->settings_registered = TRUE; - result = _nm_dbus_proxy_call_finish (priv->introspect_proxy, res, - G_VARIANT_TYPE ("(s)"), &error); - if (!result) { - char *reason2; + 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); - 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); + 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; } - check_bluez_and_try_setup_final_step (self, TRUE, reason); + 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 -check_bluez_and_try_setup_on_new_proxy (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +_connect_dun_step2_cb (NMBluez5DunContext *context, + const char *rfcomm_dev, + GError *error, + gpointer user_data) { - NMBluezManager *self = async_data_unpack (user_data); - NMBluezManagerPrivate *priv; - GError *error = NULL; + BzDBusObj *bzobj; - if (!self) + if (nm_utils_error_is_cancelled (error, FALSE)) return; - priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + 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. */ - g_return_if_fail (!priv->introspect_proxy); - g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable)); + nm_assert (!error); + nm_assert (bzobj->x_device.c_req_data); - priv->introspect_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + 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 (!priv->introspect_proxy) { - char *reason = g_strdup_printf ("bluez error creating dbus proxy: %s", error->message); + 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; - check_bluez_and_try_setup_final_step (self, FALSE, reason); - g_error_free (error); - g_free (reason); + 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; } - 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)); + 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 -check_bluez_and_try_setup (NMBluezManager *self) +_connect_nap_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) { - NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + 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; - /* there should be no ongoing detection. Anyway, cleanup_checking. */ - cleanup_checking (self, FALSE); + if (ret) + g_variant_get (ret, "(&s)", &network_iface_name); - priv->async_cancellable = g_cancellable_new (); + bzobj = user_data; - 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)); + _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_NAP, network_iface_name, NULL, error); } static void -start (NMDeviceFactory *factory) +_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) { - check_bluez_and_try_setup (NM_BLUEZ_MANAGER (factory)); + 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 9ede4d709e..0000000000 --- a/src/devices/bluetooth/nm-bluez5-manager.c +++ /dev/null @@ -1,585 +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 -#include - -#include "nm-core-internal.h" - -#include "nm-std-aux/nm-dbus-compat.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, - DBUS_INTERFACE_OBJECT_MANAGER, - 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 e904fd44ea..61a7759e11 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -10,8 +10,9 @@ #include +#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,29 +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); + state = nm_device_get_state (NM_DEVICE (self)); - if (success) { - nm_modem_act_stage2_config (modem); + g_return_if_fail (NM_IN_SET (state, NM_DEVICE_STATE_PREPARE, + NM_DEVICE_STATE_NEED_AUTH)); - nm_device_activate_schedule_stage3_ip_config_start (device); - } else { + 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 @@ -481,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); @@ -490,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); } @@ -512,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 @@ -529,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 @@ -576,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); @@ -588,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; } } @@ -605,67 +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 (nm_modem_is_claimed (modem)) return FALSE; - if (!priv->rfcomm_iface) + if (!priv->connect_rfcomm_iface) 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; - } - - /* 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."); - - modem_cleanup (self); - priv->modem = nm_modem_claim (modem); + priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_INIT; + 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); @@ -674,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; - _LOGI (LOGD_BT, - "Activation: (bluetooth) Stage 2 of 5 (Device Configure) successful. Will connect via %s.", - dun ? "DUN" : (pan ? "PAN" : "unknown")); + 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); + } +} + +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 (!is_complete) { + nm_assert (priv->connect_bt_type == NM_BT_CAPABILITY_DUN); + nm_assert (device_name); + nm_assert (!error); - if (!nm_device_is_activating (NM_DEVICE (self))) + 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), @@ -767,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 @@ -876,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, @@ -899,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); @@ -921,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 (NM_DEVICE_BT (user_data), NM_DEVICE_REMOVED); + 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) +{ + 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); + } } /*****************************************************************************/ @@ -997,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; @@ -1013,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); @@ -1045,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); @@ -1053,75 +1225,70 @@ 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 @@ -1129,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); @@ -1171,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, @@ -1194,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 index 42f3f2e553..02cfd2288f 100644 --- a/src/devices/bluetooth/tests/nm-bt-test.c +++ b/src/devices/bluetooth/tests/nm-bt-test.c @@ -2,35 +2,219 @@ #include "nm-default.h" +#include + #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) { - NMBluez5DunContext *dun_context; - GMainLoop *loop; + 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_log_info (LOGD_BT, "bluetooth test util start"); + nm_logging_init (NULL, TRUE); + + gl.argv = (const char *const*) argv; + gl.argc = argc; + gl.loop = g_main_loop_new (NULL, FALSE); - dun_context = nm_bluez5_dun_new ("aa:bb:cc:dd:ee:ff", - "aa:bb:cc:dd:ee:fa"); + _LOGI ("bluetooth test util start"); - loop = g_main_loop_new (NULL, FALSE); + gl.argv_cmd = argc >= 2 ? argv[1] : NULL; - g_main_loop_unref (loop); + 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_bluez5_dun_free (dun_context); + nm_clear_pointer (&gl.loop, g_main_loop_unref); - return EXIT_SUCCESS; + 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.c b/src/devices/nm-device.c index a30f588b27..bcbf5c27cd 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -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); } /** 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/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 32490da1a6..e6174ccc49 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -1029,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); @@ -1066,8 +1068,11 @@ nm_modem_act_stage1_prepare (NMModem *self, 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. */ diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c index 7a93b47c7b..c81a8175bd 100644 --- a/src/devices/wwan/nm-wwan-factory.c +++ b/src/devices/wwan/nm-wwan-factory.c @@ -67,10 +67,6 @@ modem_added_cb (NMModemManager *manager, 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))) - return; - if (nm_modem_is_claimed (modem)) return; @@ -80,9 +76,10 @@ 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; } 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 bedd004e81..187ce72863 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -191,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__ */ -- cgit v1.2.1