#pragma once /* ABSTRACT DEVICE COMPONENTS LIBRARY */ #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; } 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; } template void start(const typename SessionT::netservice_t::endpoint_t& endpoint, const typename SessionT::netsession_ident_t& id, typename SessionT::netsession_ctx_t&& sess_ctx, NetsrvCtorArgTs&&... ctor_args) { typename SessionT::netservice_t netservice(std::forward(ctor_args)...); netservice.asyncAccept(endpoint, [&endpoint, &id, sess_ctx, this](auto ec, auto...) { if (!ec) { auto sess = std::make_shared(id, std::forward(sess_ctx)); startSession(sess); start(endpoint, id, sess_ctx); } }); }; virtual void start() = 0; virtual void stop() { stopAllSessions(); }; protected: server_ident_t _serverIdent; }; } // namespace adc