#pragma once /* ABSTRACT DEVICE COMPONENTS LIBRARY */ #include #include #include #include #include #if __has_include() // POSIX #define FORK_EXISTS 1 #include #include #include #endif #include "adc_net_concepts.h" namespace adc { /* SOME USEFULL PRIVITIVES */ // Ageneric implementation pf 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: template constexpr static bool anySessionPredicate(const typename SessionT::netsession_ident_t&) { return true; } // started sessions weak pointers template static std::unordered_map>> _serverSessions; std::vector> _stopSessionFunc; template void startSession(std::shared_ptr& sess_ptr) { auto res = _serverSessions[this].emplace(sess_ptr); if (res.second) { sess_ptr.start(); _stopSessionFunc.emplace_back([res]() { if (!res.first.expired()) { // session is still existing auto sess = res.first.lock(); sess->stop(); return true; } else { return false; } }); } } template PredT = decltype(anySessionPredicate)> size_t stopSessions(PredT&& comp_func = anySessionPredicate()) { size_t N = 0; for (auto& wptr : _serverSessions[this]) { if (std::shared_ptr sptr = wptr.lock()) { if constexpr (std::same_as)>) { sptr->stop(); ++N; } else { if (std::forward(comp_func)(sptr->ident())) { sptr->stop(); ++N; } } } } return N; } size_t stopAllSessions() { size_t N = 0; for (auto& func : _stopSessionFunc) { func() ? ++N : 0; } _stopSessionFunc.clear(); return N; } }; /* very generic network server */ class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager { protected: public: typedef std::string server_ident_t; virtual ~AdcGenericNetServer() = default; 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(const typename SessionT::netsession_ident_t& id, const typename SessionT::netsession_ctx_t& sess_ctx, AcceptorCtorArgTs&&... ctor_args) { if (!_isListening[this]) { auto acceptor = std::make_shared( std::forward(ctor_args)...); _stopListenFunc.emplace_back([acceptor, id, this]() { std::error_code ec; acceptor->close(ec); _isListening[this][id] = false; }); doAccept(acceptor, id, sess_ctx); } }; 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(); } _stopListenFunc.clear(); stopAllSessions(); }; protected: // template // inline static std::unordered_map _isListening{}; template inline static std::unordered_map> _isListening{}; std::vector> _stopListenFunc; server_ident_t _serverIdent; template void doAccept(std::shared_ptr acceptor, const IDT& id, const CTXT& sess_ctx) { acceptor->asyncAccept([acceptor, &id, &sess_ctx, this](auto ec, typename SessionT::netservice_t srv) mutable { if (!ec) { auto sess = std::make_shared(id, std::move(srv), sess_ctx); startSession(sess); _isListening[this][id] = true; doAccept(acceptor, id, sess_ctx); } else { _isListening[this][id] = false; } }); } }; } // namespace adc