-
-
Notifications
You must be signed in to change notification settings - Fork 261
Open
Labels
bugSomething isn't workingSomething isn't working
Description
What happened?
I run home assistant core in a docker container and got exceptions at
idxdata = btmgmt_sync.send('ReadControllerIndexList', None) |
Unable to open PF_BLUETOOTH socket
I've followed the setcap instructions and no luck. I've also tried run btmgmt info
in the container, but got the same error.
I think the root cause is moby/moby#16208 and the docker network namespace doesn't have access to the raw hci device. However, a workaround is mentioned in the issue, which is to bind mount the outside network namespace into the container, and then enter the namespace for the bluetooth process.
Based on the idea, I've made a patch and it worked on my instance.
The patch
- hardcode the target network namespace
/rootns/net
- enter the namespace for any bluetooth opreations (
setns
is a per thread call )- HCIdump will setns before enter the running loop
- Upon init, bt_helper.py will start a temporary thread, setns and then call btmgmt to retrieve available bluetooth devices.
However I don't have the knowledge about home assistant config flow and don't know how to make it with ui and yaml configuration.
Here's my patch.
From 7176b3aa6db94015f0487a1c8cc013f6e9ab96a4 Mon Sep 17 00:00:00 2001
From: Yujia Qiao <ping@yqiao.me>
Date: Sat, 31 May 2025 15:54:35 +0800
Subject: [PATCH] run in target netns
Signed-off-by: Yujia Qiao <ping@yqiao.me>
---
__init__.py | 10 +++++++++-
bt_helpers.py | 24 +++++++++++++++++++++++-
config_flow.py | 1 -
3 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/__init__.py b/__init__.py
index 8e989b1de53a..0fc88f895daa 100755
--- a/__init__.py
+++ b/__init__.py
@@ -4,6 +4,7 @@ import copy
import json
import logging
from threading import Thread
+import os
import aioblescan as aiobs
import janus
@@ -19,7 +20,7 @@ from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.util import dt
from .ble_parser import BleParser
-from .bt_helpers import (BT_INTERFACES, BT_MULTI_SELECT, DEFAULT_BT_INTERFACE,
+from .bt_helpers import (BT_INTERFACES, BT_MULTI_SELECT, DEFAULT_BT_INTERFACE, NETNS,
reset_bluetooth)
from .const import (AES128KEY24_REGEX, AES128KEY32_REGEX,
AUTO_BINARY_SENSOR_LIST, AUTO_MANUFACTURER_DICT,
@@ -626,6 +627,13 @@ class HCIdump(Thread):
def run(self):
"""Run HCIdump thread."""
+ target_netns = NETNS
+ if target_netns:
+ # setns to the namespace located at target_netns
+ fd = os.open(target_netns, os.O_RDONLY)
+ os.setns(fd, os.CLONE_NEWNET)
+ os.close(fd)
+ _LOGGER.debug(f"Entered network namesapce {target_netns}")
while True:
_LOGGER.debug("HCIdump thread: Run")
mysocket = {}
diff --git a/bt_helpers.py b/bt_helpers.py
index 19cb4d1c10c5..025d008af977 100755
--- a/bt_helpers.py
+++ b/bt_helpers.py
@@ -1,6 +1,8 @@
"""BT helpers for ble_monitor."""
import logging
import time
+import os
+import threading
import pyric.utils.rfkill as rfkill
from btsocket import btmgmt_protocol, btmgmt_sync
@@ -79,6 +81,24 @@ class MGMTBluetoothCtl:
return True
return False
+def run_in_netns(netns_path, func, *args, **kwargs):
+ result_container = {}
+
+ def target():
+ try:
+ with open(netns_path, 'r') as fd:
+ os.setns(fd.fileno(), os.CLONE_NEWNET)
+ result_container['result'] = func(*args, **kwargs)
+ except Exception as e:
+ result_container['error'] = e
+
+ thread = threading.Thread(target=target)
+ thread.start()
+ thread.join()
+
+ if 'error' in result_container:
+ raise result_container['error']
+ return result_container.get('result')
# Bluetooth interfaces available on the system
def hci_get_mac(iface_list=None):
@@ -159,7 +179,9 @@ def reset_bluetooth(hci):
)
-BT_INTERFACES = hci_get_mac([0, 1, 2, 3])
+NETNS = '/rootns/net'
+
+BT_INTERFACES = run_in_netns(NETNS, lambda: hci_get_mac([0, 1, 2, 3]))
if BT_INTERFACES:
DEFAULT_BT_INTERFACE = list(BT_INTERFACES.items())[0][1]
DEFAULT_HCI_INTERFACE = list(BT_INTERFACES.items())[0][0]
Sensor type
No response
Relevant log output
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working