graceful shutdown and error handling

- Daemon: async-signal-safe SIGTERM/SIGINT via socketpair
- Tray: survives daemon restart, watches for service appear/disappear
- KRunner: skips matches if daemon unreachable
- Kate: shows error message if daemon not running or session creation fails
This commit is contained in:
Levi Neely 2026-07-06 00:24:11 +02:00
parent db9e7c2830
commit f9e902a2ad
5 changed files with 65 additions and 13 deletions

View File

@ -1,15 +1,22 @@
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDebug>
#include <QSocketNotifier>
#include <csignal>
#include <sys/socket.h>
#include <unistd.h>
#include "session_manager.h"
#include "sessionmanageradaptor.h" // generated by qt6_add_dbus_adaptor
static void signalHandler(int sig)
// Async-signal-safe shutdown via socketpair
static int sigFd[2];
static void signalHandler(int)
{
qDebug() << "Received signal" << sig << "- shutting down";
QCoreApplication::quit();
char c = 1;
write(sigFd[0], &c, 1);
}
int main(int argc, char *argv[])
@ -18,7 +25,20 @@ int main(int argc, char *argv[])
app.setApplicationName("ollie-kde");
app.setOrganizationName("ollie");
// Handle SIGTERM/SIGINT gracefully
// Set up signal handling via socketpair (async-signal-safe)
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sigFd) != 0) {
qCritical() << "Cannot create signal socketpair";
return 1;
}
auto *sigNotifier = new QSocketNotifier(sigFd[1], QSocketNotifier::Read, &app);
QObject::connect(sigNotifier, &QSocketNotifier::activated, &app, [&]() {
char c;
read(sigFd[1], &c, 1);
qDebug() << "Received shutdown signal, exiting gracefully...";
app.quit();
});
signal(SIGTERM, signalHandler);
signal(SIGINT, signalHandler);
@ -49,5 +69,8 @@ int main(int argc, char *argv[])
qDebug() << "ollie-kde daemon started on D-Bus: org.ollie.SessionManager";
return app.exec();
int rc = app.exec();
qDebug() << "ollie-kde daemon exiting";
return rc;
}

View File

@ -265,6 +265,8 @@ void OllieKateView::newSession()
m_chatOffset = 0;
m_sessionLabel->setText("○ " + m_sessionId.left(14) + " [idle]");
m_cwdLabel->setText(cwd);
} else {
m_chatView->setPlainText("Error creating session: " + reply.error().message());
}
}
@ -273,6 +275,10 @@ void OllieKateView::newSession()
void OllieKateView::ensureSession()
{
if (!m_sessionId.isEmpty()) return;
if (!m_iface->isValid()) {
m_chatView->setPlainText("Error: ollie-kde daemon not running.\nStart with: ollie-kde or rc-service --user ollie-kde start");
return;
}
newSession();
}

View File

@ -38,6 +38,11 @@ void OllieRunner::match(KRunner::RunnerContext &context)
return;
}
// Bail if daemon isn't running
if (!m_iface->isValid()) {
return;
}
QString args = query.mid(6).trimmed(); // everything after "ollie "
if (args.isEmpty() || args == QLatin1String("list")) {

View File

@ -1,6 +1,7 @@
#include <QApplication>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusServiceWatcher>
#include <QDebug>
#include "ollie_tray.h"
@ -12,20 +13,36 @@ int main(int argc, char *argv[])
app.setOrganizationName("ollie");
app.setQuitOnLastWindowClosed(false);
// Verify the daemon is reachable
QDBusConnection bus = QDBusConnection::sessionBus();
if (!bus.isConnected()) {
qCritical() << "Cannot connect to D-Bus session bus";
return 1;
}
if (!bus.interface()->isServiceRegistered("org.ollie.SessionManager")) {
qCritical() << "ollie-kde daemon not running (org.ollie.SessionManager not registered)";
return 1;
}
OllieTray tray;
qDebug() << "ollie-tray started";
// Watch for daemon appearing/disappearing
auto *watcher = new QDBusServiceWatcher(
"org.ollie.SessionManager",
bus,
QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration,
&app);
QObject::connect(watcher, &QDBusServiceWatcher::serviceRegistered, [&]() {
qDebug() << "Daemon appeared on D-Bus";
tray.updateStatus();
});
QObject::connect(watcher, &QDBusServiceWatcher::serviceUnregistered, [&]() {
qDebug() << "Daemon disappeared from D-Bus";
tray.updateStatus();
});
if (bus.interface()->isServiceRegistered("org.ollie.SessionManager")) {
qDebug() << "ollie-tray started (daemon running)";
} else {
qDebug() << "ollie-tray started (waiting for daemon)";
}
return app.exec();
}

View File

@ -15,6 +15,8 @@ public:
explicit OllieTray(QObject *parent = nullptr);
~OllieTray() override;
void updateStatus();
private slots:
void onSessionCreated(const QString &session_id);
void onSessionKilled(const QString &session_id);
@ -27,7 +29,6 @@ private slots:
private:
void setupMenu();
void updateStatus();
void updateTooltip();
QStringList callListSessions();
bool callKillSession(const QString &id);