Merge c159cc2b78 into dc5eb96f50
This commit is contained in:
commit
6f02bdcd16
@ -11,6 +11,8 @@
|
||||
#define WIFI_MAX_PASSWORD_STRLEN 64
|
||||
#define WIFI_MAX_HOSTNAME_STRLEN 31
|
||||
|
||||
#define SYSLOG_MAX_HOSTNAME_STRLEN 128
|
||||
|
||||
#define NTP_MAX_SERVER_STRLEN 31
|
||||
#define NTP_MAX_TIMEZONE_STRLEN 50
|
||||
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
|
||||
@ -76,6 +78,12 @@ struct CONFIG_T {
|
||||
bool Enabled;
|
||||
} Mdns;
|
||||
|
||||
struct {
|
||||
bool Enabled;
|
||||
char Hostname[SYSLOG_MAX_HOSTNAME_STRLEN + 1];
|
||||
uint16_t Port;
|
||||
} Syslog;
|
||||
|
||||
struct {
|
||||
char Server[NTP_MAX_SERVER_STRLEN + 1];
|
||||
char Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
|
||||
|
||||
34
include/SyslogLogger.h
Normal file
34
include/SyslogLogger.h
Normal file
@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
#include <WiFiUdp.h>
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
#include <mutex>
|
||||
|
||||
class SyslogLogger {
|
||||
public:
|
||||
SyslogLogger();
|
||||
void init(Scheduler& scheduler);
|
||||
void updateSettings(const String&& hostname);
|
||||
void write(const uint8_t *buffer, size_t size);
|
||||
|
||||
private:
|
||||
void loop();
|
||||
void disable();
|
||||
void enable();
|
||||
bool resolveAndStart();
|
||||
bool isResolved() const {
|
||||
return _address != INADDR_NONE;
|
||||
}
|
||||
|
||||
Task _loopTask;
|
||||
std::mutex _mutex;
|
||||
WiFiUDP _udp;
|
||||
IPAddress _address;
|
||||
String _syslog_hostname;
|
||||
String _proc_id;
|
||||
String _header;
|
||||
uint16_t _port;
|
||||
bool _enabled;
|
||||
};
|
||||
|
||||
extern SyslogLogger Syslog;
|
||||
@ -70,6 +70,8 @@ enum WebApiError {
|
||||
NetworkDns1Invalid,
|
||||
NetworkDns2Invalid,
|
||||
NetworkApTimeoutInvalid,
|
||||
NetworkSyslogHostnameLength,
|
||||
NetworkSyslogPort,
|
||||
|
||||
NtpBase = 9000,
|
||||
NtpServerLength,
|
||||
|
||||
@ -22,6 +22,9 @@
|
||||
|
||||
#define MDNS_ENABLED false
|
||||
|
||||
#define SYSLOG_ENABLED false
|
||||
#define SYSLOG_PORT 514
|
||||
|
||||
#define NTP_SERVER_OLD "pool.ntp.org"
|
||||
#define NTP_SERVER "opendtu.pool.ntp.org"
|
||||
#define NTP_TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||
|
||||
@ -47,6 +47,11 @@ bool ConfigurationClass::write()
|
||||
JsonObject mdns = doc["mdns"].to<JsonObject>();
|
||||
mdns["enabled"] = config.Mdns.Enabled;
|
||||
|
||||
JsonObject syslog = doc["syslog"].to<JsonObject>();
|
||||
syslog["enabled"] = config.Syslog.Enabled;
|
||||
syslog["hostname"] = config.Syslog.Hostname;
|
||||
syslog["port"] = config.Syslog.Port;
|
||||
|
||||
JsonObject ntp = doc["ntp"].to<JsonObject>();
|
||||
ntp["server"] = config.Ntp.Server;
|
||||
ntp["timezone"] = config.Ntp.Timezone;
|
||||
@ -222,6 +227,11 @@ bool ConfigurationClass::read()
|
||||
JsonObject mdns = doc["mdns"];
|
||||
config.Mdns.Enabled = mdns["enabled"] | MDNS_ENABLED;
|
||||
|
||||
JsonObject syslog = doc["syslog"];
|
||||
config.Syslog.Enabled = syslog["enabled"] | SYSLOG_ENABLED;
|
||||
strlcpy(config.Syslog.Hostname, syslog["hostname"] | "", sizeof(config.Syslog.Hostname));
|
||||
config.Syslog.Port = syslog["port"] | SYSLOG_PORT;
|
||||
|
||||
JsonObject ntp = doc["ntp"];
|
||||
strlcpy(config.Ntp.Server, ntp["server"] | NTP_SERVER, sizeof(config.Ntp.Server));
|
||||
strlcpy(config.Ntp.Timezone, ntp["timezone"] | NTP_TIMEZONE, sizeof(config.Ntp.Timezone));
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||
*/
|
||||
#include "MessageOutput.h"
|
||||
#include "SyslogLogger.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
@ -26,6 +27,8 @@ void MessageOutputClass::register_ws_output(AsyncWebSocket* output)
|
||||
|
||||
size_t MessageOutputClass::write(uint8_t c)
|
||||
{
|
||||
Syslog.write(&c, 1);
|
||||
|
||||
if (_buff_pos < BUFFER_SIZE) {
|
||||
std::lock_guard<std::mutex> lock(_msgLock);
|
||||
_buffer[_buff_pos] = c;
|
||||
@ -39,6 +42,8 @@ size_t MessageOutputClass::write(uint8_t c)
|
||||
|
||||
size_t MessageOutputClass::write(const uint8_t* buffer, size_t size)
|
||||
{
|
||||
Syslog.write(buffer, size);
|
||||
|
||||
std::lock_guard<std::mutex> lock(_msgLock);
|
||||
if (_buff_pos + size < BUFFER_SIZE) {
|
||||
memcpy(&_buffer[_buff_pos], buffer, size);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "NetworkSettings.h"
|
||||
#include "Configuration.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "SyslogLogger.h"
|
||||
#include "PinMapping.h"
|
||||
#include "Utils.h"
|
||||
#include "__compiled_constants.h"
|
||||
@ -55,6 +56,8 @@ void NetworkSettingsClass::init(Scheduler& scheduler)
|
||||
|
||||
scheduler.addTask(_loopTask);
|
||||
_loopTask.enable();
|
||||
|
||||
Syslog.init(scheduler);
|
||||
}
|
||||
|
||||
void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t info)
|
||||
@ -295,6 +298,8 @@ void NetworkSettingsClass::applyConfig()
|
||||
}
|
||||
MessageOutput.println("done");
|
||||
setStaticIp();
|
||||
|
||||
Syslog.updateSettings(getHostname());
|
||||
}
|
||||
|
||||
void NetworkSettingsClass::setHostname()
|
||||
|
||||
138
src/SyslogLogger.cpp
Normal file
138
src/SyslogLogger.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||
*/
|
||||
#include <HardwareSerial.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include "defaults.h"
|
||||
#include "SyslogLogger.h"
|
||||
#include "Configuration.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "NetworkSettings.h"
|
||||
|
||||
SyslogLogger::SyslogLogger()
|
||||
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&SyslogLogger::loop, this))
|
||||
{
|
||||
}
|
||||
|
||||
void SyslogLogger::init(Scheduler& scheduler)
|
||||
{
|
||||
// PROCID change indicates a restart.
|
||||
_proc_id = String(esp_random(), HEX);
|
||||
|
||||
scheduler.addTask(_loopTask);
|
||||
_loopTask.enable();
|
||||
}
|
||||
|
||||
void SyslogLogger::updateSettings(const String&& hostname)
|
||||
{
|
||||
auto& config = Configuration.get().Syslog;
|
||||
|
||||
// Disable logger while it is reconfigured.
|
||||
disable();
|
||||
|
||||
if (!config.Enabled) {
|
||||
MessageOutput.println("[SyslogLogger] Syslog not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
_port = config.Port;
|
||||
_syslog_hostname = config.Hostname;
|
||||
if (_syslog_hostname.isEmpty()) {
|
||||
MessageOutput.println("[SyslogLogger] Hostname not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageOutput.printf("[SyslogLogger] Logging to %s!\r\n", _syslog_hostname.c_str());
|
||||
|
||||
_header = "<14>1 - "; // RFC5424: Facility USER, severity INFO, version 1, NIL timestamp.
|
||||
_header += hostname;
|
||||
_header += " OpenDTU ";
|
||||
_header += _proc_id;
|
||||
// NIL values for message id and structured data
|
||||
_header += " - - ";
|
||||
|
||||
// Enable logger.
|
||||
enable();
|
||||
}
|
||||
|
||||
void SyslogLogger::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (!_enabled || !isResolved()) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
uint8_t c = buffer[i];
|
||||
bool overflow = false;
|
||||
if (c != '\r' && c != '\n') {
|
||||
// Replace control and non-ASCII characters with '?'.
|
||||
overflow = !_udp.write(c >= 0x20 && c < 0x7f ? c : '?');
|
||||
}
|
||||
if (c == '\n' || overflow) {
|
||||
_udp.endPacket();
|
||||
_udp.beginPacket(_address, _port);
|
||||
_udp.print(_header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SyslogLogger::disable()
|
||||
{
|
||||
MessageOutput.println("[SyslogLogger] Disable");
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (_enabled) {
|
||||
_enabled = false;
|
||||
_address = INADDR_NONE;
|
||||
_udp.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void SyslogLogger::enable()
|
||||
{
|
||||
// Bind random source port.
|
||||
if (!_udp.begin(0)) {
|
||||
MessageOutput.println("[SyslogLogger] No sockets available");
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
bool SyslogLogger::resolveAndStart()
|
||||
{
|
||||
if (Configuration.get().Mdns.Enabled) {
|
||||
_address = MDNS.queryHost(_syslog_hostname); // INADDR_NONE if failed
|
||||
}
|
||||
if (_address != INADDR_NONE) {
|
||||
if (!_udp.beginPacket(_address, _port)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!_udp.beginPacket(_syslog_hostname.c_str(), _port)) {
|
||||
return false;
|
||||
}
|
||||
_address = _udp.remoteIP(); // Store resolved address.
|
||||
}
|
||||
_udp.print(_header);
|
||||
_udp.print("[SyslogLogger] Logging to ");
|
||||
_udp.print(_syslog_hostname);
|
||||
_udp.endPacket();
|
||||
_udp.beginPacket(_address, _port);
|
||||
_udp.print(_header);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SyslogLogger::loop()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (!_enabled || !NetworkSettings.isConnected() || isResolved()) {
|
||||
return;
|
||||
}
|
||||
if (!resolveAndStart()) {
|
||||
_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
SyslogLogger Syslog;
|
||||
@ -70,6 +70,9 @@ void WebApiNetworkClass::onNetworkAdminGet(AsyncWebServerRequest* request)
|
||||
root["password"] = config.WiFi.Password;
|
||||
root["aptimeout"] = config.WiFi.ApTimeout;
|
||||
root["mdnsenabled"] = config.Mdns.Enabled;
|
||||
root["syslogenabled"] = config.Syslog.Enabled;
|
||||
root["sysloghostname"] = config.Syslog.Hostname;
|
||||
root["syslogport"] = config.Syslog.Port;
|
||||
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
@ -163,6 +166,23 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
if (root["syslogenabled"].as<bool>()) {
|
||||
if (root["sysloghostname"].as<String>().length() == 0 || root["sysloghostname"].as<String>().length() > SYSLOG_MAX_HOSTNAME_STRLEN) {
|
||||
retMsg["message"] = "Syslog Server must between 1 and " STR(SYSLOG_MAX_HOSTNAME_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::NetworkSyslogHostnameLength;
|
||||
retMsg["param"]["max"] = SYSLOG_MAX_HOSTNAME_STRLEN;
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["syslogport"].as<uint>() == 0 || root["syslogport"].as<uint>() > 65535) {
|
||||
retMsg["message"] = "Port must be a number between 1 and 65535!";
|
||||
retMsg["code"] = WebApiError::NetworkSyslogPort;
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CONFIG_T& config = Configuration.get();
|
||||
config.WiFi.Ip[0] = ipaddress[0];
|
||||
@ -195,6 +215,9 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
||||
}
|
||||
config.WiFi.ApTimeout = root["aptimeout"].as<uint>();
|
||||
config.Mdns.Enabled = root["mdnsenabled"].as<bool>();
|
||||
config.Syslog.Enabled = root["syslogenabled"].as<bool>();
|
||||
strlcpy(config.Syslog.Hostname, root["sysloghostname"].as<String>().c_str(), sizeof(config.Syslog.Hostname));
|
||||
config.Syslog.Port = root["syslogport"].as<uint>();
|
||||
|
||||
WebApi.writeConfig(retMsg);
|
||||
|
||||
|
||||
@ -462,7 +462,11 @@
|
||||
"ApTimeoutHint": "Zeit die der AccessPoint offen gehalten wird. Ein Wert von 0 bedeutet unendlich.",
|
||||
"Minutes": "Minuten",
|
||||
"EnableMdns": "mDNS aktivieren",
|
||||
"MdnsSettings": "mDNS-Einstellungen"
|
||||
"MdnsSettings": "mDNS-Einstellungen",
|
||||
"EnableSyslog": "Syslog aktivieren",
|
||||
"SyslogSettings": "Syslog-Einstellungen",
|
||||
"SyslogHostname": "Syslog Server",
|
||||
"SyslogPort": "Port"
|
||||
},
|
||||
"mqttadmin": {
|
||||
"MqttSettings": "MQTT-Einstellungen",
|
||||
|
||||
@ -462,7 +462,11 @@
|
||||
"ApTimeoutHint": "Time which the AccessPoint is kept open. A value of 0 means infinite.",
|
||||
"Minutes": "minutes",
|
||||
"EnableMdns": "Enable mDNS",
|
||||
"MdnsSettings": "mDNS Settings"
|
||||
"MdnsSettings": "mDNS Settings",
|
||||
"EnableSyslog": "Enable Syslog",
|
||||
"SyslogSettings": "Syslog Settings",
|
||||
"SyslogHostname": "Syslog Server",
|
||||
"SyslogPort": "Port"
|
||||
},
|
||||
"mqttadmin": {
|
||||
"MqttSettings": "MQTT Settings",
|
||||
|
||||
@ -10,4 +10,7 @@ export interface NetworkConfig {
|
||||
dns2: string;
|
||||
aptimeout: number;
|
||||
mdnsenabled: boolean;
|
||||
syslogenabled: boolean;
|
||||
sysloghostname: string;
|
||||
syslogport: number;
|
||||
}
|
||||
|
||||
@ -82,6 +82,31 @@
|
||||
/>
|
||||
</CardElement>
|
||||
|
||||
<CardElement :text="$t('networkadmin.SyslogSettings')" textVariant="text-bg-primary" add-space>
|
||||
<InputElement
|
||||
:label="$t('networkadmin.EnableSyslog')"
|
||||
v-model="networkConfigList.syslogenabled"
|
||||
type="checkbox"
|
||||
/>
|
||||
|
||||
<div v-if="networkConfigList.syslogenabled">
|
||||
<InputElement
|
||||
:label="$t('networkadmin.SyslogHostname')"
|
||||
v-model="networkConfigList.sysloghostname"
|
||||
type="text"
|
||||
maxlength="128"
|
||||
/>
|
||||
|
||||
<InputElement
|
||||
:label="$t('networkadmin.SyslogPort')"
|
||||
v-model="networkConfigList.syslogport"
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
/>
|
||||
</div>
|
||||
</CardElement>
|
||||
|
||||
<CardElement :text="$t('networkadmin.AdminAp')" textVariant="text-bg-primary" add-space>
|
||||
<InputElement
|
||||
:label="$t('networkadmin.ApTimeout')"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user