#include #include #include #include #include #include #include #include #include #include "CloudConnectController.h" #define log qDebug().noquote() int main(int argc, char *argv[]) { // Block SIGINT and SIGTERM from normal delivery sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigprocmask(SIG_BLOCK, &mask, nullptr); const int sfd = signalfd(-1, &mask, SFD_CLOEXEC); if (sfd == -1) { qCritical("Failed to create signalfd"); return 1; } QCoreApplication app(argc, argv); app.setApplicationName("CloudConnect"); app.setApplicationVersion("1.0"); // notifier runs on the main thread inside the event loop QSocketNotifier notifier(sfd, QSocketNotifier::Read); QObject::connect(¬ifier, &QSocketNotifier::activated, [&](int fd) { struct signalfd_siginfo info{}; // A short read leaves info zeroed; don't quit on a phantom signal. const ssize_t bytes = read(fd, &info, sizeof(info)); if (bytes != sizeof(info)) { qWarning("Short read from signalfd (%zd bytes); ignoring", bytes); return; } if (info.ssi_signo == SIGINT) { log << "Application terminated by SIGINT"; } else if (info.ssi_signo == SIGTERM) { log << "Application terminated by SIGTERM"; } app.quit(); }); QCommandLineParser parser; parser.setApplicationDescription("CloudConnect application"); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption configOption( {"c", "config"}, "Path to the configuration INI file.", "config", QDir(app.applicationDirPath()).filePath("/home/leahi/Public/leahi-realtime-cdt/CloudConnect/config/CloudConnect.ini") ); QCommandLineOption msgHandlingOption( {"m", "msg_handling"}, "Path to the message handling INI file.", "msg_handling", QDir(app.applicationDirPath()).filePath("/home/leahi/Public/leahi-realtime-cdt/CloudConnect/config/LeahiMsgHandling.ini") ); parser.addOption(configOption); parser.addOption(msgHandlingOption); parser.process(app); QThread controllerThread; CloudConnectController ccController(parser.value(configOption), parser.value(msgHandlingOption)); // Bind on the controller thread: QLocalServer's socket engine belongs to the // thread that calls listen(), and accepted sockets are parented under it. QObject::connect(&controllerThread, &QThread::started, &ccController, [&ccController, &app]() { if (!ccController.listenForApp()) { qCritical() << "Failed to bind the app socket; shutting down"; QMetaObject::invokeMethod(&app, [&app]() { app.exit(1); }, Qt::QueuedConnection); return; } // QTcpSocket belongs to the thread that constructs it. // Broker connect is async, so false = config failure, not unreachable. if (!ccController.connectToCloud()) { qCritical() << "Failed to start the MQTT connection; shutting down"; QMetaObject::invokeMethod(&app, [&app]() { app.exit(1); }, Qt::QueuedConnection); return; } // CAN last; frames arriving before MQTT CONNACK are still dropped. if (!ccController.startCan()) { qWarning() << "Failed to start the CAN interface; no frames will be forwarded"; } } ); ccController.initThread(controllerThread); const int rc = app.exec(); // Stop the worker thread before the controller is destroyed at scope exit. controllerThread.quit(); controllerThread.wait(); return rc; }