From 634a5e8cc6e53a2205f00ee7af2b6f432c525bb3 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Mon, 10 Mar 2025 18:52:23 +0100 Subject: [PATCH 1/2] qt: fix support for Wayland on linux Unfortunately the Comments before the `-extra-plugins` without line-break delimiter made it a separate command. The `-` in Makefile means `extra-plugins` is executed ignoring errors. This combination made CI pass without Wayland plugins actually being deployed. Fixes #3229. --- CHANGELOG.md | 3 +++ backend/update.go | 2 +- frontends/qt/Makefile | 10 +++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4b812095e..67990187d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - Reduced support for BitBox01 - Fix a bug that would prevent the app to perform firmware upgrade when offline. +# 4.47.1 +- Linux: fix support for Wayland + # 4.47.0 - Bundle BitBox02 firmware version v9.22.0 - Fix long transaction notes to show fully on multiple lines when necessary diff --git a/backend/update.go b/backend/update.go index 538297d86c..2ddbeee6d9 100644 --- a/backend/update.go +++ b/backend/update.go @@ -27,7 +27,7 @@ const updateFileURL = "https://bitboxapp.shiftcrypto.io/desktop.json" var ( // Version of the backend as displayed to the user. - Version = semver.NewSemVer(4, 47, 0) + Version = semver.NewSemVer(4, 47, 1) ) // UpdateFile is retrieved from the server. diff --git a/frontends/qt/Makefile b/frontends/qt/Makefile index 389b6bb7a1..2da8b574bb 100644 --- a/frontends/qt/Makefile +++ b/frontends/qt/Makefile @@ -30,12 +30,12 @@ linux: mv build/BitBox build/linux-tmp cp build/assets.rcc build/linux-tmp/ cp server/libserver.so build/linux-tmp + # Add Wayland libs so the app can run natively on Wayland too. + # The linuxdeployqt maintainer unfortunately refuses to support it automatically: https://github.com/probonopd/linuxdeployqt/issues/189 + # The list of related plugins was found by: `find $(qmake -query QT_INSTALL_PLUGINS) | grep wayland` cd build/linux-tmp && /opt/linuxdeployqt-continuous-x86_64.AppImage BitBox \ -bundle-non-qt-libs \ -unsupported-allow-new-glibc \ - # Add Wayland libs so the app can run natively on Wayland too. - # The linuxdeployqt maintainer unfortunately refuses to support it automatically: https://github.com/probonopd/linuxdeployqt/issues/189 - # The list of related plugins was found by: `find $(qmake -query QT_INSTALL_PLUGINS) | grep wayland` -extra-plugins=platforms/libqwayland-generic.so,platforms/libqwayland-egl.so,wayland-graphics-integration-client,wayland-decoration-client,wayland-shell-integration cp /usr/lib/x86_64-linux-gnu/nss/* build/linux-tmp/lib # See https://github.com/probonopd/linuxdeployqt/issues/554#issuecomment-1761834180 @@ -45,8 +45,8 @@ linux: cp resources/linux/usr/share/icons/hicolor/128x128/apps/bitbox.png build/linux-tmp mkdir build/tmp-deb/opt/ cp -aR build/linux-tmp build/tmp-deb/opt/bitbox - cd build/linux && fpm --after-install ../../resources/deb-afterinstall.sh -s dir -t deb -n bitbox -v 4.47.0 -C ../tmp-deb/ - cd build/linux && fpm --after-install ../../resources/deb-afterinstall.sh -s dir -t rpm -n bitbox -v 4.47.0 -C ../tmp-deb/ + cd build/linux && fpm --after-install ../../resources/deb-afterinstall.sh -s dir -t deb -n bitbox -v 4.47.1 -C ../tmp-deb/ + cd build/linux && fpm --after-install ../../resources/deb-afterinstall.sh -s dir -t rpm -n bitbox -v 4.47.1 -C ../tmp-deb/ # create AppImage cd build/linux-tmp && /opt/linuxdeployqt-continuous-x86_64.AppImage BitBox -appimage -unsupported-allow-new-glibc mv build/linux-tmp/BitBoxApp-*-x86_64.AppImage build/linux/ From b6cf9e12debf3ea629c073f000a32a19ef28724c Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Tue, 11 Mar 2025 13:58:17 +0100 Subject: [PATCH 2/2] qt: shutdown backend on backend shutdown to release devices Our hidraw udev rule: ``` KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2403", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="bitbox02-%n" ``` creates `/dev/bitbox02-N` symlinks (one per HID interface). The BitBoxApp uses libusb (default backend of karalabe/hid), not hidraw. However, when a device is opened, the corresponding symlink is removed, and restored when it is closed (presumably to ensure exclusive access). Howewer, we never closed the opened devices when the app closed, leading to the symlink being missing afterwards. This resulted in apps that use hidraw not being able to connect after the BitBoxApp connected first, like Sparrow. This commit calls the backend shutdown on app close, also closing the opened devices. --- CHANGELOG.md | 1 + backend/backend.go | 9 +++++++-- backend/bridgecommon/bridgecommon.go | 3 ++- backend/devices/usb/manager.go | 22 ++++++++++++++++++++++ frontends/qt/libserver.h | 2 +- frontends/qt/main.cpp | 1 + frontends/qt/server/server.go | 5 +++++ 7 files changed, 39 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67990187d2..f52563b73b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ # 4.47.1 - Linux: fix support for Wayland +- Linux: release device upon app close, enabling other apps to connect to the BitBox after the BitBoxApp closes # 4.47.0 - Bundle BitBox02 firmware version v9.22.0 diff --git a/backend/backend.go b/backend/backend.go index 5707c83407..98819d1a9d 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -898,12 +898,17 @@ func (backend *Backend) Environment() Environment { // Close shuts down the backend. After this, no other method should be called. func (backend *Backend) Close() error { + backend.ratesUpdater.Stop() + // Call this without `accountsAndKeystoreLock` as it eventually calls `DeregisterKeystore()`, + // which acquires the same lock. + if backend.usbManager != nil { + backend.usbManager.Close() + } + defer backend.accountsAndKeystoreLock.Lock()() errors := []string{} - backend.ratesUpdater.Stop() - backend.uninitAccounts(true) for _, coin := range backend.coins { diff --git a/backend/bridgecommon/bridgecommon.go b/backend/bridgecommon/bridgecommon.go index 0f665e0edb..134cefcd27 100644 --- a/backend/bridgecommon/bridgecommon.go +++ b/backend/bridgecommon/bridgecommon.go @@ -363,8 +363,9 @@ func Shutdown() { log := logging.Get().WithGroup("server") if globalShutdown != nil { + log.Info("Shutdown about to be called") globalShutdown() - log.Info("Shutdown called") + log.Info("Shutdown finished") } else { log.Info("Shutdown called, but backend not running") } diff --git a/backend/devices/usb/manager.go b/backend/devices/usb/manager.go index ea4027a8c6..284ba34047 100644 --- a/backend/devices/usb/manager.go +++ b/backend/devices/usb/manager.go @@ -91,6 +91,7 @@ type Manager struct { onUnregister func(string) updateCh chan struct{} + quitCh chan struct{} socksProxy socksproxy.SocksProxy @@ -118,6 +119,7 @@ func NewManager( onRegister: onRegister, onUnregister: onUnregister, updateCh: make(chan struct{}), + quitCh: make(chan struct{}), socksProxy: socksProxy, log: logging.Get().WithGroup("manager"), @@ -272,6 +274,14 @@ func (manager *Manager) Update() { func (manager *Manager) listen() { for { select { + case <-manager.quitCh: + return + default: + } + + select { + case <-manager.quitCh: + return case <-manager.updateCh: case <-time.After(time.Second): } @@ -336,3 +346,15 @@ func (manager *Manager) Start() { go manager.listen() manager.Update() } + +// Close closes and unregisters all devices. +func (manager *Manager) Close() { + manager.log.Info("Closing devices manager") + close(manager.quitCh) + for deviceID, device := range manager.devices { + manager.log.WithField("device-id", deviceID).Info("manager: closing device") + device.Close() + delete(manager.devices, deviceID) + manager.onUnregister(deviceID) + } +} diff --git a/frontends/qt/libserver.h b/frontends/qt/libserver.h index 91d2d7f863..e2f90a2713 100644 --- a/frontends/qt/libserver.h +++ b/frontends/qt/libserver.h @@ -49,7 +49,7 @@ extern void handleURI(cchar_t* uri); extern void serve(cppHeapFree cppHeapFreeFn, pushNotificationsCallback pushNotificationsFn, responseCallback responseFn, notifyUserCallback notifyUserFn, cchar_t* preferredLocale, getSaveFilenameCallback getSaveFilenameFn); extern void systemOpen(cchar_t* url); extern void goLog(cchar_t* msg); - +extern void backendShutdown(); #ifdef __cplusplus } #endif diff --git a/frontends/qt/main.cpp b/frontends/qt/main.cpp index 36bf0e0eaa..092e8ee0c1 100644 --- a/frontends/qt/main.cpp +++ b/frontends/qt/main.cpp @@ -460,6 +460,7 @@ int main(int argc, char *argv[]) delete view; view = nullptr; webClassMutex.unlock(); + backendShutdown(); }); #if defined(_WIN32) diff --git a/frontends/qt/server/server.go b/frontends/qt/server/server.go index f70fc503f7..ac3f9a1dd6 100644 --- a/frontends/qt/server/server.go +++ b/frontends/qt/server/server.go @@ -206,6 +206,11 @@ func goLog(msg *C.cchar_t) { logging.Get().WithGroup("qt-frontend").Info(goMsg) } +//export backendShutdown +func backendShutdown() { + bridgecommon.Shutdown() +} + func authResult(ok bool) { bridgecommon.AuthResult(ok) }