feat(python): detect busy device as a separate condition

fixes #1026
pull/2352/head
matejcik 2 years ago
parent aaa224a4f7
commit 8049282bdb

@ -0,0 +1 @@
New exception type `DeviceIsBusy` indicates that the device is in use by another process.

@ -0,0 +1 @@
trezorctl will correctly report that device is in use.

@ -21,9 +21,8 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional
import click import click
from .. import exceptions from .. import exceptions, transport
from ..client import TrezorClient from ..client import TrezorClient
from ..transport import get_transport
from ..ui import ClickUI, ScriptUI from ..ui import ClickUI, ScriptUI
if TYPE_CHECKING: if TYPE_CHECKING:
@ -67,14 +66,14 @@ class TrezorConnection:
def get_transport(self) -> "Transport": def get_transport(self) -> "Transport":
try: try:
# look for transport without prefix search # look for transport without prefix search
return get_transport(self.path, prefix_search=False) return transport.get_transport(self.path, prefix_search=False)
except Exception: except Exception:
# most likely not found. try again below. # most likely not found. try again below.
pass pass
# look for transport with prefix search # look for transport with prefix search
# if this fails, we want the exception to bubble up to the caller # if this fails, we want the exception to bubble up to the caller
return get_transport(self.path, prefix_search=True) return transport.get_transport(self.path, prefix_search=True)
def get_ui(self) -> "TrezorClientUI": def get_ui(self) -> "TrezorClientUI":
if self.script: if self.script:
@ -101,6 +100,9 @@ class TrezorConnection:
""" """
try: try:
client = self.get_client() client = self.get_client()
except transport.DeviceIsBusy:
click.echo("Device is in use by another process.")
sys.exit(1)
except Exception: except Exception:
click.echo("Failed to find a Trezor device.") click.echo("Failed to find a Trezor device.")
if self.path is not None: if self.path is not None:

@ -26,7 +26,7 @@ import click
from .. import log, messages, protobuf, ui from .. import log, messages, protobuf, ui
from ..client import TrezorClient from ..client import TrezorClient
from ..transport import enumerate_devices from ..transport import DeviceIsBusy, enumerate_devices
from ..transport.udp import UdpTransport from ..transport.udp import UdpTransport
from . import ( from . import (
AliasedGroup, AliasedGroup,
@ -247,9 +247,15 @@ def list_devices(no_resolve: bool) -> Optional[Iterable["Transport"]]:
return enumerate_devices() return enumerate_devices()
for transport in enumerate_devices(): for transport in enumerate_devices():
client = TrezorClient(transport, ui=ui.ClickUI()) try:
click.echo(f"{transport} - {format_device_name(client.features)}") client = TrezorClient(transport, ui=ui.ClickUI())
client.end_session() description = format_device_name(client.features)
client.end_session()
except DeviceIsBusy:
description = "Device is in use by another process"
except Exception:
description = "Failed to read details"
click.echo(f"{transport} - {description}")
return None return None

@ -48,6 +48,10 @@ class TransportException(TrezorException):
pass pass
class DeviceIsBusy(TransportException):
pass
class Transport: class Transport:
"""Raw connection to a Trezor device. """Raw connection to a Trezor device.

@ -21,7 +21,7 @@ from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional
import requests import requests
from ..log import DUMP_PACKETS from ..log import DUMP_PACKETS
from . import MessagePayload, Transport, TransportException from . import DeviceIsBusy, MessagePayload, Transport, TransportException
if TYPE_CHECKING: if TYPE_CHECKING:
from ..models import TrezorModel from ..models import TrezorModel
@ -37,14 +37,19 @@ CONNECTION = requests.Session()
CONNECTION.headers.update(TREZORD_ORIGIN_HEADER) CONNECTION.headers.update(TREZORD_ORIGIN_HEADER)
def call_bridge(uri: str, data: Optional[str] = None) -> requests.Response: class BridgeException(TransportException):
url = TREZORD_HOST + "/" + uri def __init__(self, path: str, status: int, message: str) -> None:
self.path = path
self.status = status
self.message = message
super().__init__(f"trezord: {path} failed with code {status}: {message}")
def call_bridge(path: str, data: Optional[str] = None) -> requests.Response:
url = TREZORD_HOST + "/" + path
r = CONNECTION.post(url, data=data) r = CONNECTION.post(url, data=data)
if r.status_code != 200: if r.status_code != 200:
error_str = ( raise BridgeException(path, r.status_code, r.json()["error"])
f"trezord: {uri} failed with code {r.status_code}: {r.json()['error']}"
)
raise TransportException(error_str)
return r return r
@ -150,7 +155,12 @@ class BridgeTransport(Transport):
return [] return []
def begin_session(self) -> None: def begin_session(self) -> None:
data = self._call("acquire/" + self.device["path"]) try:
data = self._call("acquire/" + self.device["path"])
except BridgeException as e:
if e.message == "wrong previous session":
raise DeviceIsBusy(self.device["path"]) from e
raise
self.session = data.json()["session"] self.session = data.json()["session"]
def end_session(self) -> None: def end_session(self) -> None:

@ -22,7 +22,7 @@ from typing import Iterable, List, Optional
from ..log import DUMP_PACKETS from ..log import DUMP_PACKETS
from ..models import TREZORS, TrezorModel from ..models import TREZORS, TrezorModel
from . import UDEV_RULES_STR, TransportException from . import UDEV_RULES_STR, DeviceIsBusy, TransportException
from .protocol import ProtocolBasedTransport, ProtocolV1 from .protocol import ProtocolBasedTransport, ProtocolV1
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -57,7 +57,10 @@ class WebUsbHandle:
else: else:
args = () args = ()
raise IOError("Cannot open device", *args) raise IOError("Cannot open device", *args)
self.handle.claimInterface(self.interface) try:
self.handle.claimInterface(self.interface)
except usb1.USBErrorAccess as e:
raise DeviceIsBusy(self.device) from e
def close(self) -> None: def close(self) -> None:
if self.handle is not None: if self.handle is not None:

Loading…
Cancel
Save