diff --git a/config/redbear-full.toml b/config/redbear-full.toml index 938ce487f5..0674632fc2 100644 --- a/config/redbear-full.toml +++ b/config/redbear-full.toml @@ -392,7 +392,7 @@ requires_weak = [ [service] cmd = "/usr/bin/redbear-greeterd" -envs = { VT = "3", REDBEAR_GREETER_USER = "greeter", KWIN_DRM_DEVICES = "/scheme/drm/card0", REDBEAR_DRM_WAIT_SECONDS = "10" } +envs = { VT = "3", REDBEAR_GREETER_USER = "greeter", KWIN_DRM_DEVICES = "/scheme/drm/card0", REDBEAR_DRM_WAIT_SECONDS = "30", REDBEAR_GREETER_RUNTIME_DIR = "/tmp/run/redbear-greeter" } type = "oneshot_async" """ @@ -444,7 +444,7 @@ requires_weak = [ [service] cmd = "/usr/bin/sddm" args = ["--no-daemon"] -envs = { QT_PLUGIN_PATH = "/usr/plugins", QT_QPA_PLATFORM_PLUGIN_PATH = "/usr/plugins/platforms", QML2_IMPORT_PATH = "/usr/qml", XCURSOR_THEME = "Pop", XKB_CONFIG_ROOT = "/usr/share/X11/xkb", KWIN_DRM_DEVICES = "/scheme/drm/card0", DISPLAY = ":0", WAYLAND_DISPLAY = "wayland-0", XDG_RUNTIME_DIR = "/tmp/run", XDG_SESSION_TYPE = "wayland" } +envs = { QT_PLUGIN_PATH = "/usr/plugins", QT_QPA_PLATFORM_PLUGIN_PATH = "/usr/plugins/platforms", QML2_IMPORT_PATH = "/usr/qml", XCURSOR_THEME = "Pop", XKB_CONFIG_ROOT = "/usr/share/X11/xkb", KWIN_DRM_DEVICES = "/scheme/drm/card0", DISPLAY = ":0", WAYLAND_DISPLAY = "wayland-0", XDG_RUNTIME_DIR = "/tmp/run/redbear-greeter", XDG_SESSION_TYPE = "wayland", REDBEAR_DRM_WAIT_SECONDS = "30" } type = "oneshot_async" """ diff --git a/local/recipes/kde/kf6-kcmutils/source/CMakeLists.txt b/local/recipes/kde/kf6-kcmutils/source/CMakeLists.txt index 3c25ad1ff5..b553483ede 100644 --- a/local/recipes/kde/kf6-kcmutils/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-kcmutils/source/CMakeLists.txt @@ -116,6 +116,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) +find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) # shall we use DBus? # enabled per default on Linux & BSD systems diff --git a/local/recipes/kde/kf6-kcompletion/source/CMakeLists.txt b/local/recipes/kde/kf6-kcompletion/source/CMakeLists.txt index 0220e96266..d1d4881da0 100644 --- a/local/recipes/kde/kf6-kcompletion/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-kcompletion/source/CMakeLists.txt @@ -85,6 +85,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) +find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(KF6Codecs ${KF_DEP_VERSION} REQUIRED) find_package(KF6Config ${KF_DEP_VERSION} REQUIRED) diff --git a/local/recipes/kde/kf6-kconfigwidgets/source/CMakeLists.txt b/local/recipes/kde/kf6-kconfigwidgets/source/CMakeLists.txt index 38fe6f0ccc..ffa4f2adbf 100644 --- a/local/recipes/kde/kf6-kconfigwidgets/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-kconfigwidgets/source/CMakeLists.txt @@ -88,6 +88,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) +find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) # shall we use DBus? # enabled per default on Linux & BSD systems diff --git a/local/recipes/kde/kf6-kdeclarative/source/CMakeLists.txt b/local/recipes/kde/kf6-kdeclarative/source/CMakeLists.txt index e775c475d9..47458d1663 100644 --- a/local/recipes/kde/kf6-kdeclarative/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-kdeclarative/source/CMakeLists.txt @@ -32,7 +32,7 @@ find_package(KF6GuiAddons ${KF_DEP_VERSION} REQUIRED) if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT REDOX) -####################################################### find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED) +######################################################## find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED) set(HAVE_KGLOBALACCEL TRUE) else() set(HAVE_KGLOBALACCEL FALSE) diff --git a/local/recipes/kde/kf6-kiconthemes/source/CMakeLists.txt b/local/recipes/kde/kf6-kiconthemes/source/CMakeLists.txt index f72ba5e8d6..9b516742e0 100644 --- a/local/recipes/kde/kf6-kiconthemes/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-kiconthemes/source/CMakeLists.txt @@ -107,6 +107,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) +find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6Svg ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE) # shall we use DBus? diff --git a/local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt b/local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt index 64597ff215..dad1065938 100644 --- a/local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt @@ -81,6 +81,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) +find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].") diff --git a/local/recipes/kde/kf6-kjobwidgets/source/CMakeLists.txt b/local/recipes/kde/kf6-kjobwidgets/source/CMakeLists.txt index af8ea66c7f..f3d8f2dacb 100644 --- a/local/recipes/kde/kf6-kjobwidgets/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-kjobwidgets/source/CMakeLists.txt @@ -74,6 +74,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) +find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) option(WITH_X11 "Build with support for QX11Info::appUserTime()" ON) diff --git a/local/recipes/kde/kf6-ktextwidgets/source/CMakeLists.txt b/local/recipes/kde/kf6-ktextwidgets/source/CMakeLists.txt index 1f78e416d4..0e64d935dd 100644 --- a/local/recipes/kde/kf6-ktextwidgets/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-ktextwidgets/source/CMakeLists.txt @@ -89,6 +89,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) +find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) if (WITH_TEXT_TO_SPEECH) find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED TextToSpeech) diff --git a/local/recipes/kde/kf6-kxmlgui/source/src/kswitchlanguagedialog_p.cpp b/local/recipes/kde/kf6-kxmlgui/source/src/kswitchlanguagedialog_p.cpp index e4c269d12b..8e3a00a714 100644 --- a/local/recipes/kde/kf6-kxmlgui/source/src/kswitchlanguagedialog_p.cpp +++ b/local/recipes/kde/kf6-kxmlgui/source/src/kswitchlanguagedialog_p.cpp @@ -74,10 +74,10 @@ void initializeLanguages() // Ideally setting the LANGUAGE would change the default QLocale too // but unfortunately this is too late since the QCoreApplication constructor // already created a QLocale at this stage so we need to set the reset it -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale // this is highly dependent on Qt internals, so may break, but oh well -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale(); -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale(); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy; } } diff --git a/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt b/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt index eb1bf59393..6983e7e367 100644 --- a/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt +++ b/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt @@ -1425,6 +1425,20 @@ qt_internal_extend_target(Core CONDITION REDOX io/qstorageinfo_unix.cpp ) +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + qt_internal_extend_target(Core CONDITION QT_FEATURE_cpp_winrt SOURCES platform/windows/qfactorycacheregistration_p.h @@ -1677,6 +1691,20 @@ qt_internal_extend_target(Core CONDITION REDOX io/qstorageinfo_unix.cpp ) +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + qt_internal_extend_target(Core CONDITION QT_FEATURE_itemmodel SOURCES itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h diff --git a/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h b/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h index dab50ed5d8..26d3a1114a 100644 --- a/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h +++ b/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h @@ -209,6 +209,8 @@ static_assert(std::is_signed_v, #include #include #include +#include +#include #ifndef static_assert #define static_assert _Static_assert #endif diff --git a/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp b/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp index 3ac93cc3e5..b238def903 100644 --- a/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp +++ b/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp @@ -1153,6 +1153,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l #ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT if (header.hopLimit != -1) { msg.msg_controllen += CMSG_SPACE(sizeof(int)); @@ -1193,6 +1195,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l #endif #endif #endif +#endif +#endif #endif if (header.ifindex != 0 || !header.senderAddress.isNull()) { struct in6_pktinfo *data = reinterpret_cast(CMSG_DATA(cmsgptr)); diff --git a/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h b/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h index 4426db959b..a8dfe1dc79 100644 --- a/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h +++ b/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h @@ -53,6 +53,8 @@ #include #include #include +#include +#include #include #if defined(Q_OS_VXWORKS) diff --git a/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h b/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h index aa2d8c7a3d..7fd87b6215 100644 --- a/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h +++ b/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h @@ -83,6 +83,8 @@ public: #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) #if QT_CONFIG(opengl) virtual QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const = 0; #endif /* QT_CONFIG(opengl) */ @@ -116,6 +118,8 @@ public: #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ virtual bool canCreatePlatformOffscreenSurface() const { return false; } #if QT_CONFIG(opengl) @@ -160,6 +164,8 @@ public: #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) #if QT_CONFIG(opengl) virtual void *nativeResourceForContext(NativeResource /*resource*/, QPlatformOpenGLContext */*context*/) { return nullptr; } #endif /* QT_CONFIG(opengl) */ @@ -194,6 +200,8 @@ public: #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ }; } diff --git a/local/recipes/system/redbear-greeter/P2-compositor-timeout-30s.patch b/local/recipes/system/redbear-greeter/P2-compositor-timeout-30s.patch deleted file mode 100644 index 908b06630c..0000000000 --- a/local/recipes/system/redbear-greeter/P2-compositor-timeout-30s.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/local/recipes/system/redbear-greeter/source/src/main.rs b/local/recipes/system/redbear-greeter/source/src/main.rs -index a149dac02..1a503faab 100644 ---- a/local/recipes/system/redbear-greeter/source/src/main.rs -+++ b/local/recipes/system/redbear-greeter/source/src/main.rs -@@ -246,7 +246,7 @@ impl GreeterDaemon { - - fn wait_for_wayland_socket(&self) -> Result<(), String> { - let socket_path = self.runtime_dir.join(&self.wayland_display); -- for _ in 0..60 { -+ for _ in 0..120 { - if socket_path.exists() { - return Ok(()); - } diff --git a/local/recipes/system/redbear-greeter/source/redbear-greeter-compositor b/local/recipes/system/redbear-greeter/source/redbear-greeter-compositor index 8f54130668..1907420973 100755 --- a/local/recipes/system/redbear-greeter/source/redbear-greeter-compositor +++ b/local/recipes/system/redbear-greeter/source/redbear-greeter-compositor @@ -47,7 +47,7 @@ drm_devices_ready() { wait_for_drm_devices() { local devices="${1:-}" - local wait_seconds="${REDBEAR_DRM_WAIT_SECONDS:-2}" + local wait_seconds="${REDBEAR_DRM_WAIT_SECONDS:-30}" local attempts=0 if [ -z "$devices" ]; then diff --git a/local/recipes/system/redbear-greeter/source/src/main.rs b/local/recipes/system/redbear-greeter/source/src/main.rs index dbcc5a1ce6..88c157302e 100644 --- a/local/recipes/system/redbear-greeter/source/src/main.rs +++ b/local/recipes/system/redbear-greeter/source/src/main.rs @@ -166,6 +166,15 @@ impl GreeterDaemon { .and_then(|value| value.parse::().ok()) .unwrap_or(3); let greeter_user = env::var("REDBEAR_GREETER_USER").unwrap_or_else(|_| String::from("greeter")); + // Runtime dir may be overridden by the service unit (REDBEAR_GREETER_RUNTIME_DIR). + // When unset we keep the historical default that matches the wrapper script and + // the redbear-greeter-compositor /scheme/drm fallback chain. + let runtime_dir = env::var("REDBEAR_GREETER_RUNTIME_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| PathBuf::from("/tmp/run/redbear-greeter")); + if let Err(err) = fs::create_dir_all(&runtime_dir) { + return Err(format!("failed to create runtime dir {}: {err}", runtime_dir.display())); + } if Path::new(GREETER_SOCKET_PATH).exists() { fs::remove_file(GREETER_SOCKET_PATH) @@ -185,7 +194,7 @@ impl GreeterDaemon { listener, vt, greeter_user, - runtime_dir: PathBuf::from("/tmp/run/redbear-greeter"), + runtime_dir, wayland_display: String::from("wayland-0"), state: GreeterState::Starting, message: String::from("Starting greeter"), @@ -248,14 +257,48 @@ impl GreeterDaemon { } fn wait_for_wayland_socket(&self) -> Result<(), String> { + // Primary socket: where the greeter compositor is configured to bind. + // Fallback socket: the canonical /tmp/run/ path that SDDM and + // other session services publish, in case the compositor was started + // outside the greeter's runtime dir. let socket_path = self.runtime_dir.join(&self.wayland_display); - for _ in 0..120 { - if socket_path.exists() { + let fallback_path = PathBuf::from("/tmp/run").join(&self.wayland_display); + // Greeter compositor can take a long time on real hardware: + // - redox-drm device enumeration: up to ~10s on a busy PCI bus + // - DRM device probe + KMS init: up to ~10s on first boot + // - virtual-fallback framebuffer mmap: ~1-2s + // - UnixListener::bind + accept loop startup: <1s + // 45s (180 * 250ms) gives a comfortable bound for the worst case + // (DRM device never appears, virtual backend kicks in) plus slack + // for SDDM/KDE session compositors that may briefly unbind and + // rebind the socket during hot-reload. + for attempt in 0..180 { + if socket_path.exists() || fallback_path.exists() { + if attempt == 0 { + return Ok(()); + } + eprintln!( + "redbear-greeterd: wayland socket appeared after {}ms (primary={}, fallback={})", + attempt * 250, + socket_path.display(), + fallback_path.display() + ); return Ok(()); } + if attempt % 20 == 0 && attempt > 0 { + eprintln!( + "redbear-greeterd: still waiting for compositor socket {} ({}s elapsed)", + socket_path.display(), + attempt / 4 + ); + } thread::sleep(Duration::from_millis(250)); } - Err(format!("timed out waiting for compositor socket {}", socket_path.display())) + Err(format!( + "timed out waiting for compositor socket {} (also probed {})", + socket_path.display(), + fallback_path.display() + )) } fn start_surface(&mut self) -> Result<(), String> { diff --git a/local/recipes/system/redbear-sessiond/source/src/main.rs b/local/recipes/system/redbear-sessiond/source/src/main.rs index ae37e61d43..4c04ec469e 100644 --- a/local/recipes/system/redbear-sessiond/source/src/main.rs +++ b/local/recipes/system/redbear-sessiond/source/src/main.rs @@ -60,21 +60,70 @@ fn parse_object_path(path: &str) -> Result> { Ok(OwnedObjectPath::try_from(path.to_owned())?) } -async fn wait_for_dbus_socket() { - let socket_path = env::var("DBUS_STARTER_ADDRESS") - .ok() - .and_then(|addr| addr.strip_prefix("unix:path=").map(String::from)) - .or_else(|| env::var("DBUS_SYSTEM_BUS_ADDRESS").ok() - .and_then(|addr| addr.strip_prefix("unix:path=").map(String::from))) - .unwrap_or_else(|| "/run/dbus/system_bus_socket".to_string()); +/// Candidate D-Bus system bus socket locations, in order of preference. +/// +/// Red Bear OS may launch `dbus-daemon --system` with the standard Redox path +/// (`/run/dbus/system_bus_socket`) or with one of the FHS-style fallback paths +/// that D-Bus itself accepts (`/var/run/dbus/...`). The `DBUS_STARTER_ADDRESS` +/// and `DBUS_SYSTEM_BUS_ADDRESS` env vars take precedence when set. +fn dbus_socket_candidates() -> Vec { + let mut candidates: Vec = Vec::new(); - for _ in 0..30 { - if tokio::net::UnixStream::connect(&socket_path).await.is_ok() { - return; + for env_name in ["DBUS_STARTER_ADDRESS", "DBUS_SYSTEM_BUS_ADDRESS"] { + if let Ok(addr) = env::var(env_name) { + if let Some(path) = addr.strip_prefix("unix:path=") { + candidates.push(path.to_string()); + } else if addr.starts_with('/') { + // Bare path provided by the caller. + candidates.push(addr); + } + } + } + + // Standard locations, in the order D-Bus itself tries them. + for fallback in [ + "/run/dbus/system_bus_socket", + "/var/run/dbus/system_bus_socket", + ] { + if !candidates.iter().any(|c| c == fallback) { + candidates.push(fallback.to_string()); + } + } + + candidates +} + +async fn wait_for_dbus_socket() { + let candidates = dbus_socket_candidates(); + eprintln!( + "redbear-sessiond: probing {} D-Bus socket candidate(s): {:?}", + candidates.len(), + candidates + ); + + // 30s budget total, polled at 1s intervals. We cycle through candidates + // each tick so a freshly-bound socket on a non-default path is picked up + // without having to wait a full 30s for the env-driven path. + for tick in 0..30 { + for candidate in &candidates { + if tokio::net::UnixStream::connect(candidate).await.is_ok() { + eprintln!( + "redbear-sessiond: D-Bus socket reachable at {candidate} after {tick}s" + ); + return; + } + } + if tick == 0 { + eprintln!("redbear-sessiond: still waiting for D-Bus socket (up to 30s)"); + } else if tick % 5 == 0 { + eprintln!("redbear-sessiond: still waiting for D-Bus socket ({tick}s elapsed)"); } tokio::time::sleep(Duration::from_secs(1)).await; } - eprintln!("redbear-sessiond: timed out waiting for D-Bus socket at {socket_path}"); + eprintln!( + "redbear-sessiond: timed out waiting for D-Bus socket, last tried: {:?}", + candidates + ); } fn system_connection_builder() -> Result, Box> { @@ -120,6 +169,13 @@ async fn run_daemon() -> Result<(), Box> { let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false); + // Resolve the bus address once so retry logs can name the actual endpoint + // we are connecting to (was previously hidden, making transient I/O errors + // impossible to triage). + let bus_addr = env::var("DBUS_STARTER_ADDRESS") + .or_else(|_| env::var("DBUS_SYSTEM_BUS_ADDRESS")) + .unwrap_or_else(|_| "unix:path=/run/dbus/system_bus_socket".to_string()); + let mut last_err = None; for attempt in 1..=3 { let session_path = parse_object_path(SESSION_PATH)?; @@ -141,7 +197,7 @@ async fn run_daemon() -> Result<(), Box> { .await { Ok(connection) => { - eprintln!("redbear-sessiond: registered {BUS_NAME} on the system bus"); + eprintln!("redbear-sessiond: registered {BUS_NAME} on the system bus at {bus_addr}"); control::start_control_socket(runtime.clone(), shutdown_tx.clone()); tokio::spawn(acpi_watcher::watch_and_emit(connection.clone(), runtime.clone())); wait_for_shutdown(shutdown_rx).await?; @@ -150,7 +206,9 @@ async fn run_daemon() -> Result<(), Box> { } Err(err) => { if attempt < 3 { - eprintln!("redbear-sessiond: attempt {attempt}/3 failed ({err}), retrying in 1s..."); + eprintln!( + "redbear-sessiond: attempt {attempt}/3 failed on {bus_addr} ({err}), retrying in 1s..." + ); tokio::time::sleep(Duration::from_secs(1)).await; } last_err = Some(err.into());