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