diff --git a/README.md b/README.md index 48c3f57..1e418fc 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,8 @@ Installation 5. Set bluepy-helper capability ``` - ./setcap.sh - Set up bluepy-helper capability to allow use by normal users - /usr/lib/python3.8/site-packages/bluepy-1.3.0-py3.8.egg/bluepy/bluepy-helper = cap_net_admin,cap_net_raw+eip + $ sudo ./bluepy_helper_cap.py + Set capacbility 'cap_net_raw,cap_net_admin' to /usr/lib/python3.8/site-packages/bluepy-1.3.0-py3.8.egg/bluepy/bluepy-helper ``` 6. If using a micro:bit, install Scratch-link hex on your device. diff --git a/bluepy_helper_cap.py b/bluepy_helper_cap.py new file mode 100755 index 0000000..00de779 --- /dev/null +++ b/bluepy_helper_cap.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +import sys +import os +import shutil +import bluepy +import subprocess + +import logging +logLevel = logging.INFO + +# for logging +logger = logging.getLogger(__name__) +formatter = logging.Formatter(fmt='%(asctime)s %(message)s') +handler = logging.StreamHandler() +handler.setLevel(logLevel) +handler.setFormatter(formatter) +logger.setLevel(logLevel) +logger.addHandler(handler) +logger.propagate = False + +# Check dependent tools +DEPENDENT_TOOLS = { + "setcap": "libcap2-bin (Ubuntu) or libcap (Arch)", +} + +for cmd in DEPENDENT_TOOLS: + if not shutil.which(cmd): + print(f"'{cmd}' not found. Install package {DEPENDENT_TOOLS[cmd]}.") + sys.exit(1) + +def helper_path(): + path = os.path.abspath(bluepy.__file__) + if not path: + logger.error("Bluepy module not found") + sys.exit(1) + if path.find("__init__.py") < 0: + logger.error(f"Unexpected bluepy module path: {path}") + sys.exit(1) + path = path.replace("__init__.py", "bluepy-helper") + return path + +def is_set(): + path = helper_path() + p = subprocess.run(["getcap", path], capture_output=True) + if p.returncode !=0: + logger.error(f"Failed to get capability of {path}") + return False + out = str(p.stdout) + return out.find("cap_net_admin") >= 0 and out.find("cap_net_raw") >= 0 + +def setcap(): + path = helper_path() + if is_set(): + return True + p = subprocess.run(["setcap", "cap_net_raw,cap_net_admin+eip", path], \ + capture_output=True) + if p.returncode !=0: + logger.error(f"Failed to set capability to {path}") + return False + print(f"Set capacbility 'cap_net_raw,cap_net_admin' to {path}") + return True + +if __name__ == "__main__": + setcap() diff --git a/scratch_link.py b/scratch_link.py index f577a69..9d9945a 100755 --- a/scratch_link.py +++ b/scratch_link.py @@ -21,6 +21,7 @@ import bluetooth # for BLESession (e.g. BBC micro:bit) from bluepy.btle import Scanner, UUID, Peripheral, DefaultDelegate from bluepy.btle import BTLEDisconnectError, BTLEManagementError +import bluepy_helper_cap import threading import time @@ -518,6 +519,11 @@ class BLESession(Session): err_msg = None if self.status == self.INITIAL and method == 'discover': + if not bluepy_helper_cap.is_set(): + logger.error("Capability is not set to bluepy helper.") + logger.error("Run bluepy_setcap.py with root privilege.") + logger.error("e.g. $ sudo bluepy_helper_cap.py") + sys.exit(1) found_ifaces = 0 for i in range(self.MAX_SCANNER_IF): scanner = Scanner(iface=i) diff --git a/setcap.sh b/setcap.sh deleted file mode 100755 index df58ccf..0000000 --- a/setcap.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -echo "Set up bluepy-helper capability to allow use by normal users" -find /usr -name bluepy-helper -exec sudo setcap \ - 'cap_net_raw,cap_net_admin+eip' {} \; 2> /dev/null -find /usr -name bluepy-helper -exec sudo getcap {} \; 2> /dev/null -