#pragma once /* ABSTRACT DEVICE COMPONENTS LIBRARY */ #include #include #include #include #include #include #if __has_include() // POSIX #define FORK_EXISTS 1 #include #include #include #endif #include "../common/adc_utils.h" #include "adc_net_concepts.h" namespace adc { /* SOME USEFULL PRIVITIVES */ // A generic implementation of POSIX OS daemon class AdcPosixGenericDaemon { public: virtual ~AdcPosixGenericDaemon() = default; // run server as daemon (still only on POSIX OSes) void daemonize() { daemonizePrepare(); // reference implementation of forking for POSIX OSes #ifdef FORK_EXISTS // get TEMP directory in OS auto tmp_path = std::filesystem::temp_directory_path(); if (tmp_path.empty()) { tmp_path = std::filesystem::current_path().root_path(); } // _ioContext.notify_fork(asio::execution_context::fork_prepare); if (pid_t pid = fork()) { if (pid > 0) { exit(0); } else { throw std::system_error(errno, std::generic_category(), "CANNOT FORK 1-STAGE"); } } if (setsid() == -1) { throw std::system_error(errno, std::generic_category(), "CANNOT FORK SETSID"); } std::filesystem::current_path(tmp_path); umask(0); if (pid_t pid = fork()) { if (pid > 0) { exit(0); } else { throw std::system_error(errno, std::generic_category(), "CANNOT FORK 2-STAGE"); } } close(0); close(1); close(2); // _ioContext.notify_fork(asio::io_context::fork_child); #endif daemonizeFinalize(); } protected: virtual void daemonizePrepare() = 0; virtual void daemonizeFinalize() = 0; }; // a basic network session manager (basic start and stop functionality) class AdcNetSessionManager { public: virtual ~AdcNetSessionManager() = default; protected: AdcNetSessionManager() = default; AdcNetSessionManager(const AdcNetSessionManager&) = delete; AdcNetSessionManager(AdcNetSessionManager&& other) { moveInstFunc(&other, this); } AdcNetSessionManager& operator=(const AdcNetSessionManager&) = delete; AdcNetSessionManager& operator=(AdcNetSessionManager&& other) { moveInstFunc(&other, this); return *this; }; template constexpr static bool anySessionPredicate(const typename SessionT::netsession_ident_t&) { return true; } // started sessions weak pointers template static inline std::unordered_map>> _serverSessions{}; std::vector> _stopSessionFunc; std::vector> _moveCtorFunc; template void startSession(std::shared_ptr& sess_ptr) { auto it = _serverSessions[this].end(); it = _serverSessions[this].emplace(it, sess_ptr); sess_ptr->start(); _stopSessionFunc.emplace_back([it](const AdcNetSessionManager* inst) { if (!it->expired()) { // session is still existing auto sess = it->lock(); sess->stop(); _serverSessions[inst].erase(it); return true; } else { return false; } }); // define move-function only once per SessionT! if (_serverSessions[this].size() == 1) { _moveCtorFunc.emplace_back( [](const AdcNetSessionManager* new_instance, const AdcNetSessionManager* from_inst) { _serverSessions[new_instance] = std::move(_serverSessions[from_inst]); }); } } template PredT = decltype(anySessionPredicate)> size_t stopSessions(PredT&& comp_func = anySessionPredicate()) { size_t N = 0; std::set> remove_wptr; for (auto& wptr : _serverSessions[this]) { if (std::shared_ptr sptr = wptr.lock()) { if constexpr (std::same_as)>) { sptr->stop(); remove_wptr.emplace(wptr); ++N; } else { if (std::forward(comp_func)(sptr->ident())) { sptr->stop(); remove_wptr.emplace(wptr); ++N; } } } else { // remove already stopped sessions?!! remove_wptr.emplace(wptr); } } for (auto& wptr : remove_wptr) { _serverSessions[this].erase(wptr); } return N; } size_t stopAllSessions() { size_t N = 0; for (auto& func : _stopSessionFunc) { func(this) ? ++N : 0; } _stopSessionFunc.clear(); _moveCtorFunc.clear(); // there are nothing to move after stopping of all sessions return N; } void moveInstFunc(const AdcNetSessionManager* to, const AdcNetSessionManager* from) { if (from != to) { for (auto& func : _moveCtorFunc) { func(to, from); } _stopSessionFunc = std::move(from->_stopSessionFunc); _moveCtorFunc = std::move(from->_moveCtorFunc); } } }; static_assert(interfaces::adc_logger_c>, "!!!!!"); /* very generic network server */ template > class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager, public LoggerT { public: typedef IdentT server_ident_t; typedef LoggerT logger_t; template AdcGenericNetServer(const server_ident_t& id, LoggerCtorArgTs&&... ctor_args) : _serverIdent(id), LoggerT(std::forward(ctor_args)...) { if constexpr (traits::formattable) { logInfo("Create ADC generic network server with ID: {} (addr = {}, thread = {})", id, (void*)this, utils::AdcThisThreadId()); } else { logInfo("Create ADC generic network server (addr = {}, thread = {})", (void*)this, utils::AdcThisThreadId()); } } AdcGenericNetServer(const AdcGenericNetServer&) = delete; AdcGenericNetServer(AdcGenericNetServer&& other) : AdcPosixGenericDaemon(std::move(other)), AdcNetSessionManager(std::move(other)), LoggerT(std::move(other)) { logDebug("Move ADC server class: this = {}, target = {}", (void*)this, (void*)&other); if (this == &other) { return; } _serverIdent = std::move(other._serverIdent); _stopListenFunc = std::move(other._stopListenFunc); for (auto& func : other._moveCtorFunc) { func(&other, this); } _moveCtorFunc = std::move(other._moveCtorFunc); } virtual ~AdcGenericNetServer() { if constexpr (traits::formattable) { logInfo("Delete ADC generic network server with ID: {} (addr = {}, thread = {})", _serverIdent, (void*)this, utils::AdcThisThreadId()); } else { logInfo("Delete ADC generic network server (addr = {}, thread = {})", (void*)this, utils::AdcThisThreadId()); } }; AdcGenericNetServer& operator=(const AdcGenericNetServer&) = delete; AdcGenericNetServer& operator=(AdcGenericNetServer&& other) { logDebug("Assign-move ADC server class: this = {}, target = {}", (void*)this, (void*)&other); if (this != &other) { AdcPosixGenericDaemon::operator=(std::move(other)); AdcNetSessionManager::operator=(std::move(other)); _serverIdent = std::move(other._serverIdent); _stopListenFunc = std::move(other._stopListenFunc); for (auto& func : other._moveCtorFunc) { func(&other, this); } _moveCtorFunc = std::move(other._moveCtorFunc); } return *this; } virtual server_ident_t ident() const { return _serverIdent; } // start accepting remote connections, create and start given network session // It must be assumed that this is asynchronous operation!!! template void start(SessionT::netsession_ident_t id, SessionT::netsession_ctx_t sess_ctx, AcceptorCtorArgTs&&... ctor_args) requires traits::adc_hashable_c { logDebug("Call {}", __PRETTY_FUNCTION__); if (!_isListening[this][id]) { auto acceptor = std::make_shared( std::forward(ctor_args)...); _stopListenFunc.emplace_back([acceptor, id](const AdcGenericNetServer* inst) { std::error_code ec; acceptor->close(ec); _isListening[inst][id] = false; }); if constexpr (traits::formattable) { logInfo( "Start listening for client connections at <{}> endpoint (session ID: {}, server addr = {}, thread " "= {})", acceptor->localEndpoint(), id, (void*)this, utils::AdcThisThreadId()); } else { logInfo("Start listening for client connections at <{}> endpoint (server addr = {}, thread = {})", acceptor->localEndpoint(), (void*)this, utils::AdcThisThreadId()); } doAccept(acceptor, std::move(id), std::move(sess_ctx)); } // only once per SessionT if (_isListening[this].size() == 1) { _moveCtorFunc.emplace_back( [](const AdcGenericNetServer* new_instance, const AdcGenericNetServer* from_inst) { _isListening[new_instance] = std::move(_isListening[from_inst]); }); } }; template bool isListening(const typename SessionT::netsession_ident_t& id) const { return _isListening[this][id]; } virtual void start() = 0; virtual void stop() { for (auto& func : _stopListenFunc) { func(this); } _stopListenFunc.clear(); stopAllSessions(); }; // helper methods for logging template void logMessage(LoggerT::loglevel_t level, std::format_string fmt, Ts&&... args) { LoggerT::logMessage(level, std::format(fmt, std::forward(args)...)); } template void logInfo(std::format_string fmt, Ts&&... args) { LoggerT::logInfo(std::format(fmt, std::forward(args)...)); } template void logDebug(std::format_string fmt, Ts&&... args) { LoggerT::logDebug(std::format(fmt, std::forward(args)...)); } template void logError(std::format_string fmt, Ts&&... args) { LoggerT::logError(std::format(fmt, std::forward(args)...)); } protected: // template // inline static std::unordered_map _isListening{}; template inline static std::unordered_map> _isListening{}; std::vector> _stopListenFunc; std::vector> _moveCtorFunc; server_ident_t _serverIdent; template void doAccept(std::shared_ptr acceptor, IDT id, CTXT sess_ctx) { acceptor->asyncAccept([acceptor, id = std::move(id), sess_ctx = std::move(sess_ctx), this]( auto ec, typename SessionT::netservice_t srv) mutable { if (!ec) { logInfo( "Client connection is succesfully accepted! Client endpoint: {} (server addr = {}, thread = {})", srv.remoteEndpoint(), (void*)this, utils::AdcThisThreadId()); auto sess = std::make_shared(id, std::move(srv), sess_ctx); startSession(sess); _isListening[this][id] = true; doAccept(acceptor, std::move(id), std::move(sess_ctx)); } else { this->logError("Cannot start accepting connection: {}", SessionT::netservice_t::formattableError(ec)); _isListening[this][id] = false; } }); } }; namespace interfaces { template concept adc_generic_netserver_c = requires { typename T::server_ident_t; requires std::derived_from>; }; } // namespace interfaces } // namespace adc