Merge branch 'release/0.3'

master
Geoffrey Merck 4 years ago
commit ebd8be966a

2
.gitignore vendored

@ -37,9 +37,11 @@ GitVersion.h
# test files # test files
___test.cfg ___test.cfg
Sandbox/*
# Executables # Executables
*.exe *.exe
*.out *.out
*.app *.app
dstargateway dstargateway

@ -0,0 +1,281 @@
/*
* Copyright (c) 2021 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 <iostream>
#include <fstream>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include "Utils.h"
#include "Config.h"
#include "Log.h"
CConfig::CConfig(const std::string filename) :
m_filename(filename)
{
}
CConfig::~CConfig()
{
for(auto it = m_sections.begin(); it != m_sections.end(); it++) {
delete it->second;
}
m_sections.clear();
}
bool CConfig::load()
{
std::ifstream file;
file.open(m_filename, std::ios::in);
if(!file.is_open()) {
CLog::logError("Failed to open configuration file %s", m_filename);
return false;
}
CConfigSection * currentSection = nullptr;
while(!file.eof()) {
std::string line;
std::getline(file, line);
stripComment(line);
if(isConfigSection(line)) {
auto it = m_sections.find(line);
currentSection = it != m_sections.end() ? it->second : nullptr;
if(currentSection == nullptr) {
currentSection = new CConfigSection(line);
m_sections[currentSection->getName()] = currentSection;
}
} else if(isConfigValue(line) && currentSection != nullptr) {
TConfigValue * configValue = readKeyAndValue(line);
if(configValue != nullptr) {
currentSection->getValues()[configValue->m_key] = configValue;
}
}
}
file.close();
return true;
}
void CConfig::stripComment(std::string& s) const
{
boost::trim(s);
if(s.length() > 0 && s[0] == '#') {
//we have a lien consisting only of comment, truncate it and leave
s.resize(0);
return;
}
char * sdup = strdup(s.c_str());
char * uncommentedPart = strtok(sdup, "#");
s = std::string(uncommentedPart != nullptr ? uncommentedPart : "");
boost::trim(s);
free(sdup);// could we use delete sdup here?
}
TConfigValue * CConfig::readKeyAndValue(const std::string s) const
{
TConfigValue* res = nullptr;
char * sdup = strdup(boost::trim_copy(s).c_str());
char * keyPtr = strtok(sdup, "=");
std::string key(keyPtr != nullptr ? keyPtr : "");
boost::trim(key);
if(!key.empty()) {
char * valuePtr = strtok(nullptr, "=");
std::string value(valuePtr != nullptr? valuePtr : "");
res = new TConfigValue;
res->m_key = key;
res->m_value = boost::trim_copy(value);
}
free(sdup);// could we use delete sdup here?
return res;
}
bool CConfig::getValue(const std::string &section, const std::string& key, bool &value, bool defaultValue) const
{
std::string valueTemp;
std::string dafaultValueStr = defaultValue ? "true" : "false";
bool ret = getValue(section, key, valueTemp, dafaultValueStr, {"true", "1", "false", "0"});
if(ret) {
if(isSameNoCase(valueTemp, std::string("true")) || valueTemp == "1")
value = true;
else if(isSameNoCase(valueTemp, std::string("false")) || valueTemp == "0")
value = false;
}
return ret;
}
bool CConfig::getValue(const std::string &section, const std::string& key, int &value, int min, int max, int defaultValue) const
{
TConfigValue * val = lookupValue(section, key);
if(val == nullptr || val->m_value.empty()) {
value = defaultValue;
return true;
}
if(!isDecimalInteger(val->m_value)){
CLog::logError("Configuration error: %s.%s is not a valid number (%s)", section.c_str(), key.c_str(), val->m_value.c_str());
return false;
}
int tmpValue = std::strtol(val->m_value.c_str(), nullptr, 10);
if(tmpValue < min || tmpValue > max) {
CLog::logError("Configuration error: %s.%s is out of range must be between %d and %d, actual %d", section.c_str(), key.c_str(), min, max, tmpValue);
return false;
}
value = tmpValue;
return true;
}
bool CConfig::getValue(const std::string &section, const std::string& key, double &value, double min, double max, double defaultValue) const
{
TConfigValue * val = lookupValue(section, key);
if(val == nullptr || val->m_value.empty()) {
value = defaultValue;
return true;
}
if(!isFloatingPoint(val->m_value)){
CLog::logError("Configuration error: %s.%s is not a valid floating point number (%s)", section.c_str(), key.c_str(), val->m_value.c_str());
return false;
}
double tmpValue = std::strtold(val->m_value.c_str(), nullptr);
if(tmpValue < min || tmpValue > max) {
CLog::logError("Configuration error: %s.%s is out of range must be between %f and %f, actual %f", section.c_str(), key.c_str(), min, max, tmpValue);
return false;
}
value = tmpValue;
return true;
}
bool CConfig::getValue(const std::string &section, const std::string& key, unsigned int &value, unsigned int min, unsigned int max, int defaultValue) const
{
TConfigValue * val = lookupValue(section, key);
if(val == nullptr || val->m_value.empty()) {
value = defaultValue;
return true;
}
if(!isDecimalInteger(val->m_value)){
CLog::logError("Configuration error: %s.%s is not a valid number (%s)", section.c_str(), key.c_str(), val->m_value.c_str());
return false;
}
unsigned int tmpValue = std::strtol(val->m_value.c_str(), nullptr, 10);
if(tmpValue < min || tmpValue > max) {
CLog::logError("Configuration error: %s.%s is out of range must be between %u and %u, actual %u", section.c_str(), key.c_str(), min, max, tmpValue);
return false;
}
value = tmpValue;
return true;
}
bool CConfig::getValue(const std::string &section, const std::string& key, std::string &value, unsigned int minLen, unsigned int maxLen, const std::string defaultValue) const
{
TConfigValue * val = lookupValue(section, key);
std::string valueTemp = val != nullptr ? val->m_value : defaultValue;
if(valueTemp.empty() && minLen == 0U)
valueTemp = defaultValue;
if(valueTemp.length() < minLen || valueTemp.length() > maxLen) {
CLog::logError("Configuration error: %s.%s has an invalid length, length must be between %u and %u, actual %u", section.c_str(), key.c_str(), minLen, maxLen, valueTemp.length());
return false;
}
value = valueTemp;
return true;
}
bool CConfig::getValue(const std::string &section, const std::string& key, unsigned char &value, unsigned char min, unsigned char max,unsigned char defaultValue) const
{
unsigned int tempValue;
bool ret = getValue(section, key, tempValue, (unsigned int)min, (unsigned int)max, (unsigned int) defaultValue);
if(ret) {
value = (unsigned char)tempValue;
}
return ret;
}
bool CConfig::getValue(const std::string &section, const std::string& key, std::string &value, const std::string defaultValue, const std::vector<std::string>& allowedValues) const
{
std::string valueTemp;
if(getValue(section, key, valueTemp, 0U, 2048, defaultValue)) {
for(auto s : allowedValues) {
if(isSameNoCase(s, valueTemp)) {
value = CUtils::ToLower(valueTemp);
return true;
}
}
}
// Build error message
std::stringstream allowedValuesStreamStr;
for(auto s : allowedValues) {
allowedValuesStreamStr << s << ", ";
}
// We want nice looking error message, so get rid of trailing ", "
std::string allowedValuesStr = allowedValuesStreamStr.str();
allowedValuesStr.resize(allowedValuesStr.length() - 2);
CLog::logError("Configuration error: %s.%s has an invalid value, valid values are: %s. Actual: %s", section.c_str(), key.c_str(), allowedValuesStr.c_str(), valueTemp.c_str());
return false;
}
TConfigValue * CConfig::lookupValue(const std::string& sectionName, const std::string& key) const
{
TConfigValue * res = nullptr;
std::string sectionNameTemp = sectionName;
// Make sure we have brackets around section names
if(sectionNameTemp[0] != '[') sectionNameTemp = "[" + sectionNameTemp;
if(sectionNameTemp[sectionNameTemp.length() - 1] != ']') sectionNameTemp.push_back(']');
auto itSection = m_sections.find(sectionNameTemp);
if(itSection != m_sections.end()) {
auto itValue = itSection->second->getValues().find(key);
if(itValue != itSection->second->getValues().end()) {
res = itValue->second;
}
}
return res;
}

@ -0,0 +1,114 @@
/*
* Copyright (c) 2021 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.
*/
#pragma once
#include <string>
#include <unordered_map>
#include <utility>
#include <cctype>
#include <cstring>
#include <cassert>
#include <vector>
#define isConfigSection(s)(!s.empty() && s[0] == '[' && s[s.length() - 1] == ']')
#define isConfigValue(s)(s.find('=') != std::string::npos && s.find('#') > s.find('='))
#define isSameNoCase(a,b)(strcasecmp(a.c_str(), b.c_str()) == 0)
#define isDecimalInteger(s)(!s.empty() && (s[0] == '-' || std::isdigit(s[0])) && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !(std::isdigit(c) || c == '-'); }) == s.end())
#define isFloatingPoint(s)(!s.empty() && (s[0] == '-' || std::isdigit(s[0])) && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !(std::isdigit(c) || c == '-' || c == '.'); }) == s.end())
// https://www.programmingnotes.org/6504/c-how-to-make-map-unordered-map-keys-case-insensitive-using-c/
struct case_insensitive_unordered_map {
struct comp {
bool operator() (const std::string& lhs, const std::string& rhs) const {
// On non Windows OS, use the function "strcasecmp" in #include <strings.h>
return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
}
};
struct hash {
std::size_t operator() (std::string str) const {
for (std::size_t index = 0; index < str.size(); ++index) {
auto ch = static_cast<unsigned char>(str[index]);
str[index] = static_cast<unsigned char>(std::tolower(ch));
}
return std::hash<std::string>{}(str);
}
};
};
typedef struct {
std::string m_key;
std::string m_value;
} TConfigValue;
typedef std::unordered_map<std::string, TConfigValue *, case_insensitive_unordered_map::hash, case_insensitive_unordered_map::comp> CConfigValuesMap;
class CConfigSection {
public:
CConfigSection(const std::string name) :
m_name(name)
{
assert(!name.empty());
}
~CConfigSection()
{
for(auto it = m_values.begin();it != m_values.end();it++) {
delete it->second;
}
m_values.clear();
}
std::string getName() const
{
return m_name;
}
CConfigValuesMap & getValues()
{
return m_values;
}
private:
std::string m_name;
CConfigValuesMap m_values;
};
typedef std::unordered_map<std::string, CConfigSection *, case_insensitive_unordered_map::hash, case_insensitive_unordered_map::comp> CConfigSectionsMap;
class CConfig
{
public:
CConfig(const std::string filename);
~CConfig();
bool load();
bool getValue(const std::string &section, const std::string& key, bool &value, bool defaultValue) const;
bool getValue(const std::string &section, const std::string& key, int &value, int min, int max, int defaultValue) const;
bool getValue(const std::string &section, const std::string& key, double &value, double min, double max, double defaultValue) const;
bool getValue(const std::string &section, const std::string& key, unsigned int &value, unsigned int min, unsigned int max,int defaultValue) const;
bool getValue(const std::string &section, const std::string& key, unsigned char &value, unsigned char min, unsigned char max,unsigned char defaultValue) const;
bool getValue(const std::string &section, const std::string& key, std::string &value, unsigned int minLen, unsigned int maxLen, const std::string defaultValue) const;
bool getValue(const std::string &section, const std::string& key, std::string &value, const std::string defaultValue, const std::vector<std::string>& allowedValues) const;
private:
void stripComment(std::string& s) const;
TConfigValue * readKeyAndValue(const std::string s) const;
TConfigValue * lookupValue(const std::string& section, const std::string& key) const;
std::string m_filename;
CConfigSectionsMap m_sections;
};

