/* * Copyright (C) 2010,2011 by Jonathan Naylor G4KLX * Copyright (c) 2021,2022 by Geoffrey Merck F4FXL / KC3FRA * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG_DSTARGW #include #endif #include "DStarGatewayDefs.h" #include "DStarGatewayConfig.h" #include "DStarGatewayApp.h" #include "Version.h" #include "IRCDDBMultiClient.h" #include "IRCDDBClient.h" #include "Utils.h" #include "Version.h" #include "GitVersion.h" #include "RepeaterProtocolHandlerFactory.h" #include "Log.h" #include "LogFileTarget.h" #include "LogConsoleTarget.h" #include "APRSGPSDIdFrameProvider.h" #include "APRSFixedIdFrameProvider.h" #include "Daemon.h" #include "APRSISHandlerThread.h" #include "DummyAPRSHandlerThread.h" #include "HostsFilesManager.h" CDStarGatewayApp * CDStarGatewayApp::g_app = nullptr; const std::string BANNER_1 = CStringUtils::string_format("%s Copyright (C) %s\n", FULL_PRODUCT_NAME.c_str(), VENDOR_NAME.c_str()); const std::string BANNER_2 = "DStarGateway comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"; const std::string BANNER_3 = "This is free software, and you are welcome to distribute it under certain conditions that are discussed in the LICENSE file.\n\n"; #ifdef UNIT_TESTS int fakemain(int argc, char *argv[]) #else int main(int argc, char *argv[]) #endif { std::set_terminate(CDStarGatewayApp::terminateHandler); signal(SIGSEGV, CDStarGatewayApp::sigHandlerFatal); signal(SIGILL, CDStarGatewayApp::sigHandlerFatal); signal(SIGFPE, CDStarGatewayApp::sigHandlerFatal); signal(SIGABRT, CDStarGatewayApp::sigHandlerFatal); signal(SIGTERM, CDStarGatewayApp::sigHandlerExit); signal(SIGINT, CDStarGatewayApp::sigHandlerExit); signal(SIGUSR1, CDStarGatewayApp::sigHandlerUSR); setbuf(stdout, NULL); if (2 != argc) { printf("usage: %s path_to_config_file\n", argv[0]); printf(" %s --version\n", argv[0]); return 1; } std::cout << std::endl << BANNER_1 << BANNER_2 << BANNER_3; if ('-' == argv[1][0]) { return 0; } CDStarGatewayConfig * config = new CDStarGatewayConfig(std::string((argv[1]))); if(!config->load()) { CLog::logFatal("Invalid configuration, aborting"); return false; } TDaemon daemon; config->getDaemon(daemon); if (daemon.daemon) { CLog::logInfo("Configured as a daemon, detaching ..."); auto res = CDaemon::daemonise(daemon.pidFile, daemon.user); switch (res) { case DR_PARENT: return 0; case DR_CHILD: break; case DR_PIDFILE_FAILED: case DR_FAILURE: default: CLog::logFatal("Failed to run as daemon"); delete config; CLog::finalise(); return 1; } } // Setup Log TLog logConf; config->getLog(logConf); CLog::finalise(); CLog::getRepeatThreshold() = logConf.repeatThreshold; if(logConf.displayLevel != LOG_NONE && !daemon.daemon) CLog::addTarget(new CLogConsoleTarget(logConf.displayLevel)); if(logConf.fileLevel != LOG_NONE) CLog::addTarget(new CLogFileTarget(logConf.fileLevel, logConf.logDir, logConf.fileRoot, logConf.fileRotate)); //write banner in log file if we are dameon if(daemon.daemon) { CLog::logInfo(BANNER_1); CLog::logInfo(BANNER_2); CLog::logInfo(BANNER_3); } CDStarGatewayApp gateway(config); if (!gateway.init()) { return 1; } gateway.run(); if(daemon.daemon) { CDaemon::finalise(); } return 0; } CDStarGatewayApp::CDStarGatewayApp(CDStarGatewayConfig * config) : m_config(config), m_thread(NULL) { assert(config != nullptr); g_app = this; } CDStarGatewayApp::~CDStarGatewayApp() { } bool CDStarGatewayApp::init() { return createThread(); } void CDStarGatewayApp::run() { m_thread->Run(); m_thread->Wait(); CLog::logInfo("exiting\n"); CLog::finalise(); } bool CDStarGatewayApp::createThread() { CLog::logTrace("Entering CDStarGatewayApp::createThread - Thread ID %s", THREAD_ID_STR(std::this_thread::get_id())); // Log TLog log; m_config->getLog(log); // Paths Tpaths paths; m_config->getPaths(paths); m_thread = new CDStarGatewayThread(log.logDir, paths.dataDir, ""); // Setup the gateway TGateway 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); #ifdef USE_GPSD // Setup GPSD TGPSD gpsdConfig; m_config->getGPSD(gpsdConfig); #endif // Setup APRS TAPRS aprsConfig; m_config->getAPRS(aprsConfig); CAPRSHandler * outgoingAprsWriter = nullptr; CAPRSHandler * incomingAprsWriter = nullptr; if(aprsConfig.enabled && !aprsConfig.password.empty()) { CAPRSISHandlerThread* aprsisthread = new CAPRSISHandlerThread(gatewayConfig.callsign, aprsConfig.password, gatewayConfig.address, aprsConfig.hostname, aprsConfig.port); outgoingAprsWriter = new CAPRSHandler((IAPRSHandlerBackend *)aprsisthread); incomingAprsWriter = new CAPRSHandler((IAPRSHandlerBackend *)new CDummyAPRSHandlerBackend()); if(outgoingAprsWriter->open()) { #ifdef USE_GPSD CAPRSIdFrameProvider * idFrameProvider = aprsConfig.m_positionSource == POSSRC_GPSD ? (CAPRSIdFrameProvider *)new CAPRSGPSDIdFrameProvider(gatewayConfig.callsign, gpsdConfig.m_address, gpsdConfig.m_port) : new CAPRSFixedIdFrameProvider(gatewayConfig.callsign); #else CAPRSIdFrameProvider * idFrameProvider = new CAPRSFixedIdFrameProvider(gatewayConfig.callsign); #endif idFrameProvider->start(); outgoingAprsWriter->setIdFrameProvider(idFrameProvider); m_thread->setAPRSWriters(outgoingAprsWriter, incomingAprsWriter); } else { delete outgoingAprsWriter; outgoingAprsWriter = NULL; } } // Setup access control TAccessControl accessControl; m_config->getAccessControl(accessControl); CCallsignList * whiteList = new CCallsignList(accessControl.whiteList); if(whiteList->load() && whiteList->getCount() > 0U) { m_thread->setWhiteList(whiteList); } else { delete whiteList; } CCallsignList * blackList = new CCallsignList(accessControl.blackList); if(blackList->load() && blackList->getCount() > 0U) { m_thread->setBlackList(blackList); } else { delete blackList; } CCallsignList * restrictList = new CCallsignList(accessControl.restrictList); if(restrictList->load() && restrictList->getCount() > 0U) { m_thread->setRestrictList(restrictList); } else { delete restrictList; } // Drats TDRats drats; m_config->getDRats(drats); // Setup the repeaters bool ddEnabled = false; bool atLeastOneRepeater = false; CRepeaterProtocolHandlerFactory repeaterProtocolFactory; for(unsigned int i = 0U; i < m_config->getRepeaterCount(); i++) { CLog::logTrace("Adding repeaters - CDStarGatewayApp::createThread - Rpt Idx %i - Thread ID %s", i, THREAD_ID_STR(std::this_thread::get_id())); TRepeater rptrConfig; m_config->getRepeater(i, rptrConfig); auto repeaterProtocolHandler = repeaterProtocolFactory.getRepeaterProtocolHandler(rptrConfig.hwType, gatewayConfig, rptrConfig.address, rptrConfig.port); if(repeaterProtocolHandler == nullptr) continue; atLeastOneRepeater = true; m_thread->addRepeater(rptrConfig.callsign, rptrConfig.band, rptrConfig.address, rptrConfig.port, rptrConfig.hwType, rptrConfig.reflector, rptrConfig.reflectorAtStartup, rptrConfig.reflectorReconnect, drats.enabled, rptrConfig.frequency, rptrConfig.offset, rptrConfig.range, rptrConfig.latitude, rptrConfig.longitude, rptrConfig.agl, rptrConfig.description1, rptrConfig.description2, rptrConfig.url, repeaterProtocolHandler, rptrConfig.band1, rptrConfig.band2, rptrConfig.band3); if(outgoingAprsWriter != nullptr) outgoingAprsWriter->setPort(rptrConfig.callsign, rptrConfig.band, rptrConfig.frequency, rptrConfig.offset, rptrConfig.range, rptrConfig.latitude, rptrConfig.longitude, rptrConfig.agl); if(incomingAprsWriter != nullptr) incomingAprsWriter->setPort(rptrConfig.callsign, rptrConfig.band, rptrConfig.frequency, rptrConfig.offset, rptrConfig.range, rptrConfig.latitude, rptrConfig.longitude, rptrConfig.agl); if(!ddEnabled) ddEnabled = rptrConfig.band.length() > 1U; } CLog::logTrace("Repeaters Added - CDStarGatewayApp::createThread - Thread ID %s", THREAD_ID_STR(std::this_thread::get_id())); if(!atLeastOneRepeater) { CLog::logError("Error: no repeaters are enabled or opening network communication to repeater failed"); return false; } m_thread->setDDModeEnabled(ddEnabled); CLog::logInfo("DD Mode enabled: %d", int(ddEnabled)); // Setup ircddb auto ircddbVersionInfo = "linux_" + PRODUCT_NAME + "-" + VERSION; std::vector clients; for(unsigned int i=0; i < m_config->getIrcDDBCount(); i++) { CLog::logTrace("Adding Ircddb - CDStarGatewayApp::createThread - Ircddb Idx %i - Thread ID %s", i, THREAD_ID_STR(std::this_thread::get_id())); TircDDB 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); } CLog::logTrace("Added Ircddb - CDStarGatewayApp::createThread - Ircddb Count %i - Thread ID %s", clients.size(), THREAD_ID_STR(std::this_thread::get_id())); if(clients.size() > 0U) { CIRCDDBMultiClient* multiClient = new CIRCDDBMultiClient(clients); bool res = multiClient->open(); if (!res) { CLog::logError("Cannot initialise the ircDDB protocol handler\n"); return false; } m_thread->setIRC(multiClient); } CLog::logTrace("Setting Up Dextra CDStarGatewayApp::createThread - Thread ID %s", THREAD_ID_STR(std::this_thread::get_id())); // Setup Dextra TDextra dextraConfig; m_config->getDExtra(dextraConfig); CLog::logInfo("DExtra enabled: %d, max. dongles: %u, url: %s", int(dextraConfig.enabled), dextraConfig.maxDongles, dextraConfig.hostfileUrl.c_str()); m_thread->setDExtra(dextraConfig.enabled, dextraConfig.maxDongles); CLog::logTrace("Setting Up DCS CDStarGatewayApp::createThread - Thread ID %s", THREAD_ID_STR(std::this_thread::get_id())); // Setup DCS TDCS dcsConfig; m_config->getDCS(dcsConfig); CLog::logInfo("DCS enabled: %d, url: %s", int(dcsConfig.enabled), dcsConfig.hostfileUrl.c_str()); m_thread->setDCS(dcsConfig.enabled); CLog::logTrace("Setting Up DPlus CDStarGatewayApp::createThread - Thread ID %s", THREAD_ID_STR(std::this_thread::get_id())); // Setup DPlus TDplus dplusConfig; m_config->getDPlus(dplusConfig); CLog::logInfo("D-Plus enabled: %d, max. dongles: %u, login: %s, url: %s", int(dplusConfig.enabled), dplusConfig.maxDongles, dplusConfig.login.c_str(), dplusConfig.hostfileUrl.c_str()); m_thread->setDPlus(dplusConfig.enabled, dplusConfig.maxDongles, dplusConfig.login); CLog::logTrace("Setting Up XLX CDStarGatewayApp::createThread - Thread ID %s", THREAD_ID_STR(std::this_thread::get_id())); // Setup XLX TXLX xlxConfig; m_config->getXLX(xlxConfig); CLog::logInfo("XLX enabled: %d, Hosts file url: %s", int(xlxConfig.enabled), xlxConfig.hostfileUrl.c_str()); m_thread->setXLX(xlxConfig.enabled); // Setup hostsfiles THostsFiles hostsFilesConfig; m_config->getHostsFiles(hostsFilesConfig); CHostsFilesManager::setHostFilesDirectories(hostsFilesConfig.downloadedHostFiles, hostsFilesConfig.customHostsFiles); CHostsFilesManager::setDownloadTimeout(3600 * hostsFilesConfig.downloadTimeout); CHostsFilesManager::setDextra(dextraConfig.enabled, dextraConfig.hostfileUrl); CHostsFilesManager::setDCS (dcsConfig.enabled, dcsConfig.hostfileUrl); CHostsFilesManager::setDPlus (dplusConfig.enabled, dplusConfig.hostfileUrl); CHostsFilesManager::setXLX (xlxConfig.enabled, xlxConfig.hostfileUrl); // Setup Remote TRemote 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); // Get final things ready m_thread->setIcomRepeaterHandler(repeaterProtocolFactory.getIcomProtocolHandler()); m_thread->setHBRepeaterHandler(repeaterProtocolFactory.getHBProtocolHandler()); m_thread->setDummyRepeaterHandler(repeaterProtocolFactory.getDummyProtocolHandler()); m_thread->setInfoEnabled(true); m_thread->setEchoEnabled(true); m_thread->setDTMFEnabled(true); m_thread->setLog(true, log.logIRCDDBTraffic); return true; } void CDStarGatewayApp::sigHandlerExit(int sig) { CLog::logInfo("Caught signal : %s, shutting down gateway", strsignal(sig)); if(g_app != nullptr && g_app->m_thread != nullptr) { g_app->m_thread->kill(); } } void CDStarGatewayApp::sigHandlerFatal(int sig) { CLog::logFatal("Caught signal : %s", strsignal(sig)); fprintf(stderr, "Caught signal : %s\n", strsignal(sig)); #ifdef DEBUG_DSTARGW std::stringstream stackTrace; stackTrace << boost::stacktrace::stacktrace(); CLog::logFatal("Stack Trace : \n%s", stackTrace.str().c_str()); fprintf(stderr, "Stack Trace : \n%s\n", stackTrace.str().c_str()); #endif exit(3); } void CDStarGatewayApp::sigHandlerUSR(int sig) { if(sig == SIGUSR1) { CLog::logInfo("Caught signal : %s, updating host files", strsignal(sig)); CHostsFilesManager::UpdateHostsAsync(); // call and forget } } void CDStarGatewayApp::terminateHandler() { #ifdef DEBUG_DSTARGW std::stringstream stackTrace; stackTrace << boost::stacktrace::stacktrace(); #endif std::exception_ptr eptr; eptr = std::current_exception(); try { if (eptr != nullptr) { std::rethrow_exception(eptr); } else { CLog::logFatal("Unhandled unknown exception occured"); fprintf(stderr, "Unknown ex\n"); } } catch(const std::exception& e) { CLog::logFatal("Unhandled exception occured %s", e.what()); fprintf(stderr, "Unhandled ex %s\n", e.what()); } #ifdef DEBUG_DSTARGW CLog::logFatal("Stack Trace : \n%s", stackTrace.str().c_str()); #endif exit(2); }