#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 { class AdcNetServer { protected: public: typedef std::string server_ident_t; virtual ~AdcNetServer() = default; virtual server_ident_t serverIdent() 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 stop() { stopAllSessions(); }; // run server as daemon (still only on POSIX OSes) virtual 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: server_ident_t _serverIdent; // started sessions weak pointers template static std::unordered_map> _serverSessions; std::vector> _stopSessionFunc; template void startSession(const typename SessionT::shared_ptr_t& 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(); } }); } } void stopAllSessions() { for (auto& func : _stopSessionFunc) { func(); } } virtual void daemonizePrepare() = 0; virtual void daemonizeFinalize() = 0; }; } // namespace adc