@ -98,10 +98,13 @@ bool CDStarGatewayApp::createThread()
return false; return false;
} }
TLog log;
config.getLog(log);
CLog::initialize(log.logDir + "dstargateway.log", LS_INFO, true);
Tpaths paths; Tpaths paths;
config.getPaths(paths); config.getPaths(paths);
CLog::initialize(paths.logDir + "dstargateway.log", LS_INFO, true); m_thread = new CDStarGatewayThread(log.logDir, paths.dataDir, "");
m_thread = new CDStarGatewayThread(paths.logDir, paths.dataDir, "");
// Setup the gateway // Setup the gateway
TGateway gatewayConfig; TGateway gatewayConfig;

@ -35,146 +35,150 @@ CDStarGatewayConfig::CDStarGatewayConfig(const std::string &pathname)
bool CDStarGatewayConfig::load() bool CDStarGatewayConfig::load()
{ {
CLog::logInfo("Loading configuration from %s", m_fileName.c_str()); bool ret = false;
Config cfg; CLog::logInfo("Loading Configuration from %s", m_fileName.c_str());
if(open(cfg) CConfig cfg(m_fileName);
&& loadGateway(cfg)
&& loadIrcDDB(cfg)
&& loadRepeaters(cfg)
&& loadPaths(cfg)
&& loadAPRS(cfg)
&& loadDextra(cfg)
&& loadDCS(cfg)
&& loadDPlus(cfg)
&& loadRemote(cfg)
&& loadXLX(cfg)) {
ret = open(cfg);
if(ret) {
ret = loadGateway(cfg) && ret;
ret = loadIrcDDB(cfg) && ret;
ret = loadRepeaters(cfg) && ret;
ret = loadPaths(cfg) && ret;
ret = loadLog(cfg) && ret;
ret = loadAPRS(cfg) && ret;
ret = loadDextra(cfg) && ret;
ret = loadDCS(cfg) && ret;
ret = loadDPlus(cfg) && ret;
ret = loadRemote(cfg) && ret;
ret = loadXLX(cfg) && ret;
}
if(ret) {
//properly size values //properly size values
m_gateway.callsign.resize(LONG_CALLSIGN_LENGTH - 1U, ' '); m_gateway.callsign.resize(LONG_CALLSIGN_LENGTH - 1U, ' ');
m_gateway.callsign.push_back('G'); m_gateway.callsign.push_back('G');
}
return true; else {
CLog::logError("Loading Configuration from %s failed", m_fileName.c_str());
} }
CLog::logError("Loading configuration from %s failed", m_fileName.c_str()); return ret;
return false;
} }
bool CDStarGatewayConfig::loadXLX(const Config & cfg) bool CDStarGatewayConfig::loadXLX(const CConfig & cfg)
{ {
bool ret = get_value(cfg, "xlx.enabled", m_xlx.enabled, true); bool ret = cfg.getValue("xlx", "enabled", m_xlx.enabled, true);
ret = get_value(cfg, "xlx.hostfileUrl", m_xlx.url, 0, 1024, "", false) && ret; ret = cfg.getValue("xlx", "hostfileUrl", m_xlx.url, 0, 1024, "") && ret;
m_xlx.enabled = m_xlx.enabled && !m_xlx.url.empty();
return ret; return ret;
} }
bool CDStarGatewayConfig::loadRemote(const Config & cfg) bool CDStarGatewayConfig::loadRemote(const CConfig & cfg)
{ {
bool ret = get_value(cfg, "remote.enabled", m_remote.enabled, false); bool ret = cfg.getValue("remote", "enabled", m_remote.enabled, false);
ret = get_value(cfg, "remote.port", m_remote.port, 1U, 65535U, 4242U) && ret; ret = cfg.getValue("remote", "port", m_remote.port, 1U, 65535U, 4242U) && ret;
ret = get_value(cfg, "remote.password", m_remote.password, 0, 1024, "", true) && ret; ret = cfg.getValue("remote", "password", m_remote.password, 0, 1024, "") && ret;
m_remote.enabled = m_remote.enabled && !m_remote.password.empty(); m_remote.enabled = m_remote.enabled && !m_remote.password.empty();
return ret; return ret;
} }
bool CDStarGatewayConfig::loadDextra(const Config & cfg) bool CDStarGatewayConfig::loadDextra(const CConfig & cfg)
{ {
bool ret = get_value(cfg, "dextra.enabled", m_dextra.enabled, true); bool ret = cfg.getValue("dextra", "enabled", m_dextra.enabled, true);
ret = get_value(cfg, "dextra.maxDongles", m_dextra.maxDongles, 1U, 5U, 5U) && ret; ret = cfg.getValue("dextra", "maxDongles", m_dextra.maxDongles, 1U, 5U, 5U) && ret;
return ret; return ret;
} }
bool CDStarGatewayConfig::loadDPlus(const Config & cfg) bool CDStarGatewayConfig::loadDPlus(const CConfig & cfg)
{ {
bool ret = get_value(cfg, "dplus.enabled", m_dplus.enabled, true); bool ret = cfg.getValue("dplus", "enabled", m_dplus.enabled, true);
ret = get_value(cfg, "dplus.maxDongles", m_dplus.maxDongles, 1U, 5U, 5U) && ret; ret = cfg.getValue("dplus", "maxDongles", m_dplus.maxDongles, 1U, 5U, 5U) && ret;
ret = get_value(cfg, "dplus.login", m_dplus.login, 0, LONG_CALLSIGN_LENGTH, m_gateway.callsign, true) && ret; ret = cfg.getValue("dplus", "login", m_dplus.login, 0, LONG_CALLSIGN_LENGTH, m_gateway.callsign) && ret;
m_dplus.enabled = m_dplus.enabled && !m_dplus.login.empty();
m_dplus.login = CUtils::ToUpper(m_dplus.login); m_dplus.login = CUtils::ToUpper(m_dplus.login);
return ret; return ret;
} }
bool CDStarGatewayConfig::loadDCS(const Config & cfg) bool CDStarGatewayConfig::loadDCS(const CConfig & cfg)
{ {
bool ret = get_value(cfg, "dcs.enabled", m_dcs.enabled, true); bool ret = cfg.getValue("dcs", "enabled", m_dcs.enabled, true);
return ret; return ret;
} }
bool CDStarGatewayConfig::loadAPRS(const Config & cfg) bool CDStarGatewayConfig::loadAPRS(const CConfig & cfg)
{ {
bool ret = get_value(cfg, "aprs.enabled", m_aprs.enabled, false); bool ret = cfg.getValue("aprs", "enabled", m_aprs.enabled, false);
ret = get_value(cfg, "aprs.port", m_aprs.port, 1U, 65535U, 14580U) && ret; ret = cfg.getValue("aprs", "port", m_aprs.port, 1U, 65535U, 14580U) && ret;
ret = get_value(cfg, "aprs.hostname", m_aprs.hostname, 0, 1024, "rotate.aprs2.net", true) && ret; ret = cfg.getValue("aprs", "hostname", m_aprs.hostname, 0, 1024, "rotate.aprs2.net") && ret;
ret = get_value(cfg, "aprs.password", m_aprs.password, 0U, 30U, "", true) && ret; ret = cfg.getValue("aprs", "password", m_aprs.password, 0U, 30U, "") && ret;
m_aprs.enabled = m_aprs.enabled && !m_aprs.password.empty();
return ret; return ret;
} }
bool CDStarGatewayConfig::loadPaths(const Config & cfg) bool CDStarGatewayConfig::loadLog(const CConfig & cfg)
{ {
get_value(cfg, "paths.log", m_paths.logDir, 0, 2048, "/var/log/dstargateway/", true); bool ret =cfg.getValue("log", "path", m_log.logDir, 0, 2048, "/var/log/dstargateway/");
get_value(cfg, "paths.data", m_paths.dataDir, 0, 2048, "/var/log/dstargateway/", true);
if(m_paths.logDir[m_paths.logDir.length() - 1] != '/') { if(ret && m_log.logDir[m_log.logDir.length() - 1] != '/') {
m_paths.logDir.push_back('/'); m_log.logDir.push_back('/');
}
if(m_paths.dataDir[m_paths.dataDir.length() - 1] != '/') {
m_paths.dataDir.push_back('/');
} }
//TODO 20211226 check if directory are accessible //TODO 20211226 check if directory are accessible
return true; return ret;
} }
bool CDStarGatewayConfig::loadRepeaters(const Config & cfg) bool CDStarGatewayConfig::loadPaths(const CConfig & cfg)
{ {
cfg.lookup("repeaters"); bool ret = cfg.getValue("paths", "data", m_paths.dataDir, 0, 2048, "/usr/local/share/dstargateway.d/");
// repeater parameters
for (int i=0; i<cfg.lookup("repeaters").getLength(); i++) {
std::stringstream key;
bool isOk = false;
TRepeater * repeater = new TRepeater;
key << "repeaters.[" << i << "]";
if (get_value(cfg, key.str() + ".callsign", repeater->callsign, 0, 7, m_gateway.callsign, true)) { if(ret && m_paths.dataDir[m_paths.dataDir.length() - 1] != '/') {
CUtils::ToUpper(repeater->callsign); m_paths.dataDir.push_back('/');
} }
if(get_value(cfg, key.str() + ".band", repeater->band, 1, 1, "A")) { //TODO 20211226 check if directory are accessible
CUtils::ToUpper(repeater->band);
}
if (get_value(cfg, key.str() + ".address", repeater->address, 0, 15, "127.0.0.1", true)) { return ret;
// ???
} }
if(get_value(cfg, key.str() + ".port", repeater->port, 1U, 65535U, 20011U)) { bool CDStarGatewayConfig::loadRepeaters(const CConfig & cfg)
// ??? {
} for(unsigned int i = 0; i < 4; i++) {
std::string section = ((std::stringstream() << "repeater_" << (i + 1))).str();
bool repeaterEnabled;
bool ret = cfg.getValue(section, "enabled", repeaterEnabled, false);
if(!ret || !repeaterEnabled)
continue;
TRepeater * repeater = new TRepeater;
ret = cfg.getValue(section, "band", repeater->band, 1, 2, "B") && ret;
ret = cfg.getValue(section, "callsign", repeater->callsign, 0, LONG_CALLSIGN_LENGTH - 1, m_gateway.callsign);
ret = cfg.getValue(section, "address", repeater->address, 0, 15, "127.0.0.1") && ret;
ret = cfg.getValue(section, "port", repeater->port, 1U, 65535U, 20011U) && ret;
std::string hwType; std::string hwType;
if(get_value(cfg, key.str() + ".type", hwType, 1, 5, "", false, {"hb", "icom"} )) { ret = cfg.getValue(section, "type", hwType, "", {"hb", "icom"}) && ret;
if(ret) {
if(hwType == "hb") repeater->hwType = HW_HOMEBREW; if(hwType == "hb") repeater->hwType = HW_HOMEBREW;
else if(hwType == "icom") repeater->hwType = HW_ICOM; else if(hwType == "icom") repeater->hwType = HW_ICOM;
} }
if (get_value(cfg, key.str() + ".reflector", repeater->reflector, 0, LONG_CALLSIGN_LENGTH, "", true)) { ret = cfg.getValue(section, "reflector", repeater->reflector, 0, LONG_CALLSIGN_LENGTH, "") && ret;
// ??? ret = cfg.getValue(section, "reflectorAtStartup", repeater->reflectorAtStartup, !repeater->reflector.empty()) && ret;
}
if (get_value(cfg, key.str() + ".reflectorAtStartup", repeater->reflectorAtStartup, true)) {
// ???
}
std::string reconnect; std::string reconnect;
if (get_value(cfg, key.str() + ".reflectorReconnect", reconnect, 0, 5, "never", true, ret = cfg.getValue(section, "reflectorReconnect", reconnect, "never", {"never", "fixed", "5", "10", "15", "20", "25", "30", "60", "90", "120", "180"}) && ret;
{"never", "fixed", "5", "10", "15", "20", "25", "30", "60", "90", "120", "180"})) { if(ret) {
if(reconnect == "never") repeater->reflectorReconnect = RECONNECT_NEVER; if(reconnect == "never") repeater->reflectorReconnect = RECONNECT_NEVER;
else if(reconnect == "5") repeater->reflectorReconnect = RECONNECT_5MINS; else if(reconnect == "5") repeater->reflectorReconnect = RECONNECT_5MINS;
else if(reconnect == "10") repeater->reflectorReconnect = RECONNECT_10MINS; else if(reconnect == "10") repeater->reflectorReconnect = RECONNECT_10MINS;
@ -189,145 +193,85 @@ bool CDStarGatewayConfig::loadRepeaters(const Config & cfg)
else if(reconnect == "fixed") repeater->reflectorReconnect = RECONNECT_FIXED; else if(reconnect == "fixed") repeater->reflectorReconnect = RECONNECT_FIXED;
} }
if(get_value(cfg, key.str() + ".frequency", repeater->frequency, 0.0, 1500.0, 434.0)) { ret = cfg.getValue(section, "frequency", repeater->frequency, 0.1, 1500.0, 434.0) && ret;
// ??? ret = cfg.getValue(section, "offset", repeater->offset, -50.0, 50.0, 0.0) && ret;
} ret = cfg.getValue(section, "rangeKm", repeater->range, 0.0, 3000.0, 0.0) && ret;
ret = cfg.getValue(section, "latitude", repeater->latitude, -90.0, 90.0, m_gateway.latitude) && ret;
if(get_value(cfg, key.str() + ".offset", repeater->offset, -50.0, 50.0, 0.0)) { ret = cfg.getValue(section, "longitude", repeater->longitude, -180.0, 180.0, m_gateway.longitude) && ret;
// ??? ret = cfg.getValue(section, "agl", repeater->agl, 0, 1000.0, 0.0) && ret;
} ret = cfg.getValue(section, "description1", m_gateway.description1, 0, 1024, "") && ret;
ret = cfg.getValue(section, "description2", m_gateway.description2, 0, 1024, "") && ret;
if(get_value(cfg, key.str() + ".rangeKm", repeater->range, 0.0, 3000.0, 0.0)) { ret = cfg.getValue(section, "url", m_gateway.url, 0, 1024, "") && ret;;
// ??? ret = cfg.getValue(section, "band1", repeater->band1, 0, 255, 0) && ret;
} ret = cfg.getValue(section, "band2", repeater->band2, 0, 255, 0) && ret;
ret = cfg.getValue(section, "band3", repeater->band3, 0, 255, 0) && ret;
if(get_value(cfg, key.str() + ".latitude", repeater->latitude, -90, 90.0, m_gateway.latitude)) {
// ???
}
if(get_value(cfg, key.str() + ".longitude", repeater->longitude, -180, 180.0, m_gateway.longitude)) {
// ???
}
if(get_value(cfg, key.str() + ".agl", repeater->agl, 0, 1000.0, 0.0)) {
// ???
}
if(get_value(cfg, key.str() + ".description1", m_gateway.description1, 0, 1024, "")) {
// ???
}
if(get_value(cfg, key.str() + ".description2", m_gateway.description2, 0, 1024, "")) {
// ???
}
if(get_value(cfg, key.str() + ".url", m_gateway.url, 0, 1024, "")) {
// ???
}
int band; if(ret) {
if(get_value(cfg, key.str() + ".band1", band, 0, 255, 0)) { m_repeaters.push_back(repeater);
repeater->band1 = (unsigned char)band;
}
if(get_value(cfg, key.str() + ".band2", band, 0, 255, 0)) {
repeater->band2 = (unsigned char)band;
}
if(get_value(cfg, key.str() + ".band3", band, 0, 255, 0)) {
repeater->band3 = (unsigned char)band;
}
// We have read a complete repeater record, validate it
if(repeater->callsign.length() > 0) {
isOk = true;
} }
else { else {
CLog::logError("Repeater %d has an empty callsign", i); delete repeater;
} }
if (isOk && !isalpha(repeater->band[0])) {
isOk = false;
CLog::logError("Repeater %s band is not a letter", i);
} }
if (isOk && repeater->address.length() == 0) { if(m_repeaters.size() == 0U) {
isOk = false; CLog::logError("Configuration error: no repeaters configured !");
} return false;
if(!isOk) {
delete repeater;
} else {
m_repeaters.push_back(repeater);
}
} }
return m_repeaters.size() > 0; return true;
} }
bool CDStarGatewayConfig::loadIrcDDB(const Config & cfg) bool CDStarGatewayConfig::loadIrcDDB(const CConfig & cfg)
{ {
//ircDDB Networks bool ret = true;
for(int i = 0; i < cfg.lookup("ircddb").getLength(); i++) { for(unsigned int i = 0; i < 4; i++) {
TircDDB * ircddb = new TircDDB(); std::string section = ((std::stringstream() << "ircddb_" << (i + 1))).str();
std::stringstream key; bool ircEnabled;
key << "ircddb.[" << i << "].hostname";
if(! get_value(cfg, key.str(), ircddb->hostname, 5, 30, "") || ircddb->hostname == "") {//do not allow hostname to be empty
delete ircddb;
continue;
}
key.str("");key.clear(); ret = cfg.getValue(section, "enabled", ircEnabled, false) && ret;
key << "ircddb.[" << i << "].username"; if(!ircEnabled)
if (! get_value(cfg, key.str(), ircddb->username, 3, 8, m_gateway.callsign)) {//default user name to callsign
delete ircddb;
continue; continue;
}
CUtils::ToUpper(ircddb->username);
key.str("");key.clear(); TircDDB * ircddb = new TircDDB;
key << "ircddb.[" << i << "].password"; ret = cfg.getValue(section, "hostname", ircddb->hostname, 0, 1024, "ircv4.openquad.net") && ret;
if(!get_value(cfg, key.str(), ircddb->password, 0, 30, "")) { ret = cfg.getValue(section, "username", ircddb->username, 0, LONG_CALLSIGN_LENGTH - 1, m_gateway.callsign) && ret;
delete ircddb; ret = cfg.getValue(section, "password", ircddb->password, 0, 1024, "") && ret;
continue;
}
ircddb->isQuadNet = ircddb->hostname.find("openquad.net") != std::string::npos; if(ret) {
this->m_ircDDB.push_back(ircddb); m_ircDDB.push_back(ircddb);
}
else {
delete ircddb;
} }
if(this->m_ircDDB.size() == 0) {//no ircddb network specified? Default to openquad!
TircDDB * ircddb = new TircDDB();
ircddb->hostname = "ircv4.openquad.net";
ircddb->password = "";
ircddb->username = m_gateway.callsign;
ircddb->isQuadNet = true;
this->m_ircDDB.push_back(ircddb);
CLog::logInfo("No ircDDB networks configured, defaulting to IRCDDB: host=%s user=%s", ircddb->hostname.c_str(), ircddb->username.c_str());
} }
return true; return ret;
} }
bool CDStarGatewayConfig::loadGateway(const Config & cfg) bool CDStarGatewayConfig::loadGateway(const CConfig & cfg)
{ {
bool ret = get_value(cfg, "gateway.callsign", m_gateway.callsign, 3, 8, ""); bool ret = cfg.getValue("gateway", "callsign", m_gateway.callsign, 3, 8, "");
get_value(cfg, "gateway.address", m_gateway.address, 0, 20, "0.0.0.0", true) && ret; ret = cfg.getValue("gateway", "address", m_gateway.address, 0, 20, "0.0.0.0") && ret;
get_value(cfg, "gateway.hbAddress", m_gateway.hbAddress, 0, 20, "127.0.0.1", true) && ret; ret = cfg.getValue("gateway", "hbAddress", m_gateway.hbAddress, 0, 20, "127.0.0.1") && ret;
get_value(cfg, "gateway.hbPort", m_gateway.hbPort, 1U, 65535U, 20010U) && ret; ret = cfg.getValue("gateway", "hbPort", m_gateway.hbPort, 1U, 65535U, 20010U) && ret;
get_value(cfg, "gateway.icomAddress", m_gateway.icomAddress, 0, 20, "127.0.0.1", true) && ret; ret = cfg.getValue("gateway", "icomAddress", m_gateway.icomAddress, 0, 20, "127.0.0.1") && ret;
get_value(cfg, "gateway.icomPort", m_gateway.icomPort, 1U, 65535U, 20000U) && ret; ret = cfg.getValue("gateway", "icomPort", m_gateway.icomPort, 1U, 65535U, 20000U) && ret;
get_value(cfg, "gateway.latitude", m_gateway.latitude, -90.0, 90.0, 0.0) && ret; ret = cfg.getValue("gateway", "latitude", m_gateway.latitude, -90.0, 90.0, 0.0) && ret;
get_value(cfg, "gateway.longitude", m_gateway.longitude, -180.0, 180.0, 0.0) && ret; ret = cfg.getValue("gateway", "longitude", m_gateway.longitude, -180.0, 180.0, 0.0) && ret;
get_value(cfg, "gateway.description1", m_gateway.description1, 0, 1024, "") && ret; ret = cfg.getValue("gateway", "description1", m_gateway.description1, 0, 1024, "") && ret;
get_value(cfg, "gateway.description2", m_gateway.description2, 0, 1024, "") && ret; ret = cfg.getValue("gateway", "description2", m_gateway.description2, 0, 1024, "") && ret;
get_value(cfg, "gateway.url", m_gateway.url, 0, 1024, "") && ret; ret = cfg.getValue("gateway", "url", m_gateway.url, 0, 1024, "") && ret;
std::string type; std::string type;
get_value(cfg, "gateway.type", type, 0, 8, "repeater", true, {"repeater", "hotspot"}) && ret; ret = cfg.getValue("gateway", "type", type, "repeater", {"repeater", "hotspot"}) && ret;
if(type == "repeater") m_gateway.type = GT_REPEATER; if(type == "repeater") m_gateway.type = GT_REPEATER;
if(type == "hotspot") m_gateway.type = GT_HOTSPOT; else if(type == "hotspot") m_gateway.type = GT_HOTSPOT;
std::string lang; std::string lang;
get_value(cfg, "gateway.language", lang, 0, 30, "english_uk", true, {"english_uk", "deutsch", "dansk", "francais", "italiano", "polski", "english_us", "espanol", "svenska", "nederlands_nl", "nederlands_be", "norsk", "portugues"}); ret = cfg.getValue("gateway", "language", lang, "english_uk",
{"english_uk", "deutsch", "dansk", "francais", "italiano", "polski",
"english_us", "espanol", "svenska", "nederlands_nl", "nederlands_be", "norsk", "portugues"}) && ret;;
if(lang == "english_uk") m_gateway.language = TL_ENGLISH_UK; if(lang == "english_uk") m_gateway.language = TL_ENGLISH_UK;
else if(lang == "deutsch") m_gateway.language = TL_DEUTSCH; else if(lang == "deutsch") m_gateway.language = TL_DEUTSCH;
else if(lang == "dansk") m_gateway.language = TL_DANSK; else if(lang == "dansk") m_gateway.language = TL_DANSK;
@ -347,28 +291,18 @@ bool CDStarGatewayConfig::loadGateway(const Config & cfg)
CUtils::clean(m_gateway.description2, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;"); CUtils::clean(m_gateway.description2, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;");
CUtils::clean(m_gateway.url, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;"); CUtils::clean(m_gateway.url, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;");
return true; return ret;
} }
bool CDStarGatewayConfig::open(Config & cfg) bool CDStarGatewayConfig::open(CConfig & cfg)
{ {
if (m_fileName.size() < 1) {
CLog::logError("Configuration filename too short!\n");
return false;
}
try { try {
cfg.readFile(m_fileName.c_str()); return cfg.load();
} }
catch(const FileIOException &fioex) { catch(...) {
CLog::logError("Can't read %s\n", m_fileName.c_str()); CLog::logError("Can't read %s\n", m_fileName.c_str());
return false; return false;
} }
catch(const ParseException &pex) {
CLog::logError("Parse error at %s:%d - %s\n", pex.getFile(), pex.getLine(), pex.getError());
return false;
}
return true; return true;
} }
@ -385,93 +319,6 @@ CDStarGatewayConfig::~CDStarGatewayConfig()
} }
} }
bool CDStarGatewayConfig::get_value(const Config &cfg, const std::string &path, unsigned int &value, unsigned int min, unsigned int max, unsigned int default_value)
{
value = default_value;
int valueTmp;
if(get_value(cfg, path, valueTmp, (int)min, (int)max, (int)default_value)
&& valueTmp >= 0) {
value = valueTmp;
}
return true;
}
bool CDStarGatewayConfig::get_value(const Config &cfg, const std::string &path, int &value, int min, int max, int default_value)
{
if (cfg.lookupValue(path, value)) {
if (value < min || value > max)
value = default_value;
} else
value = default_value;
return true;
}
bool CDStarGatewayConfig::get_value(const Config &cfg, const std::string &path, double &value, double min, double max, double default_value)
{
if (cfg.lookupValue(path, value)) {
if (value < min || value > max)
value = default_value;
} else
value = default_value;
return true;
}
bool CDStarGatewayConfig::get_value(const Config &cfg, const std::string &path, bool &value, bool default_value)
{
if (! cfg.lookupValue(path, value))
value = default_value;
return true;
}
bool CDStarGatewayConfig::get_value(const Config &cfg, const std::string &path, std::string &value, int min, int max, const std::string &default_value)
{
return get_value(cfg, path, value, min, max, default_value, false);
}
bool CDStarGatewayConfig::get_value(const Config &cfg, const std::string &path, std::string &value, int min, int max, const std::string &default_value, bool emptyToDefault)
{
if (cfg.lookupValue(path, value)) {
int l = value.length();
if (l<min || l>max) {
CLog::logWarning("%s has an invalid length, must be between %d and %d, actual %d", path.c_str(), min, max, l);
return false;
}
} else
value = default_value;
if(emptyToDefault && value.length() == 0) {
value = default_value;
}
return true;
}
bool CDStarGatewayConfig::get_value(const Config &cfg, const std::string &path, std::string &value, int min, int max, const std::string &default_value, bool emptyToDefault, const std::vector<std::string>& allowedValues)
{
bool ret = get_value(cfg, path, value, min, max, default_value, emptyToDefault);
if(ret) {
for(std::string s : allowedValues) {
if(s == value) {
ret = true;
break;
}
}
}
if(!ret) {
std::stringstream message;
message << path << "=" << value << " has an invalid value. Valid values are : ";
for(std::string s : allowedValues) {
message << s << ", ";
}
CLog::logWarning(message.str());
}
return ret;
}
void CDStarGatewayConfig::getGateway(TGateway & gateway) const void CDStarGatewayConfig::getGateway(TGateway & gateway) const
{ {
gateway = m_gateway; gateway = m_gateway;
@ -497,6 +344,11 @@ void CDStarGatewayConfig::getRepeater(unsigned int index, TRepeater & repeater)
repeater = *(m_repeaters[index]); repeater = *(m_repeaters[index]);
} }
void CDStarGatewayConfig::getLog(TLog & log) const
{
log = m_log;
}
void CDStarGatewayConfig::getPaths(Tpaths & paths) const void CDStarGatewayConfig::getPaths(Tpaths & paths) const
{ {
paths = m_paths; paths = m_paths;

@ -20,10 +20,9 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <libconfig.h++>
#include "Defs.h"
using namespace libconfig; #include "Defs.h"
#include "Config.h"
typedef struct { typedef struct {
GATEWAY_TYPE type; GATEWAY_TYPE type;
@ -62,9 +61,9 @@ typedef struct {
std::string description1; std::string description1;
std::string description2; std::string description2;
std::string url; std::string url;
char band1; unsigned char band1;
char band2; unsigned char band2;
char band3; unsigned char band3;
} TRepeater; } TRepeater;
typedef struct { typedef struct {
@ -75,10 +74,13 @@ typedef struct {
} TircDDB; } TircDDB;
typedef struct { typedef struct {
std::string logDir;
std::string dataDir; std::string dataDir;
} Tpaths; } Tpaths;
typedef struct {
std::string logDir;
} TLog;
typedef struct { typedef struct {
bool enabled; bool enabled;
std::string hostname; std::string hostname;
@ -113,6 +115,7 @@ typedef struct {
std::string password; std::string password;
} TRemote; } TRemote;
class CDStarGatewayConfig { class CDStarGatewayConfig {
public: public:
CDStarGatewayConfig(const std::string &pathname); CDStarGatewayConfig(const std::string &pathname);
@ -124,6 +127,7 @@ public:
unsigned int getIrcDDBCount() const; unsigned int getIrcDDBCount() const;
void getRepeater(unsigned int repeaterIndex, TRepeater & repeater) const; void getRepeater(unsigned int repeaterIndex, TRepeater & repeater) const;
unsigned int getRepeaterCount() const; unsigned int getRepeaterCount() const;
void getLog(TLog& log) const;
void getPaths(Tpaths & paths) const; void getPaths(Tpaths & paths) const;
void getAPRS(TAPRS & aprs) const; void getAPRS(TAPRS & aprs) const;
void getDExtra(TDextra & dextra) const; void getDExtra(TDextra & dextra) const;
@ -133,24 +137,18 @@ public:
void getXLX(TXLX & xlx) const; void getXLX(TXLX & xlx) const;
private: private:
bool open(Config & cfg); bool open(CConfig & cfg);
bool loadGateway(const Config & cfg); bool loadGateway(const CConfig & cfg);
bool loadIrcDDB(const Config & cfg); bool loadIrcDDB(const CConfig & cfg);
bool loadRepeaters(const Config & cfg); bool loadRepeaters(const CConfig & cfg);
bool loadPaths(const Config & cfg); bool loadLog(const CConfig & cfg);
bool loadAPRS(const Config & cfg); bool loadPaths(const CConfig & cfg);
bool loadDextra(const Config & cfg); bool loadAPRS(const CConfig & cfg);
bool loadDPlus(const Config & cfg); bool loadDextra(const CConfig & cfg);
bool loadDCS(const Config & cfg); bool loadDPlus(const CConfig & cfg);
bool loadRemote(const Config & cfg); bool loadDCS(const CConfig & cfg);
bool loadXLX(const Config & cfg); bool loadRemote(const CConfig & cfg);
bool get_value(const Config &cfg, const std::string &path, unsigned int &value, unsigned int min, unsigned int max, unsigned int default_value); bool loadXLX(const CConfig & cfg);
bool get_value(const Config &cfg, const std::string &path, int &value, int min, int max, int default_value);
bool get_value(const Config &cfg, const std::string &path, double &value, double min, double max, double default_value);
bool get_value(const Config &cfg, const std::string &path, bool &value, bool default_value);
bool get_value(const Config &cfg, const std::string &path, std::string &value, int min, int max, const std::string &default_value);
bool get_value(const Config &cfg, const std::string &path, std::string &value, int min, int max, const std::string &default_value, bool emptyToDefault);
bool get_value(const Config &cfg, const std::string &path, std::string &value, int min, int max, const std::string &default_value, bool emptyToDefault, const std::vector<std::string>& allowedValues);
std::string m_fileName; std::string m_fileName;
TGateway m_gateway; TGateway m_gateway;
@ -161,6 +159,7 @@ private:
TDCS m_dcs; TDCS m_dcs;
TRemote m_remote; TRemote m_remote;
TXLX m_xlx; TXLX m_xlx;
TLog m_log;
std::vector<TRepeater *> m_repeaters; std::vector<TRepeater *> m_repeaters;
std::vector<TircDDB *> m_ircDDB; std::vector<TircDDB *> m_ircDDB;
}; };

@ -34,7 +34,7 @@ DEPS = $(SRCS:.cpp=.d)
all: dstargateway all: dstargateway
dstargateway : GitVersion.h $(OBJS) dstargateway : GitVersion.h $(OBJS)
g++ $(CPPFLAGS) -o dstargateway $(OBJS) -lconfig++ -lcurl -pthread g++ $(CPPFLAGS) -o dstargateway $(OBJS) -lcurl -pthread
%.o : %.cpp %.o : %.cpp
g++ $(CPPFLAGS) -MMD -MD -c $< -o $@ g++ $(CPPFLAGS) -MMD -MD -c $< -o $@

@ -12,8 +12,10 @@
- [4. Contributing](#4-contributing) - [4. Contributing](#4-contributing)
- [4.1. Work Flow](#41-work-flow) - [4.1. Work Flow](#41-work-flow)
- [5. Version History](#5-version-history) - [5. Version History](#5-version-history)
- [5.1. v0.2](#51-v02) - [5.1. Version 0.3](#51-version-03)
- [5.2. v0.1](#52-v01) - [5.2. Version 0.2](#52-version-02)
- [5.3. Version 0.1](#53-version-01)
# 1. Introduction # 1. Introduction
This is a port of G4KLX Jonathan Naylor's [ircddbGateway](https://github.com/g4klx/ircDDBGateway). It is wxWidgets free and has minimal dependencies to boost (header libs only), libconfig++ and libcurl This is a port of G4KLX Jonathan Naylor's [ircddbGateway](https://github.com/g4klx/ircDDBGateway). It is wxWidgets free and has minimal dependencies to boost (header libs only), libconfig++ and libcurl
@ -64,7 +66,12 @@ sudo make install
``` ```
## 3.5. Configuring ## 3.5. Configuring
After installing you have to edit the configuration file. If you went with default paths, the config file is located in `/usr/local/etc/dstargateway.cfg` After installing you have to edit the configuration file. If you went with default paths, the config file is located in `/usr/local/etc/dstargateway.cfg`
The syntax is libconfig syntax, keep in my mind that **configuration keys are case sensitive**.
The configuration format is quite straight forward. It is organised in sections and key/value pairs.
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 separatorsensitive.
When done with configuration, the daemon will be started automatically on boot. To manual start and stop it use the usual systemd commands When done with configuration, the daemon will be started automatically on boot. To manual start and stop it use the usual systemd commands
``` ```
sudo systemctl start dstargateway.service sudo systemctl start dstargateway.service
@ -75,8 +82,10 @@ sudo systemctl stop dstargateway.service
I Use [Git flow](https://danielkummer.github.io/git-flow-cheatsheet/) as my workflow. PR are welcome and shall be done against the develop branch and follow the Git Flow branch naming rules. I Use [Git flow](https://danielkummer.github.io/git-flow-cheatsheet/) as my workflow. PR are welcome and shall be done against the develop branch and follow the Git Flow branch naming rules.
# 5. Version History # 5. Version History
## 5.1. v0.2 ## 5.1. Version 0.3
- [Improvement] Get ride of libcongif++ dependency. When upgrading from earlier version you need to manualy delete the config file before reinstalling.
## 5.2. Version 0.2
- [bugfix] ircDDBFreeze when repeater not found ([#1](https://github.com/F4FXL/DStarGateway/issues/1)) - [bugfix] ircDDBFreeze when repeater not found ([#1](https://github.com/F4FXL/DStarGateway/issues/1))
- Code sanitization - Code sanitization
## 5.2. v0.1 ## 5.3. Version 0.1
First working version First working version

@ -23,4 +23,4 @@
#include <string> #include <string>
const std::string VENDOR_NAME("Geoffrey Merck F4FXL / KC3FRA"); const std::string VENDOR_NAME("Geoffrey Merck F4FXL / KC3FRA");
const std::string VERSION("0.2"); const std::string VERSION("0.3");

@ -1,100 +1,169 @@
gateway = { # The configuration format is quite straight forward. It is organised in sections and key/value pairs.
# 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 type= # repeater, hotspot, dongle. Defaults to repeater
callsign = "N0CALL" callsign=
address = "" # this is the computer interface for the outgoing connection. Usually leave it blank and it will use whatever is avaiable. address=0.0.0.0 # this is the computer interface for the outgoing connection. Usually leave it blank and it will use whatever is avaiable.
icomAddress="172.16.0.20" icomAddress=172.16.0.20
icomPort=20000 icomPort=20000
hbAddress="127.0.0.1" #address to use for connectin with homebrew repeaters (MMDVMHost, DStarRepeater) hbAddress= #address to use for connecting to the homebrew repeaters (MMDVMHost, DStarRepeater), defaults to 127.0.0.1
hbPort=20010 hbPort=20010
latitude="" latitude=0.0
longitude="" longitude=0.0
description1="" description1=
description2="" description2=
url="" url=
language= # "english_uk", "deutsch", "dansk", "francais", "italiano", "polski", "english_us", "espanol", "svenska", "nederlands_nl", "nederlands_be", "norsk", "portugues" language= # valid values: english_uk, deutsch, dansk, francais, italiano, polski, english_us, espanol, svenska, nederlands_nl, nederlands_be, norsk, portugues
}
#up to 4 ircddb networks can be specified
# NOTHING usually needs to be specified in the ircddb section [ircddb_1]
# If nothing is specified, defaults to openquad enabled=true
ircddb = ( #ircddb networks in paretheses hostname=ircv4.openquad.net
# { username= # The ircDDB username default to the value defined for gateway callsign.
# hostname = "ipv4.openquad.net" password=
# username = "CHNGME" # The ircDDB username default to the value defined for server.callsign.
# password = "" [ircddb_2]
# }, enabled=false
# { hostname=
# hostname = "xx.ircddb.net" username= # The ircDDB username defaults to the value defined for gateway callsign.
# username = "CHNGME" # The ircDDB username default to the value defined for server.callsign. password=
# password = ""
# } [ircddb_3]
) hostname=false
username=CHNGME # The ircDDB username defaults to the value defined for gateway callsign.
repeaters = ( # The modules list is contained in parentheses password=
{ # Up to 15 different modules can be specified, each in curly brackets [ircddb_4]
band = "B" # Each module has to have a band letter hostname=false
callsign = "" # Has to be less than 8 characters, if empty gateway call will be used username=CHNGME # The ircDDB username defaults to the value defined for gateway callsign.
address = "" # address where the repeater can be found, if left empty defaults to 127.0.0.1 password=
# up to 4 repeaters can be added
[Repeater_1]
enabled=true
band=B # Each module has to have a band letter
callsign= # Has to be less than 8 characters, if empty gateway call will be used
address= # address where the repeater can be found, if left empty defaults to 127.0.0.1
port=20011 port=20011
type="hb" # hb or icom type=hb # valid values: hb or icom
reflector="" reflector=
reflectorAtStartup= # if reflector is set, this defaults to true
reflectorReconnect=30 # valid values: never, fixed, 5, 10, 15, 20, 25, 30, 60, 90, 120, 180
frequency=434.0
offset=-1.6
rangeKm=20
latitude=0.0
longitude=0.0
agl=
description1=
description2=
url=
band1=
band2=
band3=
[Repeater_2]
enabled=false
band= # Each module has to have a band letter
callsign= # Has to be less than 8 characters, if empty gateway call will be used
address= # address where the repeater can be found, if left empty defaults to 127.0.0.1
port=20012
type=hb # hb or icom
reflector=
reflectorAtStartup=true
reflectorReconnect=30 # never, fixed, 5, 10, 15, 20, 25, 30, 60, 90, 120, 180
frequency=434.0
offset=-1.6
rangeKm=20
latitude=0.0
longitude=0.0
agl=
description1=
description2=
url=
band1=
band2=
band3=
[Repeater_3]
enabled=false
band= # Each module has to have a band letter
callsign= # Has to be less than 8 characters, if empty gateway call will be used
address= # address where the repeater can be found, if left empty defaults to 127.0.0.1
port=20013
type=hb # hb or icom
reflector=DCS208 C
reflectorAtStartup=true
reflectorReconnect=30 # never, fixed, 5, 10, 15, 20, 25, 30, 60, 90, 120, 180
frequency=434.0
offset=-1.6
rangeKm=20
latitude=0.0
longitude=0.0
agl=
description1=
description2=
url=
band1=
band2=
band3=
[Repeater_4]
enabled=false
band= # Each module has to have a band letter
callsign= # Has to be less than 8 characters, if empty gateway call will be used
address= # address where the repeater can be found, if left empty defaults to 127.0.0.1
port=20014
type=hb # hb or icom
reflector=DCS208 C
reflectorAtStartup=true reflectorAtStartup=true
reflectorReconnect= # never, fixed, 5, 10, 15, 20, 25, 30, 60, 90, 120, 180 reflectorReconnect=30 # never, fixed, 5, 10, 15, 20, 25, 30, 60, 90, 120, 180
frequency=434.00000 frequency=434.0
offset=9.00000 offset=-1.6
rangeKm=0.1 rangeKm=20
latitude="" latitude=0.0
longitude="" longitude=0.0
agl="" agl=
description1="" description1=
description2="" description2=
url="" url=
band1="" band1=
band2="" band2=
band3="" band3=
# }, # be sure there is a comma between repeaters
# { # Up to 15 different modules can be specified, each in curly brackets [APRS]
# band = "C" # Each module has to have a band letter
# callsign = "N0CALL" # Has to be less than 8 characters
# port=20012
# }
}
) # close paren to close out the module defines
aprs = {
enabled=true enabled=true
hostname="rotate.aprs2.net" # Defaults to rotate.aprs2.net hostname=rotate.aprs2.net # Defaults to rotate.aprs2.net
port=14580 # Defaults to 14580, there is no reason to change this port=14580 # Defaults to 14580, there is no reason to change this
password="1234" password=12345
}
paths = { [Log]
log="/var/log/dstargateway/" path=/var/log/dstargateway/
data="/usr/local/share/dstargateway/" #Path where the data (hostfiles, audio files etc) can be found
}
dextra = { [Paths]
data=/usr/local/share/dstargateway.d/ #Path where the data (hostfiles, audio files etc) can be found
[DExtra]
enabled=true # There is no reason to disable this enabled=true # There is no reason to disable this
maxDongles=5 maxDongles=5
}
dplus = { [DPlus]
enabled=true # There is no reason to disable this enabled=true # There is no reason to disable this
maxDongles=5 maxDongles=5
login="" # defaults to gateway callsign login=F4FXL # defaults to gateway callsign
}
dcs = { [DCS]
enabled=true # There is no reason to disable this enabled=true # There is no reason to disable this
}
xlx = { [XLX]
enabled=true enabled=true
hostfileUrl="http://xlxapi.rlx.lu/api.php?do=GetXLXDMRMaster" hostfileUrl=http://xlxapi.rlx.lu/api.php?do=GetXLXDMRMaster
}
remote = { [Remote]
enabled=false enabled=false
port=4242 port=4242
password="CHANGE_ME" # If password is left blank, remote will be disabled regardless of the enabled field password=CHANGE_ME # If password is left blank, remote will be disabled regardless of the enabled field
}

Loading…
Cancel
Save

Powered by TurnKey Linux.