redox-drm: add virtio-gpu detection (1AF4:1050/1052) and Intel ARC PCI IDs

greeter: fix Wayland socket timeout (45s wait, dual-path probe)
sessiond: add D-Bus socket candidate probing with better error logging
config: align greeter/sddm runtime dirs and DRM wait timeouts
This commit is contained in:
2026-06-20 22:41:26 +03:00
parent f5311f16c8
commit 6006d20178
19 changed files with 176 additions and 37 deletions
+2 -2
View File
@@ -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"
"""
@@ -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
@@ -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)
@@ -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
@@ -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)
@@ -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?
@@ -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].")
@@ -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)
@@ -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)
@@ -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;
}
}
@@ -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
@@ -209,6 +209,8 @@ static_assert(std::is_signed_v<qint128>,
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#ifndef static_assert
#define static_assert _Static_assert
#endif
@@ -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<in6_pktinfo *>(CMSG_DATA(cmsgptr));
@@ -53,6 +53,8 @@
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#if defined(Q_OS_VXWORKS)
@@ -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) */
};
}
@@ -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(());
}
@@ -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
@@ -166,6 +166,15 @@ impl GreeterDaemon {
.and_then(|value| value.parse::<u32>().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/<display> 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> {
@@ -60,21 +60,70 @@ fn parse_object_path(path: &str) -> Result<OwnedObjectPath, Box<dyn Error>> {
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<String> {
let mut candidates: Vec<String> = 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<ConnectionBuilder<'static>, Box<dyn Error>> {
@@ -120,6 +169,13 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
.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<dyn Error>> {
}
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());