diff --git a/DStarGateway/DStarGatewayApp.cpp b/DStarGateway/DStarGatewayApp.cpp index 8acfe2e..f0c4e3e 100644 --- a/DStarGateway/DStarGatewayApp.cpp +++ b/DStarGateway/DStarGatewayApp.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #ifdef DEBUG_DSTARGW #include #endif @@ -45,6 +47,7 @@ #include "LogConsoleTarget.h" #include "APRSGPSDIdFrameProvider.h" #include "APRSFixedIdFrameProvider.h" +#include "Daemon.h" CDStarGatewayApp * CDStarGatewayApp::g_app = nullptr; @@ -70,16 +73,52 @@ int main(int argc, char *argv[]) return 1; } + printf("\n%s Copyright (C) %s\n", FULL_PRODUCT_NAME.c_str(), VENDOR_NAME.c_str()); + printf("DStarGateway comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); + printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n"); + if ('-' == argv[1][0]) { - printf("\n%s Copyright (C) %s\n", FULL_PRODUCT_NAME.c_str(), VENDOR_NAME.c_str()); - printf("DStarGateway comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); - printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n"); return 0; } - std::string cfgFile(argv[1]); + CDStarGatewayConfig * config = new CDStarGatewayConfig(std::string((argv[1]))); + if(!config->load()) { + CLog::logFatal("Invalid configuration, aborting"); + return false; + } + + TDaemon daemon; + config->getGeneral(daemon); + + if (daemon.daemon) { + CLog::logInfo("Configured as a daemon, detaching ..."); + auto res = CDaemon::daemonize(daemon.pidFile); + + switch (res) + { + case DR_PARENT: + return 0; + case DR_CHILD: + break; + case DR_PIDFILE_FAILED: + break; + case DR_FAILURE: + [[fallthrough]]; + default: + CLog::logFatal("Fork failed, exiting"); + delete config; + return 1; + } + } + + // Setup Log + TLog logConf; + config->getLog(logConf); + CLog::finalise(); + if(logConf.m_displayLevel != LOG_NONE && !daemon.daemon) CLog::addTarget(new CLogConsoleTarget(logConf.m_displayLevel)); + if(logConf.m_fileLevel != LOG_NONE) CLog::addTarget(new CLogFileTarget(logConf.m_fileLevel, logConf.logDir, logConf.m_fileRotate)); - CDStarGatewayApp gateway(cfgFile); + CDStarGatewayApp gateway(config); if (!gateway.init()) { return 1; @@ -87,22 +126,30 @@ int main(int argc, char *argv[]) gateway.run(); + if(daemon.daemon) { + CDaemon::finalize(); + } + return 0; } -CDStarGatewayApp::CDStarGatewayApp(const std::string &configFile) : m_configFile(configFile), m_thread(NULL) +CDStarGatewayApp::CDStarGatewayApp(CDStarGatewayConfig * config) : +m_config(config), +m_thread(NULL) { + assert(config != nullptr); g_app = this; } CDStarGatewayApp::~CDStarGatewayApp() { + delete m_config; + delete m_thread; } bool CDStarGatewayApp::init() { return createThread(); - //2021-12-31 F4FXL This is ugly, if we failed to create the thread we do not clean up ... :( } void CDStarGatewayApp::run() @@ -115,30 +162,18 @@ void CDStarGatewayApp::run() bool CDStarGatewayApp::createThread() { - printf("\n%s Copyright (C) %s\n", FULL_PRODUCT_NAME.c_str(), VENDOR_NAME.c_str()); - printf("DStarGateway comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); - printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n"); - - CDStarGatewayConfig config(m_configFile); - if(!config.load()) { - CLog::logFatal("Invalid configuration, aborting"); - return false; - } - - // Setup Log + // Log TLog log; - config.getLog(log); - CLog::finalise(); - if(log.m_displayLevel != LOG_NONE) CLog::addTarget(new CLogConsoleTarget(log.m_displayLevel)); - if(log.m_fileLevel != LOG_NONE) CLog::addTarget(new CLogFileTarget(log.m_fileLevel, log.logDir, log.m_fileRotate)); + m_config->getLog(log); + // Paths Tpaths paths; - config.getPaths(paths); + m_config->getPaths(paths); m_thread = new CDStarGatewayThread(log.logDir, paths.dataDir, ""); // Setup the gateway TGateway gatewayConfig; - config.getGateway(gatewayConfig); + m_config->getGateway(gatewayConfig); m_thread->setGateway(gatewayConfig.type, gatewayConfig.callsign, gatewayConfig.address); m_thread->setLanguage(gatewayConfig.language); m_thread->setLocation(gatewayConfig.latitude, gatewayConfig.longitude); @@ -146,12 +181,12 @@ bool CDStarGatewayApp::createThread() #ifdef USE_GPSD // Setup GPSD TGPSD gpsdConfig; - config.getGPSD(gpsdConfig); + m_config->getGPSD(gpsdConfig); #endif // Setup APRS TAPRS aprsConfig; - config.getAPRS(aprsConfig); + m_config->getAPRS(aprsConfig); CAPRSHandler * aprsWriter = NULL; if(aprsConfig.enabled && !aprsConfig.password.empty()) { aprsWriter = new CAPRSHandler(aprsConfig.hostname, aprsConfig.port, gatewayConfig.callsign, aprsConfig.password, gatewayConfig.address); @@ -176,9 +211,9 @@ bool CDStarGatewayApp::createThread() bool ddEnabled = false; bool atLeastOneRepeater = false; CRepeaterProtocolHandlerFactory repeaterProtocolFactory; - for(unsigned int i = 0U; i < config.getRepeaterCount(); i++) { + for(unsigned int i = 0U; i < m_config->getRepeaterCount(); i++) { TRepeater rptrConfig; - config.getRepeater(i, rptrConfig); + m_config->getRepeater(i, rptrConfig); auto repeaterProtocolHandler = repeaterProtocolFactory.getRepeaterProtocolHandler(rptrConfig.hwType, gatewayConfig, rptrConfig.address, rptrConfig.port); if(repeaterProtocolHandler == nullptr) continue; @@ -221,9 +256,9 @@ bool CDStarGatewayApp::createThread() // Setup ircddb auto ircddbVersionInfo = "linux_" + PRODUCT_NAME + "-" + VERSION; std::vector clients; - for(unsigned int i=0; i < config.getIrcDDBCount(); i++) { + for(unsigned int i=0; i < m_config->getIrcDDBCount(); i++) { TircDDB ircDDBConfig; - config.getIrcDDB(i, ircDDBConfig); + m_config->getIrcDDB(i, ircDDBConfig); CLog::logInfo("ircDDB Network %d set to %s user: %s, Quadnet %d", i + 1,ircDDBConfig.hostname.c_str(), ircDDBConfig.username.c_str(), ircDDBConfig.isQuadNet); CIRCDDB * ircDDB = new CIRCDDBClient(ircDDBConfig.hostname, 9007U, ircDDBConfig.username, ircDDBConfig.password, ircddbVersionInfo, gatewayConfig.address, ircDDBConfig.isQuadNet); clients.push_back(ircDDB); @@ -240,31 +275,31 @@ bool CDStarGatewayApp::createThread() // Setup Dextra TDextra dextraConfig; - config.getDExtra(dextraConfig); + m_config->getDExtra(dextraConfig); CLog::logInfo("DExtra enabled: %d, max. dongles: %u", int(dextraConfig.enabled), dextraConfig.maxDongles); m_thread->setDExtra(dextraConfig.enabled, dextraConfig.maxDongles); // Setup DCS TDCS dcsConfig; - config.getDCS(dcsConfig); + m_config->getDCS(dcsConfig); CLog::logInfo("DCS enabled: %d", int(dcsConfig.enabled)); m_thread->setDCS(dcsConfig.enabled); // Setup DPlus TDplus dplusConfig; - config.getDPlus(dplusConfig); + m_config->getDPlus(dplusConfig); CLog::logInfo("D-Plus enabled: %d, max. dongles: %u, login: %s", int(dplusConfig.enabled), dplusConfig.maxDongles, dplusConfig.login.c_str()); m_thread->setDPlus(dplusConfig.enabled, dplusConfig.maxDongles, dplusConfig.login); // Setup XLX TXLX xlxConfig; - config.getXLX(xlxConfig); + m_config->getXLX(xlxConfig); CLog::logInfo("XLX enabled: %d, Hosts file url: %s", int(xlxConfig.enabled), xlxConfig.url.c_str()); m_thread->setXLX(xlxConfig.enabled, xlxConfig.enabled ? CXLXHostsFileDownloader::download(xlxConfig.url) : ""); // Setup Remote TRemote remoteConfig; - config.getRemote(remoteConfig); + m_config->getRemote(remoteConfig); CLog::logInfo("Remote enabled: %d, port %u", int(remoteConfig.enabled), remoteConfig.port); m_thread->setRemote(remoteConfig.enabled, remoteConfig.password, remoteConfig.port); @@ -287,6 +322,8 @@ void CDStarGatewayApp::sigHandler(int sig) if(g_app != nullptr && g_app->m_thread != nullptr) { g_app->m_thread->kill(); } + + CDaemon::finalize(); } void CDStarGatewayApp::sigHandlerFatal(int sig) diff --git a/DStarGateway/DStarGatewayApp.h b/DStarGateway/DStarGatewayApp.h index 16df95f..6e5aa21 100644 --- a/DStarGateway/DStarGatewayApp.h +++ b/DStarGateway/DStarGatewayApp.h @@ -20,18 +20,18 @@ #pragma once #include "DStarGatewayThread.h" - +#include "DStarGatewayConfig.h" class CDStarGatewayApp { private: - std::string m_configFile; - CDStarGatewayThread *m_thread; + CDStarGatewayConfig * m_config; + CDStarGatewayThread * m_thread; bool createThread(); static CDStarGatewayApp * g_app; public: - CDStarGatewayApp(const std::string &configFile); + CDStarGatewayApp(CDStarGatewayConfig * config); ~CDStarGatewayApp(); bool init(); diff --git a/DStarGateway/DStarGatewayConfig.cpp b/DStarGateway/DStarGatewayConfig.cpp index a976c1f..5b35d4d 100644 --- a/DStarGateway/DStarGatewayConfig.cpp +++ b/DStarGateway/DStarGatewayConfig.cpp @@ -55,6 +55,7 @@ bool CDStarGatewayConfig::load() #ifdef USE_GPSD ret = loadGPSD(cfg) && ret; #endif + ret = loadDaemon(cfg) && ret; } if(ret) { @@ -69,6 +70,13 @@ bool CDStarGatewayConfig::load() return ret; } +bool CDStarGatewayConfig::loadDaemon(const CConfig & cfg) +{ + bool ret = cfg.getValue("daemon", "daemon", m_general.daemon, false); + ret = cfg.getValue("daemon", "pidfile", m_general.pidFile, 0, 1024, "") && ret; + return ret; +} + bool CDStarGatewayConfig::loadXLX(const CConfig & cfg) { bool ret = cfg.getValue("xlx", "enabled", m_xlx.enabled, true); @@ -438,3 +446,8 @@ void CDStarGatewayConfig::getGPSD(TGPSD & gpsd) const gpsd = m_gpsd; } #endif + +void CDStarGatewayConfig::getGeneral(TDaemon & gen) const +{ + gen = m_general; +} \ No newline at end of file diff --git a/DStarGateway/DStarGatewayConfig.h b/DStarGateway/DStarGatewayConfig.h index b27ca44..d8152f7 100644 --- a/DStarGateway/DStarGatewayConfig.h +++ b/DStarGateway/DStarGatewayConfig.h @@ -25,6 +25,11 @@ #include "Config.h" #include "LogSeverity.h" +typedef struct { + bool daemon; + std::string pidFile; +} TDaemon; + typedef struct { GATEWAY_TYPE type; std::string callsign; @@ -150,6 +155,7 @@ public: #ifdef USE_GPSD void getGPSD(TGPSD & gpsd) const; #endif + void getGeneral(TDaemon & gen) const; private: bool open(CConfig & cfg); @@ -167,6 +173,7 @@ private: #ifdef USE_GPSD bool loadGPSD(const CConfig & cfg); #endif + bool loadDaemon(const CConfig & cfg); std::string m_fileName; TGateway m_gateway; @@ -181,6 +188,8 @@ private: #ifdef USE_GPSD TGPSD m_gpsd; #endif + TDaemon m_general; + std::vector m_repeaters; std::vector m_ircDDB; }; diff --git a/DStarGateway/Makefile b/DStarGateway/Makefile index 73318d1..d5619c5 100644 --- a/DStarGateway/Makefile +++ b/DStarGateway/Makefile @@ -21,6 +21,7 @@ install: dstargateway @cp -fn example.cfg $(CFG_DIR)/dstargateway.cfg @sed -i "s|path=/var/log/dstargateway/|path=$(LOG_DIR)|g" $(CFG_DIR)/dstargateway.cfg @sed -i "s|data=/usr/local/share/dstargateway.d/|data=$(DATA_DIR)|g" $(CFG_DIR)/dstargateway.cfg + @sed -i "s|daemon=false|daemon=true|g" $(CFG_DIR)/dstargateway.cfg ../APRS/APRS.a: ../Common/Common.a: diff --git a/DStarGateway/example.cfg b/DStarGateway/example.cfg index b83f0d6..9ece63e 100644 --- a/DStarGateway/example.cfg +++ b/DStarGateway/example.cfg @@ -2,7 +2,6 @@ # The order of the sections or key/values pairs inside the sections does not matter nor does casing. # Boolean values can be set using true, false, 1 or 0 # Floating point values must use . (point) as decimal separator - [Gateway] type= # repeater, hotspot, dongle. Defaults to repeater callsign= @@ -22,7 +21,7 @@ language= # valid values: english_uk, deutsch, dansk, francais, ita [ircddb_1] enabled=true hostname=ircv4.openquad.net -username= # The ircDDB username default to the value defined for gateway callsign. +username= # The ircDDB username defaults to the value defined for gateway callsign. password= [ircddb_2] @@ -178,3 +177,7 @@ enabled=false port=4242 password=CHANGE_ME # If password is left blank, remote will be disabled regardless of the enabled field +[Daemon] +daemon=false +# pid file is in our case useless when running as a daemon using systemd as systemd handles all the pid stuff for us +pidfile=#/var/run/dstargateway/dstargateway.pid \ No newline at end of file