// xenomorphicd main — headless daemon entry point. // // QCoreApplication (NOT QGuiApplication): no windows, no QtGui. Claims the // single-instance flock, binds the WebSocket server, writes the discovery // file, and runs the event loop forever. Panes and shells survive the app // quitting; the daemon exits only on SIGTERM/SIGINT (graceful daemonDown) or // a fatal startup failure. stdout/stderr land in the daemon log file because // QProcess::startDetached gives the app no pipe back. #include "DaemonCore.h" #include #include #include #include #include #include #include #include namespace { // Self-pipe so the signal handler touches nothing but sig_atomic_t state and // write(); QCoreApplication is not signal-safe by itself. std::array g_signalPipe = {-1, -1}; extern "C" void handleSignal(int sig) { (void)sig; const char b = 'x'; ssize_t ignored = ::write(g_signalPipe[1], &b, 1); (void)ignored; // best effort; the handler must not do more } } // namespace int main(int argc, char* argv[]) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(QStringLiteral("xenomorphicd")); if (::pipe(g_signalPipe.data()) != 0) { std::cerr << "xenomorphicd: pipe() failed\n"; return 1; } // memset-init: {} aggregate init does not satisfy the // pro-type-member-init check for the __sigaction_u union on macOS. struct sigaction sa; // NOLINT(cppcoreguidelines-pro-type-member-init) std::memset(&sa, 0, sizeof(sa)); // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access) #ifdef __APPLE__ // Darwin stores the handler in a union member. Name it directly: the POSIX // sa_handler name is a macro there, and the union access is what clang-tidy // sees. sa.__sigaction_u.__sa_handler = handleSignal; #else sa.sa_handler = handleSignal; #endif // NOLINTEND(cppcoreguidelines-pro-type-union-access) std::memset(&sa.sa_mask, 0, sizeof(sa.sa_mask)); sa.sa_flags = 0; // no SA_RESTART: let read/connect paths see EINTR ::sigaction(SIGTERM, &sa, nullptr); ::sigaction(SIGINT, &sa, nullptr); DaemonCore core; QString error; if (!core.start(&error)) { // Lands in the daemon log via stderr redirection, and on the console // when run in the foreground. std::cerr << "xenomorphicd: " << error.toUtf8().constData() << '\n'; return 1; } // Signal thread -> event loop bridge. auto* sigNotifier = new QSocketNotifier(g_signalPipe[0], QSocketNotifier::Read, &app); QObject::connect(sigNotifier, &QSocketNotifier::activated, &app, [&core, sigNotifier](int) { sigNotifier->setEnabled(false); core.shutdown(QStringLiteral("shutdown")); QCoreApplication::exit(0); }); return QCoreApplication::exec(); }