Cockatrice/servatrice/src/signalhandler.cpp
BruebachL 825aba953a
[Servatrice] Fail loudly when startup initialization fails (#7602)
* [Servatrice] Exit non-zero when server initialization fails

initServer() reported every startup failure through a zero exit code: the
database could not be opened, the server printed nothing of its own, and the
process looked like a clean shutdown. Log the two refusal reasons at critical
level and return 1 so supervisors and the Docker exit status can tell the
difference.

* [Servatrice] Keep console logging independent of the logfile

logMessage() returned before the console echo unless a logfile was open and
server/writelog was true, so --log-to-console produced nothing when
server/logfile was empty or could not be opened. That is how a refused database
connection could end in a silent exit.

Print to the console before the logfile gates, drop the duplicate echo from
flushBuffer(), and read writelog/logfilters once in main instead of calling the
non-thread-safe global QSettings from every logging thread.

* [Servatrice] Uninstall the log handler before tearing down globals

The handler reaches into the global logger and settingsCache. Leaving it
installed while main deletes those objects turns any Qt warning emitted during
QCoreApplication/QFactoryLoader teardown into a use-after-free; with
QT_DEBUG_PLUGINS=1 that warning exists, and Servatrice died with SIGSEGV in
QSettings::value() on the way out.

* [Servatrice] Buffer console logs on the logger thread

Address review feedback on the startup failure change:

- Append console lines to a buffer drained by the logger thread, so a
  stalled stdout reader cannot block game threads.
- Report islServer->errorString() instead of the literal call text.
- Let the constructor reuse reloadConfig() instead of parsing the filter
  list twice.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-31 19:02:49 +01:00

111 lines
2.6 KiB
C++

#include "signalhandler.h"
#include "main.h"
#include "server_logger.h"
#include "settingscache.h"
#include <QSocketNotifier>
#include <QThread>
#include <QtGlobal>
#include <qobjectdefs.h>
#include <qsystemdetection.h>
#ifdef Q_OS_UNIX
#include <cstdio>
#include <execinfo.h>
#include <iostream>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#define SIGSEGV_TRACE_LINES 40
int SignalHandler::sigHupFD[2];
SignalHandler::SignalHandler(QObject *parent) : QObject(parent), snHup(nullptr)
{
#ifdef Q_OS_UNIX
::socketpair(AF_UNIX, SOCK_STREAM, 0, sigHupFD);
snHup = new QSocketNotifier(sigHupFD[1], QSocketNotifier::Read, this);
connect(snHup, SIGNAL(activated(int)), this, SLOT(internalSigHupHandler()));
struct sigaction hup;
hup.sa_handler = SignalHandler::sigHupHandler;
sigemptyset(&hup.sa_mask);
hup.sa_flags = 0;
hup.sa_flags |= SA_RESTART;
sigaction(SIGHUP, &hup, 0);
struct sigaction segv;
segv.sa_handler = SignalHandler::sigSegvHandler;
segv.sa_flags = SA_RESETHAND;
sigemptyset(&segv.sa_mask);
sigaction(SIGSEGV, &segv, 0);
sigaction(SIGABRT, &segv, 0);
signal(SIGPIPE, SIG_IGN);
#endif
}
void SignalHandler::sigHupHandler(int /* sig */)
{
#ifdef Q_OS_UNIX
char a = 1;
ssize_t writeValue = ::write(sigHupFD[0], &a, sizeof(a));
Q_UNUSED(writeValue);
#endif
}
void SignalHandler::internalSigHupHandler()
{
snHup->setEnabled(false);
#ifdef Q_OS_UNIX
char tmp;
ssize_t readValue = ::read(sigHupFD[1], &tmp, sizeof(tmp));
Q_UNUSED(readValue);
std::cerr << "Received SIGHUP" << std::endl;
#endif
logger->logMessage("Received SIGHUP, rotating logs and reloading configuration", this);
logger->rotateLogs();
settingsCache->sync();
logger->reloadConfig(settingsCache->value("server/writelog", 1).toBool(),
settingsCache->value("server/logfilters").toString());
snHup->setEnabled(true);
}
#ifdef Q_OS_UNIX
void SignalHandler::sigSegvHandler(int sig)
{
void *array[SIGSEGV_TRACE_LINES];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, SIGSEGV_TRACE_LINES);
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
if (sig == SIGSEGV) {
logger->logMessage("CRASH: SIGSEGV");
} else if (sig == SIGABRT) {
logger->logMessage("CRASH: SIGABRT");
}
logger->deleteLater();
loggerThread->wait();
delete loggerThread;
raise(sig);
}
#else
void SignalHandler::sigSegvHandler(int /* sig */)
{
}
#endif