From f86eef66c8163bdafb305ad4055aad0c7645274b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 18 Jan 2023 21:35:51 +0100 Subject: [PATCH] Simple UDP calls, if wired up a fair bit of this can go again. this is preliminary work --- src/DebugConfiguration.cpp | 241 +++++++++++++++++++++++++++++++++ src/DebugConfiguration.h | 104 +++++++++++++- src/mesh/eth/ethClient.cpp | 27 ++++ src/mesh/http/WiFiAPClient.cpp | 26 ++++ 4 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 src/DebugConfiguration.cpp diff --git a/src/DebugConfiguration.cpp b/src/DebugConfiguration.cpp new file mode 100644 index 000000000..8387170f8 --- /dev/null +++ b/src/DebugConfiguration.cpp @@ -0,0 +1,241 @@ +/* based on https://github.com/arcao/Syslog + +MIT License + +Copyright (c) 2016 Martin Sloup + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.*/ + +#include "DebugConfiguration.h" + +Syslog::Syslog(UDP &client) { + this->_client = &client; + this->_server = NULL; + this->_port = 0; + this->_deviceHostname = SYSLOG_NILVALUE; + this->_appName = SYSLOG_NILVALUE; + this->_priDefault = LOGLEVEL_KERN; +} + +Syslog &Syslog::server(const char* server, uint16_t port) { + this->_server = server; + this->_port = port; + return *this; +} + +Syslog &Syslog::server(IPAddress ip, uint16_t port) { + this->_ip = ip; + this->_server = NULL; + this->_port = port; + return *this; +} + +Syslog &Syslog::deviceHostname(const char* deviceHostname) { + this->_deviceHostname = (deviceHostname == NULL) ? SYSLOG_NILVALUE : deviceHostname; + return *this; +} + +Syslog &Syslog::appName(const char* appName) { + this->_appName = (appName == NULL) ? SYSLOG_NILVALUE : appName; + return *this; +} + +Syslog &Syslog::defaultPriority(uint16_t pri) { + this->_priDefault = pri; + return *this; +} + +Syslog &Syslog::logMask(uint8_t priMask) { + this->_priMask = priMask; + return *this; +} + +void Syslog::enable() { + this->_enabled = true; +} + +void Syslog::disable() { + this->_enabled = false; +} + +bool Syslog::isEnabled() +{ + return this->_enabled; +} + +bool Syslog::log(uint16_t pri, const __FlashStringHelper *message) { + return this->_sendLog(pri, message); +} + +bool Syslog::log(uint16_t pri, const String &message) { + return this->_sendLog(pri, message.c_str()); +} + +bool Syslog::log(uint16_t pri, const char *message) { + return this->_sendLog(pri, message); +} + +bool Syslog::vlogf(uint16_t pri, const char *fmt, va_list args) { + char *message; + size_t initialLen; + size_t len; + bool result; + + initialLen = strlen(fmt); + + message = new char[initialLen + 1]; + + len = vsnprintf(message, initialLen + 1, fmt, args); + if (len > initialLen) { + delete[] message; + message = new char[len + 1]; + + vsnprintf(message, len + 1, fmt, args); + } + + result = this->_sendLog(pri, message); + + delete[] message; + return result; +} + +bool Syslog::vlogf_P(uint16_t pri, PGM_P fmt_P, va_list args) { + char *message; + size_t initialLen; + size_t len; + bool result; + + initialLen = strlen_P(fmt_P); + + message = new char[initialLen + 1]; + + len = vsnprintf_P(message, initialLen + 1, fmt_P, args); + if (len > initialLen) { + delete[] message; + message = new char[len + 1]; + + vsnprintf(message, len + 1, fmt_P, args); + } + + result = this->_sendLog(pri, message); + + delete[] message; + return result; +} + + +bool Syslog::logf(uint16_t pri, const char *fmt, ...) { + va_list args; + bool result; + + va_start(args, fmt); + result = this->vlogf(pri, fmt, args); + va_end(args); + return result; +} + +bool Syslog::logf_P(uint16_t pri, PGM_P fmt_P, ...) { + va_list args; + bool result; + + va_start(args, fmt_P); + result = this->vlogf_P(pri, fmt_P, args); + va_end(args); + return result; +} + +inline bool Syslog::_sendLog(uint16_t pri, const char *message) { + int result; + + if (!this->_enabled) + return false; + + if ((this->_server == NULL && this->_ip == INADDR_NONE) || this->_port == 0) + return false; + + // Check priority against priMask values. + if ((LOG_MASK(LOG_PRI(pri)) & this->_priMask) == 0) + return true; + + // Set default facility if none specified. + if ((pri & LOG_FACMASK) == 0) + pri = LOG_MAKEPRI(LOG_FAC(this->_priDefault), pri); + + if (this->_server != NULL) { + result = this->_client->beginPacket(this->_server, this->_port); + } else { + result = this->_client->beginPacket(this->_ip, this->_port); + } + + if (result != 1) + return false; + + this->_client->print('<'); + this->_client->print(pri); + this->_client->print(F(">1 - ")); + this->_client->print(this->_deviceHostname); + this->_client->print(' '); + this->_client->print(this->_appName); + this->_client->print(F(" - - - \xEF\xBB\xBF")); + this->_client->print(F("[0]: ")); + this->_client->print(message); + this->_client->endPacket(); + + return true; +} + +inline bool Syslog::_sendLog(uint16_t pri, const __FlashStringHelper *message) { + int result; + + if (!this->_enabled) + return false; + + if ((this->_server == NULL && this->_ip == INADDR_NONE) || this->_port == 0) + return false; + + // Check priority against priMask values. + if ((LOG_MASK(LOG_PRI(pri)) & this->_priMask) == 0) + return true; + + // Set default facility if none specified. + if ((pri & LOG_FACMASK) == 0) + pri = LOG_MAKEPRI(LOG_FAC(this->_priDefault), pri); + + if (this->_server != NULL) { + result = this->_client->beginPacket(this->_server, this->_port); + } else { + result = this->_client->beginPacket(this->_ip, this->_port); + } + + if (result != 1) + return false; + + this->_client->print('<'); + this->_client->print(pri); + this->_client->print(F(">1 - ")); + this->_client->print(this->_deviceHostname); + this->_client->print(' '); + this->_client->print(this->_appName); + this->_client->print(F(" - - - \xEF\xBB\xBF")); + this->_client->print(message); + this->_client->endPacket(); + + + return true; +} \ No newline at end of file diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index 389a35d81..05fb1fdbb 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -1,3 +1,6 @@ +#ifndef SYSLOG_H +#define SYSLOG_H + // DEBUG LED #ifndef LED_INVERTED #define LED_INVERTED 0 // define as 1 if LED is active low (on) @@ -14,9 +17,10 @@ #endif #define MESHTASTIC_LOG_LEVEL_DEBUG "DEBUG" -#define MESHTASTIC_LOG_LEVEL_INFO "INFO " -#define MESHTASTIC_LOG_LEVEL_WARN "WARN " +#define MESHTASTIC_LOG_LEVEL_INFO "INFO " +#define MESHTASTIC_LOG_LEVEL_WARN "WARN " #define MESHTASTIC_LOG_LEVEL_ERROR "ERROR" +#define MESHTASTIC_LOG_LEVEL_CRIT "CRIT " #define MESHTASTIC_LOG_LEVEL_TRACE "TRACE" #include "SerialConsole.h" @@ -28,21 +32,72 @@ #define LOG_INFO(...) SEGGER_RTT_printf(0, __VA_ARGS__) #define LOG_WARN(...) SEGGER_RTT_printf(0, __VA_ARGS__) #define LOG_ERROR(...) SEGGER_RTT_printf(0, __VA_ARGS__) +#define LOG_CRIT(...) SEGGER_RTT_printf(0, __VA_ARGS__) +#define LOG_TRACE(...) SEGGER_RTT_printf(0, __VA_ARGS__) #else #ifdef DEBUG_PORT #define LOG_DEBUG(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_DEBUG, __VA_ARGS__) #define LOG_INFO(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_INFO, __VA_ARGS__) #define LOG_WARN(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_WARN, __VA_ARGS__) #define LOG_ERROR(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_ERROR, __VA_ARGS__) -#define LOG_TRACE(...) DEBUG_PORT.log(MESHTASTIC_LOG_TRACE, __VA_ARGS__) +#define LOG_CRIT(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_CRIT, __VA_ARGS__) +#define LOG_TRACE(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_TRACE, __VA_ARGS__) #else #define LOG_DEBUG(...) #define LOG_INFO(...) #define LOG_WARN(...) #define LOG_ERROR(...) +#define LOG_CRIT(...) +#define LOG_TRACE(...) #endif #endif +#define SYSLOG_NILVALUE "-" + +#define SYSLOG_CRIT 2 /* critical conditions */ +#define SYSLOG_ERR 3 /* error conditions */ +#define SYSLOG_WARN 4 /* warning conditions */ +#define SYSLOG_INFO 6 /* informational */ +#define SYSLOG_DEBUG 7 /* debug-level messages */ +// trace does not go out to syslog (yet?) + +#define LOG_PRIMASK 0x07 /* mask to extract priority part (internal) */ + /* extract priority */ +#define LOG_PRI(p) ((p) & LOG_PRIMASK) +#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) + +/* facility codes */ +#define LOGLEVEL_KERN (0<<3) /* kernel messages */ +#define LOGLEVEL_USER (1<<3) /* random user-level messages */ +#define LOGLEVEL_MAIL (2<<3) /* mail system */ +#define LOGLEVEL_DAEMON (3<<3) /* system daemons */ +#define LOGLEVEL_AUTH (4<<3) /* security/authorization messages */ +#define LOGLEVEL_SYSLOG (5<<3) /* messages generated internally by syslogd */ +#define LOGLEVEL_LPR (6<<3) /* line printer subsystem */ +#define LOGLEVEL_NEWS (7<<3) /* network news subsystem */ +#define LOGLEVEL_UUCP (8<<3) /* UUCP subsystem */ +#define LOGLEVEL_CRON (9<<3) /* clock daemon */ +#define LOGLEVEL_AUTHPRIV (10<<3) /* security/authorization messages (private) */ +#define LOGLEVEL_FTP (11<<3) /* ftp daemon */ + +/* other codes through 15 reserved for system use */ +#define LOGLEVEL_LOCAL0 (16<<3) /* reserved for local use */ +#define LOGLEVEL_LOCAL1 (17<<3) /* reserved for local use */ +#define LOGLEVEL_LOCAL2 (18<<3) /* reserved for local use */ +#define LOGLEVEL_LOCAL3 (19<<3) /* reserved for local use */ +#define LOGLEVEL_LOCAL4 (20<<3) /* reserved for local use */ +#define LOGLEVEL_LOCAL5 (21<<3) /* reserved for local use */ +#define LOGLEVEL_LOCAL6 (22<<3) /* reserved for local use */ +#define LOGLEVEL_LOCAL7 (23<<3) /* reserved for local use */ + +#define LOG_NFACILITIES 24 /* current number of facilities */ +#define LOG_FACMASK 0x03f8 /* mask to extract facility part */ + /* facility of pri */ +#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) + +#define LOG_MASK(pri) (1 << (pri)) /* mask for one priority */ +#define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) /* all priorities through pri */ + // ----------------------------------------------------------------------------- // AXP192 (Rev1-specific options) // ----------------------------------------------------------------------------- @@ -52,3 +107,46 @@ // Default Bluetooth PIN #define defaultBLEPin 123456 + +class Syslog { + private: + UDP* _client; + IPAddress _ip; + const char* _server; + uint16_t _port; + const char* _deviceHostname; + const char* _appName; + uint16_t _priDefault; + uint8_t _priMask = 0xff; + bool _enabled = false; + + bool _sendLog(uint16_t pri, const char *message); + bool _sendLog(uint16_t pri, const __FlashStringHelper *message); + + public: + Syslog(UDP &client); + + Syslog &server(const char* server, uint16_t port); + Syslog &server(IPAddress ip, uint16_t port); + Syslog &deviceHostname(const char* deviceHostname); + Syslog &appName(const char* appName); + Syslog &defaultPriority(uint16_t pri = LOGLEVEL_KERN); + Syslog &logMask(uint8_t priMask); + + void enable(); + void disable(); + bool isEnabled(); + + bool log(uint16_t pri, const __FlashStringHelper *message); + bool log(uint16_t pri, const String &message); + bool log(uint16_t pri, const char *message); + + bool vlogf(uint16_t pri, const char *fmt, va_list args) __attribute__((format(printf, 3, 0))); + bool vlogf_P(uint16_t pri, PGM_P fmt_P, va_list args) __attribute__((format(printf, 3, 0))); + + bool logf(uint16_t pri, const char *fmt, ...) __attribute__((format(printf, 3, 4))); + + bool logf_P(uint16_t pri, PGM_P fmt_P, ...) __attribute__((format(printf, 3, 4))); +}; + +#endif \ No newline at end of file diff --git a/src/mesh/eth/ethClient.cpp b/src/mesh/eth/ethClient.cpp index dce04b191..a80265b03 100644 --- a/src/mesh/eth/ethClient.cpp +++ b/src/mesh/eth/ethClient.cpp @@ -17,6 +17,9 @@ NTPClient timeClient(ntpUDP, config.network.ntp_server); uint32_t ntp_renew = 0; #endif +EthernetUDP syslogClient; +Syslog syslog(syslogClient); + bool ethStartupComplete = 0; using namespace concurrency; @@ -36,6 +39,27 @@ static int32_t reconnectETH() timeClient.begin(); timeClient.setUpdateInterval(60 * 60); // Update once an hour #endif + + if(config.network.rsyslog_server[0]) { + LOG_INFO("Starting Syslog client\n"); + // Defaults + int serverPort = 514; + const char *serverAddr = moduleConfig.mqtt.address; + String server = String(serverAddr); + int delimIndex = server.indexOf(':'); + if (delimIndex > 0) { + String port = server.substring(delimIndex + 1, server.length()); + server[delimIndex] = 0; + serverPort = port.toInt(); + serverAddr = server.c_str(); + } + syslog.server(serverAddr, serverPort); + syslog.deviceHostname(WiFi.getHostname()); + syslog.appName("Meshtastic"); + syslog.defaultPriority(LOGLEVEL_USER); + syslog.enable(); + } + // initWebServer(); initApiServer(); @@ -138,10 +162,13 @@ bool initEthernet() bool isEthernetAvailable() { if (!config.network.eth_enabled) { + syslog.disable(); return false; } else if (Ethernet.hardwareStatus() == EthernetNoHardware) { + syslog.disable(); return false; } else if (Ethernet.linkStatus() == LinkOFF) { + syslog.disable(); return false; } else { return true; diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp index 1139c305e..e488ecb77 100644 --- a/src/mesh/http/WiFiAPClient.cpp +++ b/src/mesh/http/WiFiAPClient.cpp @@ -39,6 +39,9 @@ unsigned long lastrun_ntp = 0; bool needReconnect = true; // If we create our reconnector, run it once at the beginning +WiFiUDP syslogClient; +Syslog syslog(syslogClient); + Periodic *wifiReconnect; static int32_t reconnectWiFi() @@ -135,6 +138,26 @@ static void onNetworkConnected() timeClient.setUpdateInterval(60 * 60); // Update once an hour #endif + if(config.network.rsyslog_server[0]) { + LOG_INFO("Starting Syslog client\n"); + // Defaults + int serverPort = 514; + const char *serverAddr = moduleConfig.mqtt.address; + String server = String(serverAddr); + int delimIndex = server.indexOf(':'); + if (delimIndex > 0) { + String port = server.substring(delimIndex + 1, server.length()); + server[delimIndex] = 0; + serverPort = port.toInt(); + serverAddr = server.c_str(); + } + syslog.server(serverAddr, serverPort); + syslog.deviceHostname(WiFi.getHostname()); + syslog.appName("Meshtastic"); + syslog.defaultPriority(LOGLEVEL_USER); + syslog.enable(); + } + initWebServer(); initApiServer(); @@ -225,6 +248,7 @@ static void WiFiEvent(WiFiEvent_t event) break; case ARDUINO_EVENT_WIFI_STA_STOP: LOG_INFO("WiFi station stopped\n"); + syslog.disable(); break; case ARDUINO_EVENT_WIFI_STA_CONNECTED: LOG_INFO("Connected to access point\n"); @@ -232,6 +256,7 @@ static void WiFiEvent(WiFiEvent_t event) case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: LOG_INFO("Disconnected from WiFi access point\n"); WiFi.disconnect(false, true); + syslog.disable(); needReconnect = true; wifiReconnect->setIntervalFromNow(1000); break; @@ -248,6 +273,7 @@ static void WiFiEvent(WiFiEvent_t event) case ARDUINO_EVENT_WIFI_STA_LOST_IP: LOG_INFO("Lost IP address and IP address is reset to 0\n"); WiFi.disconnect(false, true); + syslog.disable(); needReconnect = true; wifiReconnect->setIntervalFromNow(1000); break;