fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed

Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
This commit is contained in:
2026-05-05 20:20:37 +01:00
parent a5f97b6632
commit f31522130f
81834 changed files with 11051982 additions and 108 deletions
@@ -0,0 +1,11 @@
#####################################################################
## tst_wl_reconnect Test:
#####################################################################
qt_internal_add_test(tst_wl_reconnect
SOURCES
wl-socket.c
tst_reconnect.cpp
LIBRARIES
SharedClientTest
)
@@ -0,0 +1,260 @@
// Copyright (C) 2023 David Edmundson <davidedmundson@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mockcompositor.h"
#include <QBackingStore>
#include <QPainter>
#include <QScreen>
#include <QWindow>
#include <QMimeData>
#include <QPixmap>
#include <QDrag>
#include <QWindow>
#if QT_CONFIG(opengl)
#include <QOpenGLWindow>
#endif
#include <QRasterWindow>
#include <QtTest/QtTest>
#include <QtWaylandClient/private/qwaylandintegration_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include "wl-socket.h"
#include <unistd.h>
using namespace MockCompositor;
class TestWindow : public QRasterWindow
{
public:
TestWindow()
{
}
void focusInEvent(QFocusEvent *) override
{
++focusInEventCount;
}
void focusOutEvent(QFocusEvent *) override
{
++focusOutEventCount;
}
void keyPressEvent(QKeyEvent *event) override
{
++keyPressEventCount;
keyCode = event->nativeScanCode();
}
void keyReleaseEvent(QKeyEvent *event) override
{
++keyReleaseEventCount;
keyCode = event->nativeScanCode();
}
void mousePressEvent(QMouseEvent *event) override
{
++mousePressEventCount;
mousePressPos = event->position().toPoint();
}
void mouseReleaseEvent(QMouseEvent *) override
{
++mouseReleaseEventCount;
}
void touchEvent(QTouchEvent *event) override
{
Q_UNUSED(event);
++touchEventCount;
}
QPoint frameOffset() const { return QPoint(frameMargins().left(), frameMargins().top()); }
int focusInEventCount = 0;
int focusOutEventCount = 0;
int keyPressEventCount = 0;
int keyReleaseEventCount = 0;
int mousePressEventCount = 0;
int mouseReleaseEventCount = 0;
int touchEventCount = 0;
uint keyCode = 0;
QPoint mousePressPos;
};
class tst_WaylandReconnect : public QObject
{
Q_OBJECT
public:
tst_WaylandReconnect();
void triggerReconnect();
template<typename function_type, typename... arg_types>
auto exec(function_type func, arg_types&&... args) -> decltype(func())
{
return m_comp->exec(func, std::forward<arg_types>(args)...);
}
private Q_SLOTS:
//core
void cleanup() { QTRY_VERIFY2(m_comp->isClean(), qPrintable(m_comp->dirtyMessage())); }
void basicWindow();
void multipleScreens();
//input
void keyFocus();
private:
void configureWindow();
QScopedPointer<DefaultCompositor> m_comp;
wl_socket *m_socket;
};
tst_WaylandReconnect::tst_WaylandReconnect()
{
m_socket = wl_socket_create();
QVERIFY(m_socket);
const int socketFd = wl_socket_get_fd(m_socket);
const QByteArray socketName = wl_socket_get_display_name(m_socket);
qputenv("WAYLAND_DISPLAY", socketName);
m_comp.reset(new DefaultCompositor(CoreCompositor::Default, dup(socketFd)));
m_comp->m_config.autoEnter = false;
}
void tst_WaylandReconnect::triggerReconnect()
{
const int socketFd = wl_socket_get_fd(m_socket);
m_comp.reset(new DefaultCompositor(CoreCompositor::Default, dup(socketFd)));
m_comp->m_config.autoEnter = false;
QTest::qWait(50); //we need to spin the main loop to actually reconnect
}
void tst_WaylandReconnect::basicWindow()
{
QRasterWindow window;
window.resize(64, 48);
window.show();
QCOMPOSITOR_TRY_VERIFY(m_comp->xdgToplevel());
m_comp->surface(0)->sendEnter(m_comp->output(0));
triggerReconnect();
QCOMPOSITOR_TRY_VERIFY(m_comp->xdgToplevel());
}
void tst_WaylandReconnect::multipleScreens()
{
exec([this] { m_comp->add<Output>(); });
QRasterWindow window1;
window1.resize(64, 48);
window1.show();
QRasterWindow window2;
window2.resize(64, 48);
window2.show();
QCOMPOSITOR_TRY_VERIFY(m_comp->xdgToplevel(0));
QCOMPOSITOR_TRY_VERIFY(m_comp->xdgToplevel(1));
QCOMPOSITOR_TRY_VERIFY(!m_comp->output(1)->resourceMap().isEmpty());
// ensure they are on different outputs
exec([this] {
m_comp->surface(0)->sendEnter(m_comp->output(0));
m_comp->surface(1)->sendEnter(m_comp->output(1));
});
QTRY_VERIFY(window1.screen() != window2.screen());
auto originalScreens = QGuiApplication::screens();
QSignalSpy screenRemovedSpy(qGuiApp, &QGuiApplication::screenRemoved);
QVERIFY(screenRemovedSpy.isValid());
triggerReconnect();
// All screens plus temporary placeholder screen removed
QCOMPARE(screenRemovedSpy.count(), originalScreens.count() + 1);
for (const auto &screen : std::as_const(screenRemovedSpy)) {
originalScreens.removeOne(screen[0].value<QScreen *>());
}
QVERIFY(originalScreens.isEmpty());
QCOMPOSITOR_TRY_VERIFY(m_comp->xdgToplevel(0));
QCOMPOSITOR_TRY_VERIFY(m_comp->xdgToplevel(1));
QVERIFY(window1.screen());
QVERIFY(window2.screen());
QVERIFY(QGuiApplication::screens().contains(window1.screen()));
QVERIFY(QGuiApplication::screens().contains(window2.screen()));
}
void tst_WaylandReconnect::keyFocus()
{
TestWindow window;
window.resize(64, 48);
window.show();
configureWindow();
QTRY_VERIFY(window.isExposed());
exec([&] {
m_comp->keyboard()->sendEnter(m_comp->surface());
});
QTRY_COMPARE(window.focusInEventCount, 1);
uint keyCode = 80;
QCOMPARE(window.keyPressEventCount, 0);
exec([&] {
m_comp->keyboard()->sendKey(m_comp->client(), keyCode - 8, Keyboard::key_state_pressed);
});
QTRY_COMPARE(window.keyPressEventCount, 1);
QCOMPARE(QGuiApplication::focusWindow(), &window);
triggerReconnect();
configureWindow();
// on reconnect our knowledge of focus is reset to a clean slate
QCOMPARE(QGuiApplication::focusWindow(), nullptr);
QTRY_COMPARE(window.focusOutEventCount, 1);
// fake the user explicitly focussing this window afterwards
exec([&] {
m_comp->keyboard()->sendEnter(m_comp->surface());
});
exec([&] {
m_comp->keyboard()->sendKey(m_comp->client(), keyCode - 8, Keyboard::key_state_pressed);
});
QTRY_COMPARE(window.focusInEventCount, 2);
QTRY_COMPARE(window.keyPressEventCount, 2);
}
void tst_WaylandReconnect::configureWindow()
{
QCOMPOSITOR_TRY_VERIFY(m_comp->xdgToplevel());
m_comp->exec([&] {
m_comp->xdgToplevel()->sendConfigure({0, 0}, {});
const uint serial = m_comp->nextSerial(); // Let the window decide the size
m_comp->xdgSurface()->sendConfigure(serial);
});
}
int main(int argc, char **argv)
{
// Note when debugging that a failing reconnect will exit this
// test rather than fail. Making sure it finishes is important!
QTemporaryDir tmpRuntimeDir;
qputenv("QT_QPA_PLATFORM", "wayland"); // force QGuiApplication to use wayland plugin
qputenv("QT_WAYLAND_RECONNECT", "1");
qputenv("XDG_CURRENT_DESKTOP", "qtwaylandtests");
tst_WaylandReconnect tc;
QGuiApplication app(argc, argv);
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&tc, argc, argv);
}
#include "tst_reconnect.moc"
@@ -0,0 +1,166 @@
// Copyright (C) 2023 David Edmundson <davidedmundson@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define _DEFAULT_SOURCE
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
/* This is the size of the char array in struct sock_addr_un.
* No Wayland socket can be created with a path longer than this,
* including the null terminator.
*/
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif
#define LOCK_SUFFIX ".lock"
#define LOCK_SUFFIXLEN 5
struct wl_socket {
int fd;
int fd_lock;
struct sockaddr_un addr;
char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN];
char display_name[32];
};
static struct wl_socket *wl_socket_alloc(void)
{
struct wl_socket *s;
s = malloc(sizeof *s);
if (!s)
return NULL;
s->fd = -1;
s->fd_lock = -1;
return s;
}
static int wl_socket_lock(struct wl_socket *socket)
{
struct stat socket_stat;
snprintf(socket->lock_addr, sizeof socket->lock_addr, "%s%s", socket->addr.sun_path, LOCK_SUFFIX);
// differening from kwin, we're back to setting CLOEXEC as we're all in process
socket->fd_lock = open(socket->lock_addr, O_CREAT | O_RDWR | O_CLOEXEC, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
if (socket->fd_lock < 0) {
printf("unable to open lockfile %s check permissions\n", socket->lock_addr);
goto err;
}
if (flock(socket->fd_lock, LOCK_EX | LOCK_NB) < 0) {
printf("unable to lock lockfile %s, maybe another compositor is running\n", socket->lock_addr);
goto err_fd;
}
if (lstat(socket->addr.sun_path, &socket_stat) < 0) {
if (errno != ENOENT) {
printf("did not manage to stat file %s\n", socket->addr.sun_path);
goto err_fd;
}
} else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) {
unlink(socket->addr.sun_path);
}
return 0;
err_fd:
close(socket->fd_lock);
socket->fd_lock = -1;
err:
*socket->lock_addr = 0;
/* we did not set this value here, but without lock the
* socket won't be created anyway. This prevents the
* wl_socket_destroy from unlinking already existing socket
* created by other compositor */
*socket->addr.sun_path = 0;
return -1;
}
void wl_socket_destroy(struct wl_socket *s)
{
if (s->addr.sun_path[0])
unlink(s->addr.sun_path);
if (s->fd >= 0)
close(s->fd);
if (s->lock_addr[0])
unlink(s->lock_addr);
if (s->fd_lock >= 0)
close(s->fd_lock);
free(s);
}
const char *wl_socket_get_display_name(struct wl_socket *s)
{
return s->display_name;
}
int wl_socket_get_fd(struct wl_socket *s)
{
return s->fd;
}
struct wl_socket *wl_socket_create()
{
struct wl_socket *s;
int displayno = 0;
int name_size;
/* A reasonable number of maximum default sockets. If
* you need more than this, use the explicit add_socket API. */
const int MAX_DISPLAYNO = 32;
const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
if (!runtime_dir) {
printf("XDG_RUNTIME_DIR not set");
return NULL;
}
s = wl_socket_alloc();
if (s == NULL)
return NULL;
do {
snprintf(s->display_name, sizeof s->display_name, "wayland-%d", displayno);
s->addr.sun_family = AF_LOCAL;
name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s/%s", runtime_dir, s->display_name) + 1;
assert(name_size > 0);
if (name_size > (int)sizeof s->addr.sun_path) {
goto fail;
}
if (wl_socket_lock(s) < 0)
continue;
s->fd = socket(PF_LOCAL, SOCK_STREAM, 0);
int size = SUN_LEN(&s->addr);
int ret = bind(s->fd, (struct sockaddr*)&s->addr, size);
if (ret < 0) {
goto fail;
}
ret = listen(s->fd, 128);
if (ret < 0) {
goto fail;
}
return s;
} while (displayno++ < MAX_DISPLAYNO);
fail:
wl_socket_destroy(s);
return NULL;
}
@@ -0,0 +1,34 @@
// Copyright (C) 2023 David Edmundson <davidedmundson@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* Allocate and create a socket
* It is bound and accepted
*/
struct wl_socket *wl_socket_create();
/**
* Returns the file descriptor for the socket
*/
int wl_socket_get_fd(struct wl_socket *);
/**
* Returns the name of the socket, i.e "wayland-0"
*/
char *wl_socket_get_display_name(struct wl_socket *);
/**
* Cleanup resources and close the FD
*/
void wl_socket_destroy(struct wl_socket *socket);
#ifdef __cplusplus
}
#endif