mirror of
				https://github.com/Telecominfraproject/wlan-cloud-owprov.git
				synced 2025-10-30 18:18:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			848 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			848 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::StopEverything([[maybe_unused]] Poco::Util::Application &self) {
 | |
|         LoadConfigurationFile();
 | |
|         InitializeLoggingSystem();
 | |
| 
 | |
|         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
 | 
