diff --git a/YSFClients.sln b/YSFClients.sln index 9003b89..520a676 100644 --- a/YSFClients.sln +++ b/YSFClients.sln @@ -7,6 +7,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YSFParrot", "YSFParrot\YSFP EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YSFReflector", "YSFReflector\YSFReflector.vcxproj", "{317D87F1-3485-4739-9F94-A07738B8E19D}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YSFGateway", "YSFGateway\YSFGateway.vcxproj", "{4F82857B-D2CC-48DC-91A8-6275BDD3081B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -31,6 +33,14 @@ Global {317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x64.Build.0 = Release|x64 {317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x86.ActiveCfg = Release|Win32 {317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x86.Build.0 = Release|Win32 + {4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Debug|x64.ActiveCfg = Debug|x64 + {4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Debug|x64.Build.0 = Debug|x64 + {4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Debug|x86.ActiveCfg = Debug|Win32 + {4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Debug|x86.Build.0 = Debug|Win32 + {4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Release|x64.ActiveCfg = Release|x64 + {4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Release|x64.Build.0 = Release|x64 + {4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Release|x86.ActiveCfg = Release|Win32 + {4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/YSFReflector/Conf.cpp b/YSFReflector/Conf.cpp new file mode 100644 index 0000000..2e25b16 --- /dev/null +++ b/YSFReflector/Conf.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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 "Conf.h" +#include "Log.h" + +#include +#include +#include +#include + +const int BUFFER_SIZE = 500; + +enum SECTION { + SECTION_NONE, + SECTION_GENERAL, + SECTION_INFO, + SECTION_LOG, + SECTION_NETWORK +}; + +CConf::CConf(const std::string& file) : +m_file(file), +m_daemon(false), +m_name(), +m_description(), +m_logDisplayLevel(0U), +m_logFileLevel(0U), +m_logFilePath(), +m_logFileRoot(), +m_networkPort(0U), +m_networkDebug(false) +{ +} + +CConf::~CConf() +{ +} + +bool CConf::read() +{ + FILE* fp = ::fopen(m_file.c_str(), "rt"); + if (fp == NULL) { + ::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str()); + return false; + } + + SECTION section = SECTION_NONE; + + char buffer[BUFFER_SIZE]; + while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) { + if (buffer[0U] == '#') + continue; + + if (buffer[0U] == '[') { + if (::strncmp(buffer, "[General]", 9U) == 0) + section = SECTION_GENERAL; + else if (::strncmp(buffer, "[Info]", 6U) == 0) + section = SECTION_INFO; + else if (::strncmp(buffer, "[Log]", 5U) == 0) + section = SECTION_LOG; + else if (::strncmp(buffer, "[Network]", 9U) == 0) + section = SECTION_NETWORK; + else + section = SECTION_NONE; + + continue; + } + + char* key = ::strtok(buffer, " \t=\r\n"); + if (key == NULL) + continue; + + char* value = ::strtok(NULL, "\r\n"); + if (section == SECTION_GENERAL) { + if (::strcmp(key, "Daemon") == 0) + m_daemon = ::atoi(value) == 1; + } else if (section == SECTION_INFO) { + if (::strcmp(key, "Name") == 0) + m_name = value; + else if (::strcmp(key, "Description") == 0) + m_description = value; + } else if (section == SECTION_LOG) { + if (::strcmp(key, "FilePath") == 0) + m_logFilePath = value; + else if (::strcmp(key, "FileRoot") == 0) + m_logFileRoot = value; + else if (::strcmp(key, "FileLevel") == 0) + m_logFileLevel = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayLevel") == 0) + m_logDisplayLevel = (unsigned int)::atoi(value); + } else if (section == SECTION_NETWORK) { + if (::strcmp(key, "Port") == 0) + m_networkPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_networkDebug = ::atoi(value) == 1; + } + } + + ::fclose(fp); + + return true; +} + +bool CConf::getDaemon() const +{ + return m_daemon; +} + +std::string CConf::getName() const +{ + return m_name; +} + +std::string CConf::getDescription() const +{ + return m_description; +} + +unsigned int CConf::getLogDisplayLevel() const +{ + return m_logDisplayLevel; +} + +unsigned int CConf::getLogFileLevel() const +{ + return m_logFileLevel; +} + +std::string CConf::getLogFilePath() const +{ + return m_logFilePath; +} + +std::string CConf::getLogFileRoot() const +{ + return m_logFileRoot; +} + +unsigned int CConf::getNetworkPort() const +{ + return m_networkPort; +} + +bool CConf::getNetworkDebug() const +{ + return m_networkDebug; +} diff --git a/YSFReflector/Conf.h b/YSFReflector/Conf.h new file mode 100644 index 0000000..e2cdc4d --- /dev/null +++ b/YSFReflector/Conf.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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. + */ + +#if !defined(CONF_H) +#define CONF_H + +#include +#include + +class CConf +{ +public: + CConf(const std::string& file); + ~CConf(); + + bool read(); + + // The General section + bool getDaemon() const; + + // The Info section + std::string getName() const; + std::string getDescription() const; + + // The Log section + unsigned int getLogDisplayLevel() const; + unsigned int getLogFileLevel() const; + std::string getLogFilePath() const; + std::string getLogFileRoot() const; + + // The Network section + unsigned int getNetworkPort() const; + bool getNetworkDebug() const; + +private: + std::string m_file; + bool m_daemon; + + std::string m_name; + std::string m_description; + + unsigned int m_logDisplayLevel; + unsigned int m_logFileLevel; + std::string m_logFilePath; + std::string m_logFileRoot; + + unsigned int m_networkPort; + bool m_networkDebug; +}; + +#endif diff --git a/YSFReflector/Log.cpp b/YSFReflector/Log.cpp new file mode 100644 index 0000000..e289fe3 --- /dev/null +++ b/YSFReflector/Log.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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 "Log.h" + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +static unsigned int m_fileLevel = 2U; +static std::string m_filePath; +static std::string m_fileRoot; + +static FILE* m_fpLog = NULL; + +static unsigned int m_displayLevel = 2U; + +static struct tm m_tm; + +static char LEVELS[] = " DMIWEF"; + +static bool LogOpen() +{ + if (m_fileLevel == 0U) + return true; + + time_t now; + ::time(&now); + + struct tm* tm = ::gmtime(&now); + + if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) { + if (m_fpLog != NULL) + return true; + } else { + if (m_fpLog != NULL) + ::fclose(m_fpLog); + } + + char filename[50U]; +#if defined(_WIN32) || defined(_WIN64) + ::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#else + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#endif + + m_fpLog = ::fopen(filename, "a+t"); + m_tm = *tm; + + return m_fpLog != NULL; +} + +bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel) +{ + m_filePath = filePath; + m_fileRoot = fileRoot; + m_fileLevel = fileLevel; + m_displayLevel = displayLevel; + return ::LogOpen(); +} + +void LogFinalise() +{ + if (m_fpLog != NULL) + ::fclose(m_fpLog); +} + +void Log(unsigned int level, const char* fmt, ...) +{ + assert(fmt != NULL); + + char buffer[300U]; +#if defined(_WIN32) || defined(_WIN64) + SYSTEMTIME st; + ::GetSystemTime(&st); + + ::sprintf(buffer, "%c: %04u-%02u-%02u %02u:%02u:%02u.%03u ", LEVELS[level], st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); +#else + struct timeval now; + ::gettimeofday(&now, NULL); + + struct tm* tm = ::gmtime(&now.tv_sec); + + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U); +#endif + + va_list vl; + va_start(vl, fmt); + + ::vsprintf(buffer + ::strlen(buffer), fmt, vl); + + va_end(vl); + + if (level >= m_fileLevel && m_fileLevel != 0U) { + bool ret = ::LogOpen(); + if (!ret) + return; + + ::fprintf(m_fpLog, "%s\n", buffer); + ::fflush(m_fpLog); + } + + if (level >= m_displayLevel && m_displayLevel != 0U) { + ::fprintf(stdout, "%s\n", buffer); + ::fflush(stdout); + } + + if (level == 6U) { // Fatal + ::fclose(m_fpLog); + exit(1); + } +} diff --git a/YSFReflector/Log.h b/YSFReflector/Log.h new file mode 100644 index 0000000..d671ef9 --- /dev/null +++ b/YSFReflector/Log.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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. + */ + +#if !defined(LOG_H) +#define LOG_H + +#include + +#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__) +#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__) +#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__) +#define LogWarning(fmt, ...) Log(4U, fmt, ##__VA_ARGS__) +#define LogError(fmt, ...) Log(5U, fmt, ##__VA_ARGS__) +#define LogFatal(fmt, ...) Log(6U, fmt, ##__VA_ARGS__) + +extern void Log(unsigned int level, const char* fmt, ...); + +extern bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel); +extern void LogFinalise(); + +#endif diff --git a/YSFReflector/Makefile b/YSFReflector/Makefile index ea5e6e7..783d774 100644 --- a/YSFReflector/Makefile +++ b/YSFReflector/Makefile @@ -4,7 +4,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x LIBS = LDFLAGS = -g -OBJECTS = Network.o StopWatch.o Timer.o UDPSocket.o Utils.o YSFReflector.o +OBJECTS = Conf.o Log.o Network.o StopWatch.o Timer.o UDPSocket.o Utils.o YSFReflector.o all: YSFReflector diff --git a/YSFReflector/Network.cpp b/YSFReflector/Network.cpp index d20bb38..d3061f7 100644 --- a/YSFReflector/Network.cpp +++ b/YSFReflector/Network.cpp @@ -26,18 +26,26 @@ const unsigned int BUFFER_LENGTH = 200U; -CNetwork::CNetwork(unsigned int port, bool debug) : +CNetwork::CNetwork(unsigned int port, const std::string& name, const std::string& description, bool debug) : m_socket(port), +m_name(name), +m_description(description), m_address(), m_port(0U), m_callsign(), m_debug(debug), -m_buffer(1000U, "YSF Network") +m_buffer(1000U, "YSF Network"), +m_status(NULL) { + m_name.resize(16U, ' '); + m_description.resize(14U, ' '); + + m_status = new unsigned char[50U]; } CNetwork::~CNetwork() { + delete[] m_status; } bool CNetwork::open() @@ -101,6 +109,12 @@ void CNetwork::clock(unsigned int ms) return; } + // Handle incoming status requests + if (::memcmp(buffer, "YSFS", 4U) == 0) { + m_socket.write(m_status, 42U, address, port); + return; + } + // Invalid packet type? if (::memcmp(buffer, "YSFD", 4U) != 0) return; @@ -142,6 +156,33 @@ bool CNetwork::readPoll(std::string& callsign, in_addr& address, unsigned int& p return true; } +void CNetwork::setCount(unsigned int count) +{ + if (count > 999U) + count = 999U; + + unsigned int hash = 0U; + + for (unsigned int i = 0U; i < m_name.size(); i++) { + hash += m_name.at(i); + hash += (hash << 10); + hash ^= (hash >> 6); + } + + for (unsigned int i = 0U; i < m_description.size(); i++) { + hash += m_description.at(i); + hash += (hash << 10); + hash ^= (hash >> 6); + } + + // Final avalanche + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + ::sprintf((char*)m_status, "YSFS%05u%16.16s%14.14s%03u", hash % 100000U, m_name.c_str(), m_description.c_str(), count); +} + void CNetwork::close() { m_socket.close(); diff --git a/YSFReflector/Network.h b/YSFReflector/Network.h index 89e8b2a..75590a4 100644 --- a/YSFReflector/Network.h +++ b/YSFReflector/Network.h @@ -29,7 +29,7 @@ class CNetwork { public: - CNetwork(unsigned int port, bool debug); + CNetwork(unsigned int port, const std::string& name, const std::string& description, bool debug); ~CNetwork(); bool open(); @@ -44,13 +44,18 @@ public: void clock(unsigned int ms); + void setCount(unsigned int count); + private: CUDPSocket m_socket; + std::string m_name; + std::string m_description; in_addr m_address; unsigned int m_port; std::string m_callsign; bool m_debug; CRingBuffer m_buffer; + unsigned char* m_status; }; #endif diff --git a/YSFReflector/UDPSocket.cpp b/YSFReflector/UDPSocket.cpp index 733edd0..396f1f7 100644 --- a/YSFReflector/UDPSocket.cpp +++ b/YSFReflector/UDPSocket.cpp @@ -17,6 +17,7 @@ */ #include "UDPSocket.h" +#include "Log.h" #include @@ -37,7 +38,7 @@ m_fd(-1) WSAData data; int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); if (wsaRet != 0) - ::fprintf(stderr, "Error from WSAStartup\n"); + LogError("Error from WSAStartup"); #endif } @@ -50,7 +51,7 @@ m_fd(-1) WSAData data; int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); if (wsaRet != 0) - ::fprintf(stderr, "Error from WSAStartup\n"); + LogError("Error from WSAStartup"); #endif } @@ -77,7 +78,7 @@ in_addr CUDPSocket::lookup(const std::string& hostname) return addr; } - ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + LogError("Cannot find address for host %s", hostname.c_str()); addr.s_addr = INADDR_NONE; return addr; @@ -94,7 +95,7 @@ in_addr CUDPSocket::lookup(const std::string& hostname) return addr; } - ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + LogError("Cannot find address for host %s", hostname.c_str()); addr.s_addr = INADDR_NONE; return addr; @@ -106,9 +107,9 @@ bool CUDPSocket::open() m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); if (m_fd < 0) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Cannot create the UDP socket, err: %lu\n", ::GetLastError()); + LogError("Cannot create the UDP socket, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Cannot create the UDP socket, err: %d\n", errno); + LogError("Cannot create the UDP socket, err: %d", errno); #endif return false; } @@ -127,7 +128,7 @@ bool CUDPSocket::open() addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); #endif if (addr.sin_addr.s_addr == INADDR_NONE) { - ::fprintf(stderr, "The local address is invalid - %s\n", m_address.c_str()); + LogError("The local address is invalid - %s", m_address.c_str()); return false; } } @@ -135,18 +136,18 @@ bool CUDPSocket::open() int reuse = 1; if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Cannot set the UDP socket option, err: %lu\n", ::GetLastError()); + LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Cannot set the UDP socket option, err: %d\n", errno); + LogError("Cannot set the UDP socket option, err: %d", errno); #endif return false; } if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Cannot bind the UDP address, err: %lu\n", ::GetLastError()); + LogError("Cannot bind the UDP address, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Cannot bind the UDP address, err: %d\n", errno); + LogError("Cannot bind the UDP address, err: %d", errno); #endif return false; } @@ -177,9 +178,9 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, in_addr& addres int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); if (ret < 0) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Error returned from UDP select, err: %lu\n", ::GetLastError()); + LogError("Error returned from UDP select, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Error returned from UDP select, err: %d\n", errno); + LogError("Error returned from UDP select, err: %d", errno); #endif return -1; } @@ -201,9 +202,9 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, in_addr& addres #endif if (len <= 0) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Error returned from recvfrom, err: %lu\n", ::GetLastError()); + LogError("Error returned from recvfrom, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Error returned from recvfrom, err: %d\n", errno); + LogError("Error returned from recvfrom, err: %d", errno); #endif return -1; } @@ -233,9 +234,9 @@ bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const i #endif if (ret < 0) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Error returned from sendto, err: %lu\n", ::GetLastError()); + LogError("Error returned from sendto, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Error returned from sendto, err: %d\n", errno); + LogError("Error returned from sendto, err: %d", errno); #endif return false; } diff --git a/YSFReflector/Utils.cpp b/YSFReflector/Utils.cpp index d5b2c75..49ded13 100644 --- a/YSFReflector/Utils.cpp +++ b/YSFReflector/Utils.cpp @@ -12,6 +12,7 @@ */ #include "Utils.h" +#include "Log.h" #include #include @@ -27,7 +28,7 @@ void CUtils::dump(int level, const std::string& title, const unsigned char* data { assert(data != NULL); - ::fprintf(stdout, "%s\n", title.c_str()); + ::Log(level, "%s", title.c_str()); unsigned int offset = 0U; @@ -58,7 +59,7 @@ void CUtils::dump(int level, const std::string& title, const unsigned char* data output += '*'; - ::fprintf(stdout, "%04X: %s\n", offset, output.c_str()); + ::Log(level, "%04X: %s", offset, output.c_str()); offset += 16U; diff --git a/YSFReflector/YSFReflector.cpp b/YSFReflector/YSFReflector.cpp index 89c6a69..7b8d069 100644 --- a/YSFReflector/YSFReflector.cpp +++ b/YSFReflector/YSFReflector.cpp @@ -20,6 +20,7 @@ #include "StopWatch.h" #include "Network.h" #include "Version.h" +#include "Log.h" #if defined(_WIN32) || defined(_WIN64) #include @@ -27,6 +28,12 @@ #include #endif +#if defined(_WIN32) || defined(_WIN64) +const char* DEFAULT_INI_FILE = "YSFReflector.ini"; +#else +const char* DEFAULT_INI_FILE = "/etc/YSFReflector.ini"; +#endif + #include #include #include @@ -35,39 +42,34 @@ int main(int argc, char** argv) { - if (argc == 1) { - ::fprintf(stderr, "Usage: YSFReflector [log file]\n"); - return 1; - } - - unsigned int port = ::atoi(argv[1]); - if (port == 0U) { - ::fprintf(stderr, "YSFReflector: invalid port number\n"); - return 1; - } - - FILE* fp = NULL; - if (argc > 2) { - fp = ::fopen(argv[2], "wt"); - if (fp == NULL) { - ::fprintf(stderr, "YSFReflector: cannot open the logging file - %s\n", argv[2]); - return 1; + const char* iniFile = DEFAULT_INI_FILE; + if (argc > 1) { + for (int currentArg = 1; currentArg < argc; ++currentArg) { + std::string arg = argv[currentArg]; + if ((arg == "-v") || (arg == "--version")) { + ::fprintf(stdout, "YSFReflector version %s\n", VERSION); + return 0; + } + else if (arg.substr(0, 1) == "-") { + ::fprintf(stderr, "Usage: YSFReflector [-v|--version] [filename]\n"); + return 1; + } + else { + iniFile = argv[currentArg]; + } } } - CYSFReflector Reflector(port, fp); - Reflector.run(); - - if (fp != NULL) - ::fclose(fp); + CYSFReflector* reflector = new CYSFReflector(std::string(iniFile)); + reflector->run(); + delete reflector; return 0; } -CYSFReflector::CYSFReflector(unsigned int port, FILE* fp) : -m_port(port), -m_repeaters(), -m_fp(fp) +CYSFReflector::CYSFReflector(const std::string& file) : +m_conf(file), +m_repeaters() { } @@ -77,9 +79,80 @@ CYSFReflector::~CYSFReflector() void CYSFReflector::run() { - CNetwork network(m_port, false); + bool ret = m_conf.read(); + if (!ret) { + ::fprintf(stderr, "YSFRefector: cannot read the .ini file\n"); + return; + } + + ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); + if (!ret) { + ::fprintf(stderr, "YSFReflector: unable to open the log file\n"); + return; + } + +#if !defined(_WIN32) && !defined(_WIN64) + bool m_daemon = m_conf.getDaemon(); + if (m_daemon) { + // Create new process + pid_t pid = ::fork(); + if (pid == -1) { + ::LogWarning("Couldn't fork() , exiting"); + return -1; + } + else if (pid != 0) + exit(EXIT_SUCCESS); + + // Create new session and process group + if (::setsid() == -1) { + ::LogWarning("Couldn't setsid(), exiting"); + return -1; + } + + // Set the working directory to the root directory + if (::chdir("/") == -1) { + ::LogWarning("Couldn't cd /, exiting"); + return -1; + } + + ::close(STDIN_FILENO); + ::close(STDOUT_FILENO); + ::close(STDERR_FILENO); + + //If we are currently root... + if (getuid() == 0) { + struct passwd* user = ::getpwnam("mmdvm"); + if (user == NULL) { + ::LogError("Could not get the mmdvm user, exiting"); + return -1; + } - bool ret = network.open(); + uid_t mmdvm_uid = user->pw_uid; + gid_t mmdvm_gid = user->pw_gid; + + //Set user and group ID's to mmdvm:mmdvm + if (setgid(mmdvm_gid) != 0) { + ::LogWarning("Could not set mmdvm GID, exiting"); + return -1; + } + + if (setuid(mmdvm_uid) != 0) { + ::LogWarning("Could not set mmdvm UID, exiting"); + return -1; + } + + //Double check it worked (AKA Paranoia) + if (setuid(0) != -1) { + ::LogWarning("It's possible to regain root - something is wrong!, exiting"); + return -1; + } + } + } +#endif + + CNetwork network(m_conf.getNetworkPort(), m_conf.getName(), m_conf.getDescription(), m_conf.getNetworkDebug()); + + ret = network.open(); if (!ret) return; @@ -89,7 +162,7 @@ void CYSFReflector::run() CTimer pollTimer(1000U, 5U); pollTimer.start(); - log("Starting YSFReflector-%s", VERSION); + LogMessage("Starting YSFReflector-%s", VERSION); CTimer watchdogTimer(1000U, 0U, 1500U); @@ -115,7 +188,7 @@ void CYSFReflector::run() else ::memcpy(dst, "??????????", YSF_CALLSIGN_LENGTH); - log("Received data from %10.10s to %10.10s at %10.10s", src, dst, buffer + 4U); + LogMessage("Received data from %10.10s to %10.10s at %10.10s", src, dst, buffer + 4U); } else { if (::memcmp(tag, buffer + 4U, YSF_CALLSIGN_LENGTH) == 0) { bool changed = false; @@ -131,7 +204,7 @@ void CYSFReflector::run() } if (changed) - log("Received data from %10.10s to %10.10s at %10.10s", src, dst, buffer + 4U); + LogMessage("Received data from %10.10s to %10.10s at %10.10s", src, dst, buffer + 4U); } } @@ -149,7 +222,7 @@ void CYSFReflector::run() } if (buffer[34U] == 0x01U) { - log("Received end of transmission"); + LogMessage("Received end of transmission"); watchdogTimer.stop(); } } @@ -163,13 +236,14 @@ void CYSFReflector::run() if (ret) { CYSFRepeater* rpt = findRepeater(callsign); if (rpt == NULL) { - log("Adding %s", callsign.c_str()); + LogMessage("Adding %s", callsign.c_str()); rpt = new CYSFRepeater; rpt->m_timer.start(); rpt->m_callsign = callsign; rpt->m_address = address; rpt->m_port = port; m_repeaters.push_back(rpt); + network.setCount(m_repeaters.size()); } else { rpt->m_timer.start(); rpt->m_address = address; @@ -195,15 +269,16 @@ void CYSFReflector::run() for (std::vector::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { if ((*it)->m_timer.hasExpired()) { - log("Removing %s", (*it)->m_callsign.c_str()); + LogMessage("Removing %s", (*it)->m_callsign.c_str()); m_repeaters.erase(it); + network.setCount(m_repeaters.size()); break; } } watchdogTimer.clock(ms); if (watchdogTimer.isRunning() && watchdogTimer.hasExpired()) { - log("Network watchdog has expired"); + LogMessage("Network watchdog has expired"); watchdogTimer.stop(); } @@ -217,6 +292,8 @@ void CYSFReflector::run() } network.close(); + + ::LogFinalise(); } CYSFRepeater* CYSFReflector::findRepeater(const std::string& callsign) const @@ -228,36 +305,3 @@ CYSFRepeater* CYSFReflector::findRepeater(const std::string& callsign) const return NULL; } - -void CYSFReflector::log(const char* text, ...) -{ - char buffer[300U]; -#if defined(_WIN32) || defined(_WIN64) - SYSTEMTIME st; - ::GetSystemTime(&st); - - ::sprintf(buffer, "%04u-%02u-%02u %02u:%02u:%02u ", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); -#else - struct timeval now; - ::gettimeofday(&now, NULL); - - struct tm* tm = ::gmtime(&now.tv_sec); - - ::sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); -#endif - - va_list vl; - va_start(vl, text); - - ::vsprintf(buffer + ::strlen(buffer), text, vl); - - va_end(vl); - - if (m_fp != NULL) { - ::fprintf(m_fp, "%s\n", buffer); - ::fflush(m_fp); - } - - ::fprintf(stdout, "%s\n", buffer); - ::fflush(stdout); -} \ No newline at end of file diff --git a/YSFReflector/YSFReflector.h b/YSFReflector/YSFReflector.h index 2b10b5e..9491b25 100644 --- a/YSFReflector/YSFReflector.h +++ b/YSFReflector/YSFReflector.h @@ -20,6 +20,7 @@ #define YSFReflector_H #include "Timer.h" +#include "Conf.h" #include #include @@ -56,18 +57,17 @@ public: class CYSFReflector { public: - CYSFReflector(unsigned int port, FILE* fp); + CYSFReflector(const std::string& file); ~CYSFReflector(); void run(); private: - unsigned int m_port; + CConf m_conf; + std::string m_file; std::vector m_repeaters; - FILE* m_fp; CYSFRepeater* findRepeater(const std::string& callsign) const; - void log(const char* text, ...); }; #endif diff --git a/YSFReflector/YSFReflector.ini b/YSFReflector/YSFReflector.ini new file mode 100644 index 0000000..41cf3ed --- /dev/null +++ b/YSFReflector/YSFReflector.ini @@ -0,0 +1,17 @@ +[General] +Daemon=1 + +[Info] +Name=16 characters max +Description=14 characters max + +[Log] +# Logging levels, 0=No logging +DisplayLevel=1 +FileLevel=1 +FilePath=. +FileRoot=YSFReflector + +[Network] +Port=24000 +Debug=0 diff --git a/YSFReflector/YSFReflector.vcxproj b/YSFReflector/YSFReflector.vcxproj index 4e5dec2..3ee4fc9 100644 --- a/YSFReflector/YSFReflector.vcxproj +++ b/YSFReflector/YSFReflector.vcxproj @@ -146,6 +146,8 @@ + + @@ -157,6 +159,8 @@ + + diff --git a/YSFReflector/YSFReflector.vcxproj.filters b/YSFReflector/YSFReflector.vcxproj.filters index d07cf1d..e0454ae 100644 --- a/YSFReflector/YSFReflector.vcxproj.filters +++ b/YSFReflector/YSFReflector.vcxproj.filters @@ -38,6 +38,12 @@ Header Files + + Header Files + + + Header Files + @@ -58,5 +64,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file