[PATCH 0/4] MR10417: winebth.sys: Handle BlueZ crashes and restarts, only make calls to BlueZ if it is available.
* Only start initial object enumeration if GetNameOwner succeeds for `org.bluez` (i.e, BlueZ is up). * Listen for the `NameOwnerChanged` signal on the system bus, this happens when: * BlueZ shuts down/crashes: In this case, we emit a `BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN`, which removes all added radios/devices/services from the PE driver. * BlueZ starts, here we perform an initial enumeration of objects, and register the authentication agent with BlueZ. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10417
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/winebth.sys/winebth.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 510d7fb72b7..92b74562c3e 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -809,7 +809,6 @@ static void remove_bluetooth_radio( winebluetooth_radio_t radio ) TRACE( "Removing bluetooth radio %p\n", (void *)radio.handle ); device->removed = TRUE; list_remove( &device->entry ); - IoInvalidateDeviceRelations( device->device_obj, BusRelations ); break; } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10417
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/winebth.sys/winebth.c | 75 ++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 92b74562c3e..6c4c6272a77 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -751,6 +751,7 @@ static NTSTATUS radio_get_hw_name_w( winebluetooth_radio_t radio, WCHAR **name ) static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added event ) { struct bluetooth_pdo_ext *ext; + struct bluetooth_radio *radio; DEVICE_OBJECT *device_obj; UNICODE_STRING string; NTSTATUS status; @@ -758,6 +759,18 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added WCHAR *hw_name; static unsigned int radio_index; + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( radio, &device_list, struct bluetooth_radio, entry ) + { + if (winebluetooth_radio_equal( radio->radio, event.radio )) + { + WARN( "Radio %#Ix already exists, skipping.\n", event.radio.handle ); + LeaveCriticalSection( &device_list_cs ); + winebluetooth_radio_free( event.radio ); + return; + } + } + swprintf( name, ARRAY_SIZE( name ), L"\\Device\\WINEBTH-RADIO-%d", radio_index++ ); TRACE( "Adding new bluetooth radio %p: %s\n", (void *)event.radio.handle, debugstr_w( name ) ); @@ -765,6 +778,8 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added if (status) { ERR( "Failed to get hardware name for radio %p, status %#lx\n", (void *)event.radio.handle, status ); + LeaveCriticalSection( &device_list_cs ); + winebluetooth_radio_free( event.radio ); return; } @@ -774,6 +789,8 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added if (status) { ERR( "Failed to create device, status %#lx\n", status ); + LeaveCriticalSection( &device_list_cs ); + winebluetooth_radio_free( event.radio ); return; } @@ -790,7 +807,6 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added InitializeListHead( &ext->radio.irp_list ); - EnterCriticalSection( &device_list_cs ); list_add_tail( &device_list, &ext->radio.entry ); LeaveCriticalSection( &device_list_cs ); @@ -901,10 +917,21 @@ static void bluetooth_radio_add_remote_device( struct winebluetooth_watcher_even { if (winebluetooth_radio_equal( event.radio, radio->radio )) { + struct bluetooth_remote_device *device; struct bluetooth_pdo_ext *ext; DEVICE_OBJECT *device_obj; NTSTATUS status; + LIST_FOR_EACH_ENTRY( device, &radio->remote_devices, struct bluetooth_remote_device, entry ) + { + if (winebluetooth_device_equal( device->device, event.device )) + { + WARN( "Remote device %#Ix already exists, skipping.\n", event.device.handle ); + winebluetooth_device_free( event.device ); + goto done; + } + } + status = IoCreateDevice( driver_obj, sizeof( *ext ), NULL, FILE_DEVICE_BLUETOOTH, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &device_obj ); if (status) @@ -942,6 +969,7 @@ static void bluetooth_radio_add_remote_device( struct winebluetooth_watcher_even break; } } +done: LeaveCriticalSection( &device_list_cs ); winebluetooth_radio_free( event.radio ); @@ -1178,28 +1206,20 @@ static void complete_irp( IRP *irp, NTSTATUS result ) IoCompleteRequest( irp, IO_NO_INCREMENT ); } +/* Enables the low energy interface for this device if it hasn't been already. Caller should hold device->props_cs. */ static void bluetooth_device_enable_le_iface( struct bluetooth_remote_device *device ) { - EnterCriticalSection( &device->props_cs ); /* The device hasn't been started by the PnP manager yet. Set le, and let remote_device_pdo_pnp enable the * interface. */ if (!device->started) - { device->le = TRUE; - LeaveCriticalSection( &device->props_cs ); - return; - } - - if (device->le) + else if (!device->le) { - LeaveCriticalSection( &device->props_cs ); - return; - } - device->le = TRUE; - if (!IoRegisterDeviceInterface( device->device_obj, &GUID_BLUETOOTHLE_DEVICE_INTERFACE, NULL, - &device->bthle_symlink_name )) + device->le = TRUE; + if (!IoRegisterDeviceInterface( device->device_obj, &GUID_BLUETOOTHLE_DEVICE_INTERFACE, NULL, + &device->bthle_symlink_name )) IoSetDeviceInterfaceState( &device->bthle_symlink_name, TRUE ); - LeaveCriticalSection( &device->props_cs ); + } } static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_event_gatt_service_added event ) @@ -1215,10 +1235,22 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even { if (winebluetooth_device_equal( event.device, device->device ) && !device->removed) { + struct bluetooth_gatt_service *service; struct bluetooth_pdo_ext *ext; DEVICE_OBJECT *device_obj; NTSTATUS status; + EnterCriticalSection( &device->props_cs ); + LIST_FOR_EACH_ENTRY( service, &device->gatt_services, struct bluetooth_gatt_service, entry ) + { + if (winebluetooth_gatt_service_equal( service->service, event.service )) + { + WARN( "GATT service %#Ix already exists, skipping.\n", event.device.handle ); + LeaveCriticalSection( &device->props_cs ); + goto failed; + } + } + TRACE( "Adding GATT service %s for remote device %p\n", debugstr_guid( &event.uuid ), (void *)event.device.handle ); @@ -1227,6 +1259,7 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even if (status) { ERR( "Failed to create GATT service PDO, status %#lx\n", status ); + LeaveCriticalSection( &device->props_cs ); goto failed; } @@ -1244,7 +1277,6 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even ext->gatt_service.chars_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": bluetooth_gatt_service.chars_cs"); bluetooth_device_enable_le_iface( device ); - EnterCriticalSection( &device->props_cs ); list_add_tail( &device->gatt_services, &ext->gatt_service.entry ); LeaveCriticalSection( &device->props_cs ); @@ -1327,6 +1359,17 @@ bluetooth_gatt_service_add_characteristic( struct winebluetooth_watcher_event_ga { struct bluetooth_gatt_characteristic *entry; + LIST_FOR_EACH_ENTRY( entry, &svc->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + if (winebluetooth_gatt_characteristic_equal( entry->characteristic, characteristic.characteristic )) + { + WARN( "GATT characteristic %#Ix already exists, skipping.\n", + entry->characteristic.handle ); + LeaveCriticalSection( &device->props_cs ); + goto failed; + } + } + if (!(entry = calloc( 1, sizeof( *entry ) ))) { LeaveCriticalSection( &device->props_cs ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10417
From: Vibhav Pant <vibhavp@gmail.com> Ensure that GetNameOwner("org.bluez") succeeds before we get a list of BlueZ objects and register an authentication agent with it. --- dlls/winebth.sys/dbus.c | 184 ++++++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 83 deletions(-) diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index a2b1e1020fb..ad1cf0b382f 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -292,22 +292,61 @@ static NTSTATUS bluez_get_objects_async( DBusConnection *connection, DBusPending DBusMessage *request; dbus_bool_t success; - TRACE( "Getting managed objects under '/' at service '%s'\n", BLUEZ_DEST ); - request = p_dbus_message_new_method_call( - BLUEZ_DEST, "/", DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects" ); + request = p_dbus_message_new_method_call( BLUEZ_DEST, "/", DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects" ); if (!request) - { return STATUS_NO_MEMORY; - } success = p_dbus_connection_send_with_reply( connection, request, call, -1 ); p_dbus_message_unref( request ); + return success ? (*call ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR) : STATUS_NO_MEMORY; +} + +#define WINE_BLUEZ_AUTH_AGENT_PATH "/org/winehq/wine/winebth/AuthAgent" + +static void bluez_register_auth_agent_callback( DBusPendingCall *call, void *data ) +{ + DBusMessage *reply = p_dbus_pending_call_steal_reply( call ); + DBusError error; + + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message( &error, reply )) + ERR( "Failed to register authentication agent with BlueZ, expect issues with pairing: %s\n", + dbgstr_dbus_error( &error ) ); + else + TRACE( "Registered authentication agent %s with BlueZ\n", WINE_BLUEZ_AUTH_AGENT_PATH ); + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); +} + +static NTSTATUS bluez_register_auth_agent_async( DBusConnection *connection ) +{ + static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; + static const char *capability = "KeyboardDisplay"; + DBusPendingCall *call; + DBusMessage *request; + dbus_bool_t success; + + request = p_dbus_message_new_method_call( BLUEZ_DEST, "/org/bluez", BLUEZ_INTERFACE_AGENT_MANAGER, "RegisterAgent" ); + if (!request) + return STATUS_NO_MEMORY; + + success = p_dbus_message_append_args( request, DBUS_TYPE_OBJECT_PATH, &wine_bluez_auth_agent_path, DBUS_TYPE_STRING, + &capability, DBUS_TYPE_INVALID ); if (!success) + { + p_dbus_message_unref( request ); return STATUS_NO_MEMORY; + } - if (*call == NULL) - return STATUS_INVALID_PARAMETER; + success = p_dbus_connection_send_with_reply( connection, request, &call, -1 ); + p_dbus_message_unref( request ); + if (!success) + return STATUS_NO_MEMORY; + if (!call) + return STATUS_INTERNAL_ERROR; + p_dbus_pending_call_set_notify( call, bluez_register_auth_agent_callback, NULL, NULL ); + p_dbus_pending_call_unref( call ); return STATUS_SUCCESS; } @@ -986,6 +1025,7 @@ static NTSTATUS bluez_device_get_props_by_path_async( DBusConnection *connection struct bluez_watcher_ctx { + DBusConnection *connection; char *bluez_dbus_unique_name; DBusPendingCall *init_device_list_call; @@ -1062,13 +1102,15 @@ void *bluez_dbus_init( void ) return connection; } -/* Return the unique connection name for org.bluez. We use this to ensure that only BlueZ can make method calls to us. */ -static NTSTATUS bluez_dbus_get_unique_name( DBusConnection *connection, char **unique_name ) +/* Return the unique connection name for org.bluez. We use this to ensure that: + * - BlueZ is actually available on this sytem before we make any calls to it. + * - Only BlueZ can make method calls to us. + */ +static NTSTATUS bluez_dbus_get_unique_name_async( DBusConnection *connection, DBusPendingCall **call ) { - const char *bluez_name = BLUEZ_DEST, *name; - NTSTATUS ret = STATUS_SUCCESS; - DBusMessage *request, *reply; - DBusError error; + const char *bluez_name = BLUEZ_DEST; + DBusMessage *request; + dbus_bool_t success; request = p_dbus_message_new_method_call( "org.freedesktop.DBus", "/org/freedesktop/DBus", DBUS_INTERFACE_DBUS, "GetNameOwner" ); @@ -1080,30 +1122,40 @@ static NTSTATUS bluez_dbus_get_unique_name( DBusConnection *connection, char **u return STATUS_NO_MEMORY; } - p_dbus_error_init( &error ); - reply = p_dbus_connection_send_with_reply_and_block( connection, request, bluez_timeout, &error ); + success = p_dbus_connection_send_with_reply( connection, request, call, bluez_timeout); p_dbus_message_unref( request ); - if (!reply) - { - ret = bluez_dbus_error_to_ntstatus( &error ); - WARN_(dbus)( "Failed to get unique name for org.bluez: %s\n", dbgstr_dbus_error( &error ) ); - p_dbus_error_free( &error ); - return ret; - } + return success ? (*call ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR) : STATUS_NO_MEMORY; +} - if (!p_dbus_message_get_args( reply, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID )) - { - ERR_(dbus)( "Failed to read string from GetNameOwner reply: %s\n", dbgstr_dbus_error( &error ) ); - ret = bluez_dbus_error_to_ntstatus( &error ); - } - else if (!(*unique_name = strdup( name ))) - ret = STATUS_NO_MEMORY; - else - TRACE_(dbus)( "Got unique connection name for org.bluez: %s", debugstr_a( name ) ); - p_dbus_message_unref( reply ); - p_dbus_error_free( &error ); +/* Called once the BlueZ service has been activated by the system. */ +static void bluez_on_service_available( struct bluez_watcher_ctx *ctx ) +{ + NTSTATUS status; - return ret; + TRACE_(dbus)( "org.bluez available on %s, initializing bluetooth.\n", debugstr_a( ctx->bluez_dbus_unique_name ) ); + + if ((status = bluez_register_auth_agent_async( ctx->connection ))) + ERR_(dbus)( "Failed to create async RegisterAgent call: %#x\n", status ); + if ((status = bluez_get_objects_async( ctx->connection, &ctx->init_device_list_call ))) + ERR_(dbus)( "Failed to create async GetManagedObjects call: %#x\n", status ); +} + +static void bluez_dbus_get_name_owner_callback( DBusPendingCall *call, void *data ) +{ + DBusMessage *reply = p_dbus_pending_call_steal_reply( call ); + struct bluez_watcher_ctx *ctx = data; + const char *name; + DBusError error; + + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message( &error, reply )) + WARN_(dbus)( "Failed to get unique name for org.bluez: %s\n", dbgstr_dbus_error( &error ) ); + else if (!p_dbus_message_get_args( reply, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID )) + ERR_(dbus)( "Failed to read string from GetNameOwner reply: %s\n", dbgstr_dbus_error( &error ) ); + else if ((ctx->bluez_dbus_unique_name = strdup( name ))) + bluez_on_service_available( ctx ); + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); } void bluez_dbus_close( void *connection ) @@ -1236,17 +1288,12 @@ static DBusHandlerResult bluez_auth_agent_vtable_message_handler( DBusConnection const static struct DBusObjectPathVTable bluez_auth_agent_object_vtable = { .message_function = bluez_auth_agent_vtable_message_handler }; -#define WINE_BLUEZ_AUTH_AGENT_PATH "/org/winehq/wine/winebth/AuthAgent" - NTSTATUS bluez_auth_agent_start( void *connection, void **auth_agent_ctx ) { - static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; - static const char *capability = "KeyboardDisplay"; struct bluez_auth_agent_ctx *ctx; - DBusMessage *request; + NTSTATUS status = STATUS_SUCCESS; dbus_bool_t success; DBusError error; - NTSTATUS status; TRACE( "(%s, %p)\n", dbgstr_dbus_connection( connection ), auth_agent_ctx ); @@ -1269,40 +1316,9 @@ NTSTATUS bluez_auth_agent_start( void *connection, void **auth_agent_ctx ) ERR_(dbus)( "Failed to register object: %s\n", dbgstr_dbus_error( &error ) ); status = bluez_dbus_error_to_ntstatus( &error ); bluez_auth_agent_ctx_decref( ctx ); - goto done; } - - request = p_dbus_message_new_method_call( BLUEZ_DEST, "/org/bluez", BLUEZ_INTERFACE_AGENT_MANAGER, - "RegisterAgent" ); - if (!request) - { - status = STATUS_NO_MEMORY; - goto failure; - } - - success = p_dbus_message_append_args( request, DBUS_TYPE_OBJECT_PATH, &wine_bluez_auth_agent_path, DBUS_TYPE_STRING, - &capability, DBUS_TYPE_INVALID ); - if (!success) - { - status = STATUS_NO_MEMORY; - goto failure; - } - - success = p_dbus_connection_send( connection, request, NULL ); - p_dbus_message_unref( request ); - if (!success) - { - status = STATUS_NO_MEMORY; - goto failure; - } - status = STATUS_SUCCESS; - *auth_agent_ctx = ctx; - goto done; - -failure: - p_dbus_connection_unregister_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH ); - bluez_auth_agent_ctx_decref( ctx ); -done: + else + *auth_agent_ctx = ctx; p_dbus_error_free( &error ); return status; } @@ -2166,7 +2182,8 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v dbus_bool_t success; /* Only allow incoming method calls from org.bluez. */ - if (!(sender = p_dbus_message_get_sender( msg )) || strcmp( sender, ctx->bluez_dbus_unique_name )) + if (!(sender = p_dbus_message_get_sender( msg )) || !ctx->bluez_dbus_unique_name || + strcmp( sender, ctx->bluez_dbus_unique_name )) { if (!(reply = p_dbus_message_new_error( msg, DBUS_ERROR_ACCESS_DENIED, "Access Denied" ))) return DBUS_HANDLER_RESULT_NEED_MEMORY; @@ -2261,20 +2278,19 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) NTSTATUS bluez_watcher_init( void *connection, void **ctx ) { - DBusError err; + struct bluez_watcher_ctx *watcher_ctx; + DBusPendingCall *call; NTSTATUS status; - struct bluez_watcher_ctx *watcher_ctx = - calloc( 1, sizeof( struct bluez_watcher_ctx ) ); + DBusError err; SIZE_T i; - if (watcher_ctx == NULL) return STATUS_NO_MEMORY; + if (!(watcher_ctx = calloc( 1, sizeof( struct bluez_watcher_ctx ) ))) return STATUS_NO_MEMORY; p_dbus_error_init( &err ); - status = bluez_dbus_get_unique_name( connection, &watcher_ctx->bluez_dbus_unique_name ); - if (status) goto done; - status = bluez_get_objects_async( connection, &watcher_ctx->init_device_list_call ); - if (status) + watcher_ctx->connection = connection; + if ((status = bluez_dbus_get_unique_name_async( connection, &call ))) goto done; + if (!p_dbus_pending_call_set_notify( call, bluez_dbus_get_name_owner_callback, watcher_ctx, NULL )) { - ERR( "could not create async GetManagedObjects call: %#x\n", status); + status = STATUS_NO_MEMORY; goto done; } list_init( &watcher_ctx->initial_radio_list ); @@ -2309,6 +2325,7 @@ done: p_dbus_error_free( &err ); if (status) { + p_dbus_pending_call_cancel( call ); if (watcher_ctx->init_device_list_call) { p_dbus_pending_call_cancel( watcher_ctx->init_device_list_call ); @@ -2322,6 +2339,7 @@ done: *ctx = watcher_ctx; TRACE( "ctx=%p\n", ctx ); } + p_dbus_pending_call_unref( call ); return status; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10417
From: Vibhav Pant <vibhavp@gmail.com> On receiving a NameOwnerChanged signal for org.bluez, use the new_owner argument to determine if the BlueZ service was started or stopped, and call bluez_on_service_available or emit a BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN event to remove all devices respectively. --- dlls/winebth.sys/dbus.c | 89 ++++++++++++++++++++++++++------- dlls/winebth.sys/winebth.c | 19 +++++++ dlls/winebth.sys/winebth_priv.h | 1 + 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index ad1cf0b382f..ab836407f60 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -91,6 +91,7 @@ const int bluez_timeout = -1; #define DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED "InterfacesAdded" #define DBUS_OBJECTMANAGER_SIGNAL_INTERFACESREMOVED "InterfacesRemoved" #define DBUS_PROPERTIES_SIGNAL_PROPERTIESCHANGED "PropertiesChanged" +#define DBUS_DBUS_SIGNAL_NAMEOWNERCHANGED "NameOwnerChanged" #define DBUS_INTERFACES_ADDED_SIGNATURE \ DBUS_TYPE_OBJECT_PATH_AS_STRING \ @@ -114,6 +115,9 @@ const int bluez_timeout = -1; DBUS_DICT_ENTRY_END_CHAR_AS_STRING \ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING +#define DBUS_NAMEOWNERCHANGED_SIGNATURE \ + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING + #define BLUEZ_DEST "org.bluez" #define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1" #define BLUEZ_INTERFACE_DEVICE "org.bluez.Device1" @@ -1152,7 +1156,9 @@ static void bluez_dbus_get_name_owner_callback( DBusPendingCall *call, void *dat WARN_(dbus)( "Failed to get unique name for org.bluez: %s\n", dbgstr_dbus_error( &error ) ); else if (!p_dbus_message_get_args( reply, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID )) ERR_(dbus)( "Failed to read string from GetNameOwner reply: %s\n", dbgstr_dbus_error( &error ) ); - else if ((ctx->bluez_dbus_unique_name = strdup( name ))) + /* If bluez_signal_handler has already set a unique_name, nothing needs to be done. This can happen if BlueZ + * (re)starts right after we set up the signal handler in bluez_watcher_init. */ + else if (!ctx->bluez_dbus_unique_name && ((ctx->bluez_dbus_unique_name = strdup( name )))) bluez_on_service_available( ctx ); p_dbus_error_free( &error ); p_dbus_message_unref( reply ); @@ -1705,9 +1711,47 @@ static UINT16 bluez_dbus_get_invalidated_properties_from_iter( return mask; } -static void bluez_signal_handler( DBusConnection *conn, DBusMessage *msg, const char *signal_iface, const char *signal_name, const char *signal_sig, struct list *event_list ) +static void bluez_signal_handler( DBusConnection *conn, DBusMessage *msg, const char *signal_iface, + const char *signal_name, const char *signal_sig, struct bluez_watcher_ctx *ctx ) { + struct list *event_list = &ctx->event_list; + if (!strcmp( signal_iface, DBUS_INTERFACE_DBUS ) && + !strcmp( signal_name, DBUS_DBUS_SIGNAL_NAMEOWNERCHANGED ) && + !strcmp( signal_sig, DBUS_NAMEOWNERCHANGED_SIGNATURE )) + { + const static union winebluetooth_watcher_event_data empty_event; + const char *name, *old_owner, *new_owner; + DBusError error; + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID )) + { + ERR( "Failed to read NameOwnerChanged arguments: %s\n", dbgstr_dbus_error( &error ) ); + p_dbus_error_free( &error ); + return; + } + p_dbus_error_free( &error ); + if (strcmp( name, BLUEZ_DEST )) return; + if (new_owner[0] && (!ctx->bluez_dbus_unique_name || strcmp( ctx->bluez_dbus_unique_name, new_owner ))) + { + free( ctx->bluez_dbus_unique_name ); + if ((ctx->bluez_dbus_unique_name = strdup( new_owner ))) + bluez_on_service_available( ctx ); + } + else if (ctx->bluez_dbus_unique_name) + { + TRACE_(dbus)( "org.bluez down, removing all entries.\n" ); + + free( ctx->bluez_dbus_unique_name ); + ctx->bluez_dbus_unique_name = NULL; + /* When BlueZ shuts down gracefully, we will already have received InterfaceRemoved signals for + * all previously added Bluetooth entries. This event is useful for when BlueZ crashes, in which + * case we would have to remove the entries ourselves. */ + bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN, empty_event ); + } + } if (!strcmp( signal_iface, DBUS_INTERFACE_OBJECTMANAGER ) && !strcmp( signal_name, DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED ) && !strcmp( signal_sig, DBUS_INTERFACES_ADDED_SIGNATURE )) @@ -2174,7 +2218,7 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v type = p_dbus_message_get_type( msg ); if (type == DBUS_MESSAGE_TYPE_SIGNAL) bluez_signal_handler( conn, msg, p_dbus_message_get_interface( msg ), p_dbus_message_get_member( msg ), - p_dbus_message_get_signature( msg ), &ctx->event_list ); + p_dbus_message_get_signature( msg ), ctx ); else if (type == DBUS_MESSAGE_TYPE_METHOD_CALL) { DBusMessage *reply; @@ -2206,8 +2250,14 @@ static const char BLUEZ_MATCH_PROPERTIES[] = "type='signal'," "interface='"DBUS_INTERFACE_PROPERTIES"'," "member='PropertiesChanged'," "sender='"BLUEZ_DEST"',"; +static const char DBUS_MATCH_NAMEOWNERCHANGED[] = "type='signal'," + "interface='"DBUS_INTERFACE_DBUS"'," + "member='NameOwnerChanged'," + "arg0='"BLUEZ_DEST"'," + "path='/org/freedesktop/DBus'," + "sender='org.freedesktop.DBus'"; -static const char *BLUEZ_MATCH_RULES[] = { BLUEZ_MATCH_OBJECTMANAGER, BLUEZ_MATCH_PROPERTIES }; +static const char *BLUEZ_MATCH_RULES[] = { BLUEZ_MATCH_OBJECTMANAGER, BLUEZ_MATCH_PROPERTIES, DBUS_MATCH_NAMEOWNERCHANGED }; /* Free up the watcher alongside any remaining events and initial devices and other associated resources. */ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) @@ -2233,6 +2283,8 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) list_remove( &event1->entry ); switch (event1->event_type) { + case BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN: + break; case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED: unix_name_free( (struct unix_name *)event1->event.radio_added.radio.handle ); break; @@ -2272,6 +2324,7 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) free( event1 ); } + p_dbus_connection_unref( watcher->connection ); free( watcher->bluez_dbus_unique_name ); free( watcher ); } @@ -2285,14 +2338,7 @@ NTSTATUS bluez_watcher_init( void *connection, void **ctx ) SIZE_T i; if (!(watcher_ctx = calloc( 1, sizeof( struct bluez_watcher_ctx ) ))) return STATUS_NO_MEMORY; - p_dbus_error_init( &err ); - watcher_ctx->connection = connection; - if ((status = bluez_dbus_get_unique_name_async( connection, &call ))) goto done; - if (!p_dbus_pending_call_set_notify( call, bluez_dbus_get_name_owner_callback, watcher_ctx, NULL )) - { - status = STATUS_NO_MEMORY; - goto done; - } + watcher_ctx->connection = p_dbus_connection_ref( connection ); list_init( &watcher_ctx->initial_radio_list ); list_init( &watcher_ctx->initial_device_list ); list_init( &watcher_ctx->initial_gatt_service_list ); @@ -2304,33 +2350,43 @@ NTSTATUS bluez_watcher_init( void *connection, void **ctx ) * is racy as the filter is removed from a different thread. */ if (!p_dbus_connection_add_filter( connection, bluez_filter, watcher_ctx, NULL )) { - ERR( "Could not add DBus filter\n" ); + ERR_(dbus)( "Could not add DBus filter\n" ); status = STATUS_NO_MEMORY; goto done; } + p_dbus_error_init( &err ); for (i = 0; i < ARRAY_SIZE( BLUEZ_MATCH_RULES ); i++) { const char *rule = BLUEZ_MATCH_RULES[i]; - TRACE( "Adding DBus match rule %s\n", debugstr_a( rule ) ); + TRACE_(dbus)( "Adding DBus match rule %s\n", debugstr_a( rule ) ); p_dbus_bus_add_match( connection, rule, &err ); if (p_dbus_error_is_set( &err )) { - ERR( "Could not add DBus match %s: %s\n", debugstr_a( rule ), dbgstr_dbus_error( &err ) ); + ERR_(dbus)( "Could not add DBus match %s: %s\n", debugstr_a( rule ), dbgstr_dbus_error( &err ) ); status = bluez_dbus_error_to_ntstatus( &err ); goto done; } } + /* Get the unique name after setting up a signal handler, this avoids a race condition if BlueZ (re)starts (and thus + * gets a new unique name) between the call to GetNameOwner and dbus_connection_add_filter. */ + if ((status = bluez_dbus_get_unique_name_async( connection, &call ))) goto done; + if (!p_dbus_pending_call_set_notify( call, bluez_dbus_get_name_owner_callback, watcher_ctx, NULL )) + { + status = STATUS_NO_MEMORY; + p_dbus_pending_call_cancel( call ); + } + p_dbus_pending_call_unref( call ); done: p_dbus_error_free( &err ); if (status) { - p_dbus_pending_call_cancel( call ); if (watcher_ctx->init_device_list_call) { p_dbus_pending_call_cancel( watcher_ctx->init_device_list_call ); p_dbus_pending_call_unref( watcher_ctx->init_device_list_call ); } + p_dbus_connection_unref( watcher_ctx->connection ); free( watcher_ctx->bluez_dbus_unique_name ); free( watcher_ctx ); } @@ -2339,7 +2395,6 @@ done: *ctx = watcher_ctx; TRACE( "ctx=%p\n", ctx ); } - p_dbus_pending_call_unref( call ); return status; } diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 6c4c6272a77..242088616e1 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -748,6 +748,22 @@ static NTSTATUS radio_get_hw_name_w( winebluetooth_radio_t radio, WCHAR **name ) free( name_a ); return STATUS_SUCCESS; } + +static void bluetooth_remove_all_radios( void ) +{ + struct bluetooth_radio *radio, *radio2; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY_SAFE( radio, radio2, &device_list, struct bluetooth_radio, entry ) + { + radio->removed = TRUE; + list_remove( &radio->entry ); + } + LeaveCriticalSection( &device_list_cs ); + + IoInvalidateDeviceRelations( bus_pdo, BusRelations ); +} + static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added event ) { struct bluetooth_pdo_ext *ext; @@ -1462,6 +1478,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) struct winebluetooth_watcher_event *event = &result.data.watcher_event; switch (event->event_type) { + case BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN: + bluetooth_remove_all_radios(); + break; case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED: add_bluetooth_radio( event->event_data.radio_added ); break; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 765fe1d55f7..bba57bf73e3 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -264,6 +264,7 @@ static inline BOOL winebluetooth_gatt_characteristic_equal( winebluetooth_gatt_c enum winebluetooth_watcher_event_type { + BLUETOOTH_WATCHER_EVENT_TYPE_SERVICE_DOWN, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED, -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10417
This merge request was approved by Elizabeth Figura. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10417
participants (2)
-
Elizabeth Figura (@zfigura) -
Vibhav Pant -
Vibhav Pant (@vibhavp)