mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralfms.git
synced 2025-10-30 18:27:54 +00:00
837 lines
28 KiB
C++
837 lines
28 KiB
C++
//
|
|
// Created by stephane bourque on 2022-10-26.
|
|
//
|
|
|
|
#include "Poco/AsyncChannel.h"
|
|
#include "Poco/ConsoleChannel.h"
|
|
#include "Poco/FileChannel.h"
|
|
#include "Poco/FormattingChannel.h"
|
|
#include "Poco/JSON/JSONException.h"
|
|
#include "Poco/Net/FTPSStreamFactory.h"
|
|
#include "Poco/Net/FTPStreamFactory.h"
|
|
#include "Poco/Net/HTTPSStreamFactory.h"
|
|
#include "Poco/Net/HTTPStreamFactory.h"
|
|
#include "Poco/Net/SSLManager.h"
|
|
#include "Poco/NullChannel.h"
|
|
#include "Poco/PatternFormatter.h"
|
|
#include "Poco/SplitterChannel.h"
|
|
|
|
#include "framework/ALBserver.h"
|
|
#include "framework/AuthClient.h"
|
|
#include "framework/KafkaManager.h"
|
|
#include "framework/MicroService.h"
|
|
#include "framework/MicroServiceErrorHandler.h"
|
|
#include "framework/MicroServiceNames.h"
|
|
#include "framework/RESTAPI_ExtServer.h"
|
|
#include "framework/RESTAPI_GenericServerAccounting.h"
|
|
#include "framework/RESTAPI_IntServer.h"
|
|
#include "framework/UI_WebSocketClientServer.h"
|
|
#include "framework/WebSocketLogger.h"
|
|
#include "framework/utils.h"
|
|
|
|
#ifdef USE_MEDUSA_CLIENT
|
|
#include <medusa/MedusaClient.h>
|
|
#endif
|
|
|
|
namespace OpenWifi {
|
|
|
|
static std::string MakeServiceListString(const Types::MicroServiceMetaMap &Services) {
|
|
std::string SvcList;
|
|
for (const auto &Svc : Services) {
|
|
if (SvcList.empty())
|
|
SvcList = Svc.second.Type;
|
|
else
|
|
SvcList += ", " + Svc.second.Type;
|
|
}
|
|
return SvcList;
|
|
}
|
|
|
|
void MicroService::BusMessageReceived([[maybe_unused]] const std::string &Key,
|
|
const std::string &Payload) {
|
|
std::lock_guard G(InfraMutex_);
|
|
|
|
Poco::Logger &BusLogger = EventBusManager()->Logger();
|
|
|
|
try {
|
|
Poco::JSON::Parser P;
|
|
auto Object = P.parse(Payload).extract<Poco::JSON::Object::Ptr>();
|
|
|
|
if (Object->has(KafkaTopics::ServiceEvents::Fields::ID) &&
|
|
Object->has(KafkaTopics::ServiceEvents::Fields::EVENT)) {
|
|
uint64_t ID = Object->get(KafkaTopics::ServiceEvents::Fields::ID);
|
|
auto Event = Object->get(KafkaTopics::ServiceEvents::Fields::EVENT).toString();
|
|
if (ID != ID_) {
|
|
if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN ||
|
|
Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE ||
|
|
Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) {
|
|
if (Object->has(KafkaTopics::ServiceEvents::Fields::TYPE) &&
|
|
Object->has(KafkaTopics::ServiceEvents::Fields::PUBLIC) &&
|
|
Object->has(KafkaTopics::ServiceEvents::Fields::PRIVATE) &&
|
|
Object->has(KafkaTopics::ServiceEvents::Fields::VRSN) &&
|
|
Object->has(KafkaTopics::ServiceEvents::Fields::KEY)) {
|
|
auto PrivateEndPoint =
|
|
Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString();
|
|
if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) {
|
|
Services_.erase(PrivateEndPoint);
|
|
poco_information(
|
|
BusLogger,
|
|
fmt::format(
|
|
"Service {} ID={} leaving system.",
|
|
Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE)
|
|
.toString(),
|
|
ID));
|
|
} else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN ||
|
|
Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) {
|
|
auto ServiceInfo = Types::MicroServiceMeta{
|
|
.Id = ID,
|
|
.Type = Poco::toLower(
|
|
Object->get(KafkaTopics::ServiceEvents::Fields::TYPE)
|
|
.toString()),
|
|
.PrivateEndPoint =
|
|
Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE)
|
|
.toString(),
|
|
.PublicEndPoint =
|
|
Object->get(KafkaTopics::ServiceEvents::Fields::PUBLIC)
|
|
.toString(),
|
|
.AccessKey =
|
|
Object->get(KafkaTopics::ServiceEvents::Fields::KEY)
|
|
.toString(),
|
|
.Version = Object->get(KafkaTopics::ServiceEvents::Fields::VRSN)
|
|
.toString(),
|
|
.LastUpdate = Utils::Now()};
|
|
|
|
auto s1 = MakeServiceListString(Services_);
|
|
auto PreviousSize = Services_.size();
|
|
Services_[PrivateEndPoint] = ServiceInfo;
|
|
auto CurrentSize = Services_.size();
|
|
if(Event == KafkaTopics::ServiceEvents::EVENT_JOIN) {
|
|
if(!s1.empty()) {
|
|
poco_information(
|
|
BusLogger,
|
|
fmt::format(
|
|
"Service {} ID={} is joining the system.",
|
|
Object
|
|
->get(
|
|
KafkaTopics::ServiceEvents::Fields::PRIVATE)
|
|
.toString(),
|
|
ID));
|
|
}
|
|
std::string SvcList;
|
|
for (const auto &Svc : Services_) {
|
|
if (SvcList.empty())
|
|
SvcList = Svc.second.Type;
|
|
else
|
|
SvcList += ", " + Svc.second.Type;
|
|
}
|
|
poco_information(
|
|
BusLogger,
|
|
fmt::format("Current list of microservices: {}", SvcList));
|
|
} else if(CurrentSize!=PreviousSize) {
|
|
poco_information(
|
|
BusLogger,
|
|
fmt::format(
|
|
"Service {} ID={} is being added back in.",
|
|
Object
|
|
->get(KafkaTopics::ServiceEvents::Fields::PRIVATE)
|
|
.toString(),
|
|
ID));
|
|
}
|
|
}
|
|
} else {
|
|
poco_information(
|
|
BusLogger,
|
|
fmt::format("KAFKA-MSG: invalid event '{}', missing a field.",
|
|
Event));
|
|
}
|
|
} else if (Event == KafkaTopics::ServiceEvents::EVENT_REMOVE_TOKEN) {
|
|
if (Object->has(KafkaTopics::ServiceEvents::Fields::TOKEN)) {
|
|
#ifndef TIP_SECURITY_SERVICE
|
|
AuthClient()->RemovedCachedToken(
|
|
Object->get(KafkaTopics::ServiceEvents::Fields::TOKEN).toString());
|
|
#endif
|
|
} else {
|
|
poco_information(
|
|
BusLogger,
|
|
fmt::format("KAFKA-MSG: invalid event '{}', missing token", Event));
|
|
}
|
|
} else {
|
|
poco_information(BusLogger,
|
|
fmt::format("Unknown Event: {} Source: {}", Event, ID));
|
|
}
|
|
}
|
|
} else {
|
|
std::ostringstream os;
|
|
Object->stringify(std::cout);
|
|
poco_error(BusLogger, fmt::format("Bad bus message: {}", os.str()));
|
|
}
|
|
|
|
auto ServiceHint = Services_.begin();
|
|
auto now = Utils::Now();
|
|
auto si1 = Services_.size();
|
|
auto ss1 = MakeServiceListString(Services_);
|
|
while(ServiceHint!=Services_.end()) {
|
|
if ((now - ServiceHint->second.LastUpdate) > 120) {
|
|
poco_information(BusLogger, fmt::format("ZombieService: Removing service {}, ", ServiceHint->second.PublicEndPoint));
|
|
ServiceHint = Services_.erase(ServiceHint);
|
|
} else
|
|
++ServiceHint;
|
|
}
|
|
if(Services_.size() != si1) {
|
|
auto ss2 = MakeServiceListString(Services_);
|
|
poco_information(BusLogger, fmt::format("Current list of microservices: {} -> {}", ss1, ss2));
|
|
}
|
|
|
|
} catch (const Poco::Exception &E) {
|
|
BusLogger.log(E);
|
|
}
|
|
}
|
|
|
|
Types::MicroServiceMetaVec MicroService::GetServices(const std::string &Type) {
|
|
std::lock_guard G(InfraMutex_);
|
|
|
|
auto T = Poco::toLower(Type);
|
|
Types::MicroServiceMetaVec Res;
|
|
for (const auto &[_, ServiceRec] : Services_) {
|
|
if (ServiceRec.Type == T)
|
|
Res.push_back(ServiceRec);
|
|
}
|
|
return Res;
|
|
}
|
|
|
|
Types::MicroServiceMetaVec MicroService::GetServices() {
|
|
std::lock_guard G(InfraMutex_);
|
|
|
|
Types::MicroServiceMetaVec Res;
|
|
for (const auto &[_, ServiceRec] : Services_) {
|
|
Res.push_back(ServiceRec);
|
|
}
|
|
return Res;
|
|
|
|
}
|
|
|
|
void MicroService::LoadConfigurationFile() {
|
|
if(ConfigContent_.empty()) {
|
|
std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR, ".");
|
|
ConfigFileName_ =
|
|
ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : ConfigFileName_;
|
|
Poco::Path ConfigFile(ConfigFileName_);
|
|
|
|
if (!ConfigFile.isFile()) {
|
|
std::cerr << DAEMON_APP_NAME << ": Configuration " << ConfigFile.toString()
|
|
<< " does not seem to exist. Please set " + DAEMON_CONFIG_ENV_VAR +
|
|
" env variable the path of the " + DAEMON_PROPERTIES_FILENAME +
|
|
" file."
|
|
<< std::endl;
|
|
std::exit(Poco::Util::Application::EXIT_CONFIG);
|
|
}
|
|
PropConfigurationFile_ = new Poco::Util::PropertyFileConfiguration(ConfigFile.toString());
|
|
} else {
|
|
std::istringstream is(ConfigContent_);
|
|
PropConfigurationFile_ = new Poco::Util::PropertyFileConfiguration(is);
|
|
}
|
|
configPtr()->addWriteable(PropConfigurationFile_, PRIO_DEFAULT);
|
|
}
|
|
|
|
void MicroService::Reload() {
|
|
LoadConfigurationFile();
|
|
LoadMyConfig();
|
|
}
|
|
|
|
void MicroService::LoadMyConfig() {
|
|
NoAPISecurity_ = ConfigGetBool("openwifi.security.restapi.disable", false);
|
|
std::string KeyFile = ConfigPath("openwifi.service.key", "");
|
|
if (!KeyFile.empty()) {
|
|
std::string KeyFilePassword = ConfigPath("openwifi.service.key.password", "");
|
|
AppKey_ = Poco::SharedPtr<Poco::Crypto::RSAKey>(
|
|
new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword));
|
|
Cipher_ = CipherFactory_.createCipher(*AppKey_);
|
|
Signer_.setRSAKey(AppKey_);
|
|
Signer_.addAllAlgorithms();
|
|
NoBuiltInCrypto_ = false;
|
|
} else {
|
|
NoBuiltInCrypto_ = true;
|
|
}
|
|
|
|
ID_ = Utils::GetSystemId();
|
|
if (!DebugMode_)
|
|
DebugMode_ = ConfigGetBool("openwifi.system.debug", false);
|
|
MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private");
|
|
MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public");
|
|
UIURI_ = ConfigGetString("openwifi.system.uri.ui");
|
|
MyHash_ = Utils::ComputeHash(MyPublicEndPoint_);
|
|
}
|
|
|
|
void MicroService::InitializeLoggingSystem() {
|
|
static auto initialized = false;
|
|
|
|
if (!initialized) {
|
|
initialized = true;
|
|
LoadConfigurationFile();
|
|
|
|
auto LoggingDestination =
|
|
MicroService::instance().ConfigGetString("logging.type", "file");
|
|
auto LoggingFormat = MicroService::instance().ConfigGetString(
|
|
"logging.format", "%Y-%m-%d %H:%M:%S.%i %s: [%p][thr:%I] %t");
|
|
auto UseAsyncLogs_ = MicroService::instance().ConfigGetBool("logging.asynch", true);
|
|
auto DisableWebSocketLogging =
|
|
MicroService::instance().ConfigGetBool("logging.websocket", false);
|
|
|
|
if (LoggingDestination == "null") {
|
|
Poco::AutoPtr<Poco::NullChannel> DevNull(new Poco::NullChannel);
|
|
Poco::Logger::root().setChannel(DevNull);
|
|
} else if (LoggingDestination == "console") {
|
|
SetConsoleLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat);
|
|
} else if (LoggingDestination == "colorconsole") {
|
|
SetColorConsoleLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat);
|
|
} else if (LoggingDestination == "sql") {
|
|
SetSQLLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat);
|
|
} else if (LoggingDestination == "syslog") {
|
|
SetSyslogLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat);
|
|
} else {
|
|
SetFileLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat,
|
|
DAEMON_ROOT_ENV_VAR);
|
|
}
|
|
|
|
auto Level = Poco::Logger::parseLevel(
|
|
MicroService::instance().ConfigGetString("logging.level", "debug"));
|
|
Poco::Logger::root().setLevel(Level);
|
|
if (!DisableWebSocketLogging) {
|
|
static const UI_WebSocketClientServer::NotificationTypeIdVec Notifications = {
|
|
{1, "log"}};
|
|
UI_WebSocketClientServer()->RegisterNotifications(Notifications);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MicroService::SetConsoleLogs(bool UseAsync, bool DisableWebSocketLogging,
|
|
const std::string &FormatterPattern) {
|
|
|
|
Poco::AutoPtr<Poco::ConsoleChannel> Console(new Poco::ConsoleChannel);
|
|
Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter);
|
|
Formatter->setProperty("pattern", FormatterPattern);
|
|
Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(
|
|
new Poco::FormattingChannel(Formatter, Console));
|
|
|
|
if (DisableWebSocketLogging) {
|
|
if (UseAsync) {
|
|
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel));
|
|
Poco::Logger::root().setChannel(Async);
|
|
} else {
|
|
Poco::Logger::root().setChannel(FormattingChannel);
|
|
}
|
|
} else {
|
|
Poco::AutoPtr<WebSocketLogger> WSLogger(new WebSocketLogger);
|
|
Poco::AutoPtr<Poco::SplitterChannel> Splitter(new Poco::SplitterChannel);
|
|
Splitter->addChannel(WSLogger);
|
|
Splitter->addChannel(FormattingChannel);
|
|
if (UseAsync) {
|
|
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter));
|
|
Poco::Logger::root().setChannel(Async);
|
|
} else {
|
|
Poco::Logger::root().setChannel(Splitter);
|
|
}
|
|
}
|
|
Poco::Logger::root().information(fmt::format("Enabled console logs: asynch={} websocket={}",
|
|
UseAsync, DisableWebSocketLogging));
|
|
}
|
|
|
|
void MicroService::SetColorConsoleLogs(bool UseAsync, bool DisableWebSocketLogging,
|
|
const std::string &FormatterPattern) {
|
|
|
|
Poco::AutoPtr<Poco::ColorConsoleChannel> Console(new Poco::ColorConsoleChannel);
|
|
Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter);
|
|
Formatter->setProperty("pattern", FormatterPattern);
|
|
Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(
|
|
new Poco::FormattingChannel(Formatter, Console));
|
|
|
|
if (DisableWebSocketLogging) {
|
|
if (UseAsync) {
|
|
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel));
|
|
Poco::Logger::root().setChannel(Async);
|
|
} else {
|
|
Poco::Logger::root().setChannel(FormattingChannel);
|
|
}
|
|
} else {
|
|
Poco::AutoPtr<WebSocketLogger> WSLogger(new WebSocketLogger);
|
|
Poco::AutoPtr<Poco::SplitterChannel> Splitter(new Poco::SplitterChannel);
|
|
Splitter->addChannel(WSLogger);
|
|
Splitter->addChannel(FormattingChannel);
|
|
if (UseAsync) {
|
|
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter));
|
|
Poco::Logger::root().setChannel(Async);
|
|
} else {
|
|
Poco::Logger::root().setChannel(Splitter);
|
|
}
|
|
}
|
|
Poco::Logger::root().information(
|
|
fmt::format("Enabled color console logs: asynch={} websocket={}", UseAsync,
|
|
DisableWebSocketLogging));
|
|
}
|
|
|
|
void MicroService::SetSQLLogs([[maybe_unused]] bool UseAsync,
|
|
[[maybe_unused]] bool DisableWebSocketLogging,
|
|
[[maybe_unused]] const std::string &FormatterPattern) {
|
|
//"CREATE TABLE T_POCO_LOG (Source VARCHAR, Name VARCHAR, ProcessId INTEGER, Thread VARCHAR,
|
|
//ThreadId INTEGER, Priority INTEGER, Text VARCHAR, DateTime DATE)"
|
|
}
|
|
|
|
void MicroService::SetSyslogLogs([[maybe_unused]] bool UseAsync,
|
|
[[maybe_unused]] bool DisableWebSocketLogging,
|
|
[[maybe_unused]] const std::string &FormatterPattern) {}
|
|
|
|
void MicroService::SetFileLogs(bool UseAsync, bool DisableWebSocketLogging,
|
|
const std::string &FormatterPattern,
|
|
const std::string &root_env_var) {
|
|
std::string DefaultLogPath = fmt::format("${}/logs", root_env_var);
|
|
auto LoggingLocationDir =
|
|
MicroService::instance().ConfigPath("logging.path", DefaultLogPath);
|
|
Poco::File LD(LoggingLocationDir);
|
|
try {
|
|
if (!LD.exists()) {
|
|
LD.createDirectory();
|
|
}
|
|
} catch (const Poco::Exception &E) {
|
|
std::cout << "Cannot create " << LD.path() << " Error: " << E.message() << std::endl;
|
|
}
|
|
auto LoggingLocationDirFilePattern = LoggingLocationDir + "/log";
|
|
|
|
Poco::AutoPtr<Poco::FileChannel> FileChannel(new Poco::FileChannel);
|
|
FileChannel->setProperty("rotation", "10 M");
|
|
FileChannel->setProperty("archive", "timestamp");
|
|
FileChannel->setProperty("purgeCount", "10");
|
|
FileChannel->setProperty("path", LoggingLocationDirFilePattern);
|
|
|
|
Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter);
|
|
Formatter->setProperty("pattern", FormatterPattern);
|
|
Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(
|
|
new Poco::FormattingChannel(Formatter, FileChannel));
|
|
|
|
if (DisableWebSocketLogging) {
|
|
if (UseAsync) {
|
|
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel));
|
|
Poco::Logger::root().setChannel(Async);
|
|
} else {
|
|
Poco::Logger::root().setChannel(FormattingChannel);
|
|
}
|
|
} else {
|
|
Poco::AutoPtr<WebSocketLogger> WSLogger(new WebSocketLogger);
|
|
Poco::AutoPtr<Poco::SplitterChannel> Splitter(new Poco::SplitterChannel);
|
|
Splitter->addChannel(WSLogger);
|
|
Splitter->addChannel(FormattingChannel);
|
|
if (UseAsync) {
|
|
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter));
|
|
Poco::Logger::root().setChannel(Async);
|
|
} else {
|
|
Poco::Logger::root().setChannel(Splitter);
|
|
}
|
|
}
|
|
Poco::Logger::root().information(fmt::format("Enabled file logs: asynch={} websocket={}",
|
|
UseAsync, DisableWebSocketLogging));
|
|
}
|
|
|
|
void DaemonPostInitialization(Poco::Util::Application &self);
|
|
|
|
void MicroService::StartEverything(Poco::Util::Application &self) {
|
|
LoadConfigurationFile();
|
|
InitializeLoggingSystem();
|
|
|
|
static bool InitializedBaseService=false;
|
|
if(!InitializedBaseService) {
|
|
InitializedBaseService = true;
|
|
SubSystems_.push_back(KafkaManager());
|
|
SubSystems_.push_back(ALBHealthCheckServer());
|
|
SubSystems_.push_back(RESTAPI_ExtServer());
|
|
SubSystems_.push_back(RESTAPI_IntServer());
|
|
#ifndef TIP_SECURITY_SERVICE
|
|
SubSystems_.push_back(AuthClient());
|
|
#endif
|
|
|
|
Poco::Net::initializeSSL();
|
|
Poco::Net::HTTPStreamFactory::registerFactory();
|
|
Poco::Net::HTTPSStreamFactory::registerFactory();
|
|
Poco::Net::FTPStreamFactory::registerFactory();
|
|
Poco::Net::FTPSStreamFactory::registerFactory();
|
|
}
|
|
|
|
Poco::File DataDir(ConfigPath("openwifi.system.data"));
|
|
DataDir_ = DataDir.path();
|
|
if (!DataDir.exists()) {
|
|
try {
|
|
DataDir.createDirectory();
|
|
} catch (const Poco::Exception &E) {
|
|
Logger_.log(E);
|
|
}
|
|
}
|
|
WWWAssetsDir_ = ConfigPath("openwifi.restapi.wwwassets", "");
|
|
if (WWWAssetsDir_.empty())
|
|
WWWAssetsDir_ = DataDir_;
|
|
|
|
LoadMyConfig();
|
|
|
|
AllowExternalMicroServices_ = ConfigGetBool("allowexternalmicroservices", true);
|
|
|
|
InitializeSubSystemServers();
|
|
ServerApplication::initialize(self);
|
|
DaemonPostInitialization(self);
|
|
|
|
Types::TopicNotifyFunction F = [this](const std::string &Key, const std::string &Payload) {
|
|
this->BusMessageReceived(Key, Payload);
|
|
};
|
|
KafkaManager()->RegisterTopicWatcher(KafkaTopics::SERVICE_EVENTS, F);
|
|
}
|
|
|
|
void MicroService::initialize([[maybe_unused]] Poco::Util::Application &self) {
|
|
#ifndef USE_MEDUSA_CLIENT
|
|
StartEverything(self);
|
|
#endif
|
|
}
|
|
|
|
void MicroService::uninitialize() {
|
|
// add your own uninitialization code here
|
|
ServerApplication::uninitialize();
|
|
}
|
|
|
|
void MicroService::reinitialize(Poco::Util::Application &self) {
|
|
ServerApplication::reinitialize(self);
|
|
// add your own reinitialization code here
|
|
}
|
|
|
|
void MicroService::defineOptions(Poco::Util::OptionSet &options) {
|
|
ServerApplication::defineOptions(options);
|
|
|
|
options.addOption(
|
|
Poco::Util::Option("help", "", "display help information on command line arguments")
|
|
.required(false)
|
|
.repeatable(false)
|
|
.callback(
|
|
Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleHelp)));
|
|
|
|
options.addOption(Poco::Util::Option("file", "", "specify the configuration file")
|
|
.required(false)
|
|
.repeatable(false)
|
|
.argument("file")
|
|
.callback(Poco::Util::OptionCallback<MicroService>(
|
|
this, &MicroService::handleConfig)));
|
|
|
|
options.addOption(Poco::Util::Option("debug", "", "to run in debug, set to true")
|
|
.required(false)
|
|
.repeatable(false)
|
|
.callback(Poco::Util::OptionCallback<MicroService>(
|
|
this, &MicroService::handleDebug)));
|
|
|
|
options.addOption(
|
|
Poco::Util::Option("logs", "", "specify the log directory and file (i.e. dir/file.log)")
|
|
.required(false)
|
|
.repeatable(false)
|
|
.argument("dir")
|
|
.callback(
|
|
Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleLogs)));
|
|
|
|
options.addOption(Poco::Util::Option("version", "", "get the version and quit.")
|
|
.required(false)
|
|
.repeatable(false)
|
|
.callback(Poco::Util::OptionCallback<MicroService>(
|
|
this, &MicroService::handleVersion)));
|
|
}
|
|
|
|
void MicroService::handleHelp([[maybe_unused]] const std::string &name,
|
|
[[maybe_unused]] const std::string &value) {
|
|
HelpRequested_ = true;
|
|
displayHelp();
|
|
stopOptionsProcessing();
|
|
}
|
|
|
|
void MicroService::handleVersion([[maybe_unused]] const std::string &name,
|
|
[[maybe_unused]] const std::string &value) {
|
|
HelpRequested_ = true;
|
|
std::cout << Version() << std::endl;
|
|
stopOptionsProcessing();
|
|
}
|
|
|
|
void MicroService::handleDebug([[maybe_unused]] const std::string &name,
|
|
const std::string &value) {
|
|
if (value == "true")
|
|
DebugMode_ = true;
|
|
}
|
|
|
|
void MicroService::handleLogs([[maybe_unused]] const std::string &name,
|
|
const std::string &value) {
|
|
LogDir_ = value;
|
|
}
|
|
|
|
void MicroService::handleConfig([[maybe_unused]] const std::string &name,
|
|
const std::string &value) {
|
|
ConfigFileName_ = value;
|
|
}
|
|
|
|
void MicroService::displayHelp() {
|
|
Poco::Util::HelpFormatter helpFormatter(options());
|
|
helpFormatter.setCommand(commandName());
|
|
helpFormatter.setUsage("OPTIONS");
|
|
helpFormatter.setHeader("A " + DAEMON_APP_NAME + " implementation for TIP.");
|
|
helpFormatter.format(std::cout);
|
|
}
|
|
|
|
void MicroService::InitializeSubSystemServers() {
|
|
for (auto i : SubSystems_) {
|
|
addSubsystem(i);
|
|
}
|
|
}
|
|
|
|
void MicroService::StartSubSystemServers() {
|
|
AddActivity("Starting");
|
|
for (auto i : SubSystems_) {
|
|
i->Start();
|
|
}
|
|
EventBusManager()->Start();
|
|
}
|
|
|
|
void MicroService::StopSubSystemServers() {
|
|
AddActivity("Stopping");
|
|
EventBusManager()->Stop();
|
|
for (auto i = SubSystems_.rbegin(); i != SubSystems_.rend(); ++i) {
|
|
(*i)->Stop();
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] std::string MicroService::CreateUUID() {
|
|
static std::random_device rd;
|
|
static std::mt19937_64 gen(rd());
|
|
static std::uniform_int_distribution<> dis(0, 15);
|
|
static std::uniform_int_distribution<> dis2(8, 11);
|
|
|
|
std::stringstream ss;
|
|
int i;
|
|
ss << std::hex;
|
|
for (i = 0; i < 8; i++) {
|
|
ss << dis(gen);
|
|
}
|
|
ss << "-";
|
|
for (i = 0; i < 4; i++) {
|
|
ss << dis(gen);
|
|
}
|
|
ss << "-4";
|
|
for (i = 0; i < 3; i++) {
|
|
ss << dis(gen);
|
|
}
|
|
ss << "-";
|
|
ss << dis2(gen);
|
|
for (i = 0; i < 3; i++) {
|
|
ss << dis(gen);
|
|
}
|
|
ss << "-";
|
|
for (i = 0; i < 12; i++) {
|
|
ss << dis(gen);
|
|
};
|
|
return ss.str();
|
|
}
|
|
|
|
bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem,
|
|
const std::string &Level) {
|
|
try {
|
|
auto P = Poco::Logger::parseLevel(Level);
|
|
auto Sub = Poco::toLower(SubSystem);
|
|
|
|
if (Sub == "all") {
|
|
for (auto i : SubSystems_) {
|
|
i->Logger().setLevel(P);
|
|
}
|
|
return true;
|
|
} else {
|
|
for (auto i : SubSystems_) {
|
|
if (Sub == Poco::toLower(i->Name())) {
|
|
i->Logger().setLevel(P);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} catch (const Poco::Exception &E) {
|
|
std::cerr << "Exception" << std::endl;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MicroService::Reload(const std::string &Sub) {
|
|
for (auto i : SubSystems_) {
|
|
if (Poco::toLower(Sub) == Poco::toLower(i->Name())) {
|
|
i->reinitialize(Poco::Util::Application::instance());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Types::StringVec MicroService::GetSubSystems() const {
|
|
Types::StringVec Result;
|
|
for (auto i : SubSystems_)
|
|
Result.push_back(Poco::toLower(i->Name()));
|
|
return Result;
|
|
}
|
|
|
|
Types::StringPairVec MicroService::GetLogLevels() {
|
|
Types::StringPairVec Result;
|
|
|
|
for (auto &i : SubSystems_) {
|
|
auto P = std::make_pair(i->Name(), Utils::LogLevelToString(i->GetLoggingLevel()));
|
|
Result.push_back(P);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
const Types::StringVec &MicroService::GetLogLevelNames() {
|
|
static Types::StringVec LevelNames{"none", "fatal", "critical", "error", "warning",
|
|
"notice", "information", "debug", "trace"};
|
|
return LevelNames;
|
|
}
|
|
|
|
uint64_t MicroService::ConfigGetInt(const std::string &Key, uint64_t Default) {
|
|
return (uint64_t)config().getInt64(Key, Default);
|
|
}
|
|
|
|
uint64_t MicroService::ConfigGetInt(const std::string &Key) { return config().getInt(Key); }
|
|
|
|
uint64_t MicroService::ConfigGetBool(const std::string &Key, bool Default) {
|
|
return config().getBool(Key, Default);
|
|
}
|
|
|
|
uint64_t MicroService::ConfigGetBool(const std::string &Key) { return config().getBool(Key); }
|
|
|
|
std::string MicroService::ConfigGetString(const std::string &Key, const std::string &Default) {
|
|
return config().getString(Key, Default);
|
|
}
|
|
|
|
std::string MicroService::ConfigGetString(const std::string &Key) {
|
|
return config().getString(Key);
|
|
}
|
|
|
|
std::string MicroService::ConfigPath(const std::string &Key, const std::string &Default) {
|
|
std::string R = config().getString(Key, Default);
|
|
return Poco::Path::expand(R);
|
|
}
|
|
|
|
std::string MicroService::ConfigPath(const std::string &Key) {
|
|
std::string R = config().getString(Key);
|
|
return Poco::Path::expand(R);
|
|
}
|
|
|
|
std::string MicroService::Encrypt(const std::string &S) {
|
|
if (NoBuiltInCrypto_) {
|
|
return S;
|
|
}
|
|
return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);
|
|
;
|
|
}
|
|
|
|
std::string MicroService::Decrypt(const std::string &S) {
|
|
if (NoBuiltInCrypto_) {
|
|
return S;
|
|
}
|
|
return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);
|
|
;
|
|
}
|
|
|
|
std::string MicroService::MakeSystemEventMessage(const std::string &Type) const {
|
|
Poco::JSON::Object Obj;
|
|
Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT, Type);
|
|
Obj.set(KafkaTopics::ServiceEvents::Fields::ID, ID_);
|
|
Obj.set(KafkaTopics::ServiceEvents::Fields::TYPE, Poco::toLower(DAEMON_APP_NAME));
|
|
Obj.set(KafkaTopics::ServiceEvents::Fields::PUBLIC, MyPublicEndPoint_);
|
|
Obj.set(KafkaTopics::ServiceEvents::Fields::PRIVATE, MyPrivateEndPoint_);
|
|
Obj.set(KafkaTopics::ServiceEvents::Fields::KEY, MyHash_);
|
|
Obj.set(KafkaTopics::ServiceEvents::Fields::VRSN, Version_);
|
|
std::stringstream ResultText;
|
|
Poco::JSON::Stringifier::stringify(Obj, ResultText);
|
|
return ResultText.str();
|
|
}
|
|
|
|
[[nodiscard]] bool MicroService::IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) {
|
|
try {
|
|
auto APIKEY = Request.get("X-API-KEY");
|
|
return APIKEY == MyHash_;
|
|
} catch (const Poco::Exception &E) {
|
|
Logger_.log(E);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MicroService::SavePID() {
|
|
try {
|
|
std::ofstream O;
|
|
O.open(MicroService::instance().DataDir() + "/pidfile",
|
|
std::ios::binary | std::ios::trunc);
|
|
O << Poco::Process::id();
|
|
O.close();
|
|
} catch (...) {
|
|
std::cout << "Could not save system ID" << std::endl;
|
|
}
|
|
}
|
|
|
|
int MicroService::main([[maybe_unused]] const ArgVec &args) {
|
|
MicroServiceErrorHandler ErrorHandler(*this);
|
|
Poco::ErrorHandler::set(&ErrorHandler);
|
|
|
|
Args_ = args;
|
|
|
|
if (!HelpRequested_) {
|
|
SavePID();
|
|
|
|
Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME);
|
|
logger.notice(fmt::format("Starting {} version {}.", DAEMON_APP_NAME, Version()));
|
|
|
|
if (Poco::Net::Socket::supportsIPv6())
|
|
poco_information(logger, "System supports IPv6.");
|
|
else
|
|
poco_information(logger, "System does NOT support IPv6.");
|
|
|
|
if (config().getBool("application.runAsDaemon", false)) {
|
|
poco_information(logger, "Starting as a daemon.");
|
|
}
|
|
|
|
#ifdef USE_MEDUSA_CLIENT
|
|
MedusaClient::instance()->SetSubSystems(SubSystems_);
|
|
MedusaClient::instance()->Start();
|
|
waitForTerminationRequest();
|
|
MedusaClient::instance()->Stop();
|
|
#else
|
|
poco_information(logger, fmt::format("System ID set to {}", ID_));
|
|
StartSubSystemServers();
|
|
waitForTerminationRequest();
|
|
StopSubSystemServers();
|
|
logger.notice(fmt::format("Stopped {}...", DAEMON_APP_NAME));
|
|
#endif
|
|
}
|
|
|
|
return Application::EXIT_OK;
|
|
}
|
|
|
|
void MicroService::AddActivity(const std::string &Activity) {
|
|
if (!DataDir_.empty()) {
|
|
std::string ActivityFile{DataDir_ + "/activity.log"};
|
|
try {
|
|
std::ofstream of(ActivityFile, std::ios_base::app | std::ios_base::out);
|
|
auto t = std::chrono::system_clock::now();
|
|
std::time_t now = std::chrono::system_clock::to_time_t(t);
|
|
of << Activity << " at " << std::ctime(&now);
|
|
} catch (...) {
|
|
}
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] std::string MicroService::Sign(Poco::JWT::Token &T, const std::string &Algo) {
|
|
if (NoBuiltInCrypto_) {
|
|
return T.toString();
|
|
} else {
|
|
return Signer_.sign(T, Algo);
|
|
}
|
|
}
|
|
|
|
void MicroService::DeleteOverrideConfiguration() {
|
|
Poco::File F(DataDir_ + ExtraConfigurationFilename);
|
|
|
|
try {
|
|
if (F.exists())
|
|
F.remove();
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
} // namespace OpenWifi
|