30#include <flow/util/util.hpp>
32#include <boost/interprocess/sync/named_mutex.hpp>
33#include <boost/interprocess/sync/scoped_lock.hpp>
34#include <boost/move/make_unique.hpp>
35#include <boost/thread/condition_variable.hpp>
163template<
typename Session_server_t,
typename Server_session_t>
165 public flow::log::Log_context,
166 private boost::noncopyable
181 using Channels =
typename Server_session_obj::Channels;
217 template<
typename Per_app_setup_func>
222 Error_code* err_code, Per_app_setup_func&& per_app_setup_func);
255 template<
typename Task_err,
256 typename N_init_channels_by_srv_req_func,
typename Mdt_load_func>
261 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func,
262 Mdt_load_func&& mdt_load_func,
263 Task_err&& on_done_func);
345 template<
typename Task>
395 using Mutex = flow::util::Mutex_non_recursive;
501#define TEMPLATE_SESSION_SERVER_IMPL \
502 template<typename Session_server_t, typename Server_session_t>
504#define CLASS_SESSION_SERVER_IMPL \
505 Session_server_impl<Session_server_t, Server_session_t>
508template<
typename Per_app_setup_func>
509CLASS_SESSION_SERVER_IMPL::Session_server_impl
512 Per_app_setup_func&& per_app_setup_func) :
514 flow::log::Log_context(logger_ptr,
Log_component::S_SESSION),
515 m_srv_app_ref(srv_app_ref_arg),
516 m_mq_msg_size_limit(0),
517 m_this_session_srv(this_session_srv_arg),
518 m_cli_app_master_set_ref(cli_app_master_set_ref),
519 m_per_app_setup_func(std::move(per_app_setup_func)),
520 m_state(std::in_place)
524 using flow::error::Runtime_error;
525 using flow::async::reset_this_thread_pinning;
526 using flow::log::Sev;
527 using boost::movelib::make_unique;
528 using boost::system::system_category;
529 using boost::io::ios_all_saver;
532 using Named_sh_mutex = boost::interprocess::named_mutex;
533 using Named_sh_mutex_ptr = boost::movelib::unique_ptr<Named_sh_mutex>;
534 using Sh_lock_guard = boost::interprocess::scoped_lock<Named_sh_mutex>;
547 const auto name_bad = [](
const string& name) ->
bool
556 for (
const auto& cli_app_name_and_obj : cli_app_master_set_ref)
558 if (name_bad(cli_app_name_and_obj.first))
560 found_bad_name =
true;
561 bad_name = cli_app_name_and_obj.first;
568 FLOW_LOG_WARNING(
"Session-server [" << *
this <<
"]: An App name -- [" << bad_name <<
"] -- is "
570 "this violates the App::m_name contract and would corrupt the conventional "
571 "shared-resource naming scheme. Emitting error.");
579 m_state->m_incomplete_session_graveyard
580 = boost::movelib::make_unique<flow::async::Single_thread_task_loop>
585 flow::util::ostream_op_string(
"SSvG-",
this));
617 if constexpr(Server_session_obj::S_MQS_ENABLED)
622 util::remove_each_persistent_with_name_prefix<Blob_stream_mq_base<Mq>>
706 const auto own_creds = Process_credentials::own_process_credentials();
709 FLOW_LOG_WARNING(
"Session acceptor [" << *
this <<
"]: Creation underway. However, just before writing "
710 "CNS (Current Namespace Store), a/k/a PID file, we determined that "
711 "the `user` aspect of our effective credentials [" << own_creds <<
"] does not match "
712 "the hard-configured value passed to this ctor: "
714 "We cannot proceed, as this would violate the security/safety model of ipc::session. "
721 else if (
const auto own_invoked_as = own_creds.process_invoked_as(&our_err_code);
726 FLOW_LOG_WARNING(
"Session acceptor [" << *
this <<
"]: Creation underway. However, just before writing "
727 "CNS (Current Namespace Store), a/k/a PID file, we failed to query our own "
728 "executable-invoked-as (binary name) value "
729 "([" << our_err_code <<
"] [" << our_err_code.message() <<
"]). Emitting error.");
733 FLOW_LOG_WARNING(
"Session acceptor [" << *
this <<
"]: Creation underway. However, just before writing "
734 "CNS (Current Namespace Store), a/k/a PID file, we determined that we were invoked as "
735 "[" << own_invoked_as <<
"], which does not *exactly* match the hard-configured "
737 "Every opposing Client_session would refuse to open a session with us. Emitting error.");
743 const auto mutex_name = empty_session_base->cur_ns_store_mutex_absolute_name();
745 const auto cns_path = empty_session_base->cur_ns_store_absolute_path();
749 const auto logger_ptr = get_logger();
750 if (logger_ptr && logger_ptr->should_log(Sev::S_INFO, get_log_component()))
752 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
753 FLOW_LOG_INFO_WITHOUT_CHECKING
754 (
"Session acceptor [" << *
this <<
"]: Created. Writing CNS (Current Namespace Store), a/k/a PID "
755 "file [" << cns_path <<
"] (perms "
756 "[" << std::setfill(
'0')
758 << std::oct << cns_perms.get_permissions() <<
"], "
759 "shared-mutex name [" << mutex_name <<
"], shared-mutex perms "
761 << mutex_perms.get_permissions() <<
"]); "
762 "then listening for incoming master socket stream "
763 "connects (through Native_socket_stream_acceptor that was just cted) to address "
764 "based partially on the namespace (PID) written to that file.");
769 Named_sh_mutex_ptr sh_mutex;
771 "Session_server_impl::ctor:named-mutex-open-or-create", [&]()
773 sh_mutex = make_unique<Named_sh_mutex>(
util::OPEN_OR_CREATE, mutex_name.native_str(), mutex_perms);
783 Sh_lock_guard sh_lock{*sh_mutex};
792 const bool we_created_cns = !fs::exists(cns_path, dummy);
793 ofstream cns_file{cns_path};
811 if ((!our_err_code) && we_created_cns)
822 cns_file << empty_session_base->srv_namespace().str() <<
'\n';
824 if (!cns_file.good())
826 const auto sys_err_code = our_err_code = {errno, system_category()};
827 FLOW_LOG_WARNING(
"Session acceptor [" << *
this <<
"]: Could not open or write CNS (PID) "
828 "file [" << cns_path <<
"]; system error details follow.");
829 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
845 *err_code = our_err_code;
849 throw Runtime_error{our_err_code, FLOW_UTIL_WHERE_AM_I_STR()};
855 m_state->m_master_sock_acceptor
856 = make_unique<transport::Native_socket_stream_acceptor>
858 empty_session_base->session_master_socket_stream_acceptor_absolute_name(),
862 if ((!err_code) || (!*err_code))
864 m_state->m_incomplete_session_graveyard->start(reset_this_thread_pinning);
871CLASS_SESSION_SERVER_IMPL::~Session_server_impl()
877 assert(m_state->m_stopping &&
"Terminal Session_server sub-class dtor must call dtor_stop_accepting() first.");
878 dtor_stop_accepting();
885 if (!m_deinit_func_or_empty.empty())
887 FLOW_LOG_INFO(
"Session acceptor [" << *
this <<
"]: Continuing shutdown. A sub-class desires final de-init "
888 "work, once everything else is stopped (might be persistent resource cleanup); invoking "
890 m_deinit_func_or_empty();
891 FLOW_LOG_TRACE(
"De-init work finished.");
896void CLASS_SESSION_SERVER_IMPL::dtor_stop_accepting()
900 if (m_state->m_stopping)
906 FLOW_LOG_INFO(
"Session acceptor [" << *
this <<
"]: Shutting down. The Native_socket_stream_acceptor will now "
907 "shut down, and all outstanding Native_socket_stream_acceptor handlers and Server_session "
908 "handlers shall fire with operation-aborted error codes.");
914 m_state->m_master_sock_acceptor.reset();
926 Lock_guard incomplete_sessions_lock{m_state->m_mutex};
927 m_state->m_stopping =
true;
928 while (m_state->m_n_handlers_active != 0)
930 m_state->m_handlers_done.wait(incomplete_sessions_lock);
940 for (
const auto& incomplete_session : m_state->m_incomplete_sessions)
949 m_state->m_incomplete_session_graveyard.reset();
953template<
typename Task_err,
954 typename N_init_channels_by_srv_req_func,
typename Mdt_load_func>
959 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func,
960 Mdt_load_func&& mdt_load_func,
961 Task_err&& on_done_handler)
964 using flow::async::Task_asio_err;
965 using boost::make_shared;
968 assert(target_session);
969 assert(m_state->m_master_sock_acceptor &&
"By contract do not invoke async_accept() if ctor failed.");
988 Task_asio_err on_done_func{std::move(on_done_handler)};
990 const auto mq_msg_size_limit_snapshot = m_mq_msg_size_limit;
991 auto sock_stm = make_shared<transport::sync_io::Native_socket_stream>();
992 const auto sock_stm_raw = sock_stm.get();
999 struct Log_in_out_args
1001 Channels m_init_channels_by_srv_req;
1003 Channels m_init_channels_by_cli_req;
1005 const auto log_in_out_args = make_shared<Log_in_out_args>();
1007 FLOW_LOG_INFO(
"Session acceptor [" << *
this <<
"]: Async-accept request: Immediately issuing socket stream "
1008 "acceptor async-accept as step 1. If that async-succeeds, we will complete the login asynchronously; "
1009 "if that succeeds we will finally emit the ready-to-go session to user via handler.");
1010 m_state->m_master_sock_acceptor->async_accept
1012 [
this, target_session, mq_msg_size_limit_snapshot, init_channels_by_srv_req, mdt_from_cli_or_null,
1013 init_channels_by_cli_req, log_in_out_args,
1014 n_init_channels_by_srv_req_func = std::move(n_init_channels_by_srv_req_func),
1015 mdt_load_func = std::move(mdt_load_func),
1016 sock_stm = std::move(sock_stm),
1017 on_done_func = std::move(on_done_func)]
1051 FLOW_LOG_INFO(
"Session acceptor [" << *
this <<
"]: Async-accept request: Socket stream "
1052 "acceptor async-accept succeeded resulting in socket stream [" << *sock_stm <<
"]; that was step "
1053 "1. Now we create the server session and have it undergo async-login; if that succeeds we will "
1054 "finally emit the ready-to-go session to user via handler.");
1061 std::move(*sock_stm)));
1068 Lock_guard incomplete_sessions_lock{m_state->m_mutex};
1069 m_state->m_incomplete_sessions.insert(incomplete_session);
1075 const auto cli_it = m_cli_app_master_set_ref.find(
string{cli_app_name});
1076 return (cli_it == m_cli_app_master_set_ref.end()) ?
nullptr : &cli_it->second;
1092 return m_per_app_setup_func(cli_app);
1111 mq_msg_size_limit_snapshot,
1113 init_channels_by_srv_req ? &log_in_out_args->m_init_channels_by_srv_req :
nullptr,
1114 mdt_from_cli_or_null ? &log_in_out_args->m_mdt_from_cli :
nullptr,
1115 init_channels_by_cli_req ? &log_in_out_args->m_init_channels_by_cli_req :
nullptr,
1117 std::move(cli_app_lookup_func),
1118 std::move(cli_namespace_func),
1119 std::move(pre_rsp_setup_func),
1121 std::move(n_init_channels_by_srv_req_func),
1122 std::move(mdt_load_func),
1125 target_session, init_channels_by_srv_req, mdt_from_cli_or_null, init_channels_by_cli_req, log_in_out_args,
1126 on_done_func = std::move(on_done_func)]
1133 auto incomplete_session = incomplete_session_observer.lock();
1134 assert(incomplete_session &&
"m_incomplete_sessions holds it until this handler, or shutdown, is done.");
1141 Lock_guard incomplete_sessions_lock{m_state->m_mutex};
1142 if (!(shutting_down = m_state->m_stopping))
1145 const bool erased_ok = 1 ==
1147 m_state->m_incomplete_sessions.erase(incomplete_session);
1148 assert(erased_ok &&
"Who else would have erased it?!");
1151 ++m_state->m_n_handlers_active;
1174 m_state->m_incomplete_session_graveyard->post([incomplete_session = std::move(incomplete_session)]
1178 incomplete_session.reset();
1181 assert((!incomplete_session) &&
"It should've been nullified by being moved-from into the captures.");
1186 on_done_func(async_err_code);
1190 assert(incomplete_session);
1193 FLOW_LOG_INFO(
"Session acceptor [" << *
this <<
"]: Async-accept request: Successfully resulted in logged-in "
1194 "server session [" << *incomplete_session <<
"]. Feeding to user via callback.");
1197 if (init_channels_by_srv_req)
1199 *init_channels_by_srv_req = std::move(log_in_out_args->m_init_channels_by_srv_req);
1201 if (mdt_from_cli_or_null)
1203 *mdt_from_cli_or_null = std::move(log_in_out_args->m_mdt_from_cli);
1205 if (init_channels_by_cli_req)
1207 *init_channels_by_cli_req = std::move(log_in_out_args->m_init_channels_by_cli_req);
1209 *target_session = std::move(*incomplete_session);
1212 on_done_func(async_err_code);
1213 FLOW_LOG_TRACE(
"Handler finished.");
1221 Lock_guard incomplete_sessions_lock{m_state->m_mutex};
1222 if (--m_state->m_n_handlers_active == 0)
1224 m_state->m_handlers_done.notify_all();
1231size_t CLASS_SESSION_SERVER_IMPL::mq_msg_size_limit()
const
1233 static_assert(Server_session_obj::S_MQS_ENABLED,
1234 "Nonsensical to call this for a Session_server concrete type that is not MQ-enabled.");
1236 return m_mq_msg_size_limit;
1240void CLASS_SESSION_SERVER_IMPL::mq_msg_size_limit(
size_t limit)
1242 static_assert(Server_session_obj::S_MQS_ENABLED,
1243 "Nonsensical to call this for a Session_server concrete type that is not MQ-enabled.");
1245 const auto prev_limit = m_mq_msg_size_limit;
1246 m_mq_msg_size_limit = flow::util::round_to_multiple(limit, flow::util::max_align_sz());
1250 FLOW_LOG_INFO(
"MQ msg-size limit changed via user call: [" << prev_limit <<
"] => round-up[" << limit <<
"] = "
1251 "[" << m_mq_msg_size_limit <<
"]; 0 = we choose based on best struc::Channel perf. "
1252 "This affects sessions from subsequent async_accept()s; existing ones keep their values.");
1256void CLASS_SESSION_SERVER_IMPL::to_ostream(std::ostream* os)
const
1258 *os <<
'[' << m_srv_app_ref <<
"]@" <<
static_cast<const void*
>(
this);
1262typename CLASS_SESSION_SERVER_IMPL::Session_server_obj* CLASS_SESSION_SERVER_IMPL::this_session_srv()
1264 return m_this_session_srv;
1268template<
typename Task>
1269void CLASS_SESSION_SERVER_IMPL::sub_class_set_deinit_func(
Task&& task)
1271 m_deinit_func_or_empty = std::move(task);
1278 val.to_ostream(&os);
1282#undef CLASS_SESSION_SERVER_IMPL
1283#undef TEMPLATE_SESSION_SERVER_IMPL
Internal class template comprising API/logic common to every Session_server variant,...
Server_session_t Server_session_obj
Useful short-hand for the concrete Server_session type emitted by async_accept().
size_t m_mq_msg_size_limit
See mq_msg_size_limit(); starts at zero but can be overridden to another value and/or back to zero vi...
boost::shared_ptr< Server_session_obj > Incomplete_session
Internally used ref-counted handle to a Server_session_obj, suitable for capturing and passing around...
void to_ostream(std::ostream *os) const
See Session_server method.
void sub_class_set_deinit_func(Task &&task)
Utility for sub-classes: ensures that task() is invoked near the end of *this dtor's execution,...
void mq_msg_size_limit(size_t limit)
See Session_server method.
Session_server_t Session_server_obj
See this_session_srv().
boost::unordered_set< Incomplete_session > Incomplete_sessions
Short-hand for set of Incomplete_session, with fast insertion and removal by key Incomplete_session i...
flow::util::Lock_guard< Mutex > Lock_guard
Short-hand for Mutex lock.
void async_accept(Server_session_obj *target_session, Channels *init_channels_by_srv_req, Mdt_reader_ptr *mdt_from_cli_or_null, Channels *init_channels_by_cli_req, N_init_channels_by_srv_req_func &&n_init_channels_by_srv_req_func, Mdt_load_func &&mdt_load_func, Task_err &&on_done_func)
See Session_server method.
void dtor_stop_accepting()
Synchronously stops all async_accept() activity, the post-condition being that no internal handler – ...
const Client_app::Master_set & m_cli_app_master_set_ref
See ctor.
const Server_app & m_srv_app_ref
See Session_server public data member.
typename Server_session_obj::Mdt_reader_ptr Mdt_reader_ptr
Short-hand for Session_mv::Mdt_reader_ptr.
Function< void()> m_deinit_func_or_empty
See sub_class_set_deinit_func(). .empty() unless that was called at least once.
typename Server_session_obj::Channels Channels
Short-hand for Session_mv::Channels.
Session_server_impl(flow::log::Logger *logger_ptr, Session_server_obj *this_session_srv_arg, const Server_app &srv_app_ref, const Client_app::Master_set &cli_app_master_set_ref, Error_code *err_code, Per_app_setup_func &&per_app_setup_func)
See Session_server ctor; it does that.
Session_server_obj *const m_this_session_srv
See this_session_srv().
boost::weak_ptr< Server_session_obj > Incomplete_session_observer
weak_ptr observer of an Incomplete_session.
std::optional< State > m_state
See State.
size_t mq_msg_size_limit() const
See Session_server method.
Session_server_obj * this_session_srv()
Returns pointer to the object that is privately sub-classing us.
const Function< Error_code(const Client_app &client_app)> m_per_app_setup_func
See ctor.
~Session_server_impl()
See Session_server dtor.
flow::util::Mutex_non_recursive Mutex
Short-hand for State::m_mutex type.
To be instantiated typically once in a given process, an object of this type asynchronously listens f...
Base of Blob_stream_mq_sender and Blob_stream_mq_receiver containing certain static facilities,...
Implements both sync_io::Native_handle_sender and sync_io::Native_handle_receiver concepts by using a...
A process's credentials (PID, UID, GID as of this writing).
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
static Shared_name ct(const Source &src)
Copy-constructs from a char-sequence container such as string or util::String_view.
static constexpr char S_SEPARATOR
Character we use, by convention, to separate conceptual folders within str().
@ S_RESOURCE_OWNER_UNEXPECTED
A resource in the file system (file, SHM pool, MQ, etc.) has or could have unexpected owner; ipc::ses...
@ S_MUTEX_BIPC_MISC_LIBRARY_ERROR
Low-level boost.ipc.mutex: boost.interprocess emitted miscellaneous library exception sans a system c...
@ S_SERVER_APP_EXEC_PATH_INCONSISTENT
Session-server creation: this process's executable path, as invoked, does not match the registered va...
@ S_INVALID_ARGUMENT
User called an API with 1 or more arguments against the API spec.
@ S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER
Async completion handler is being called prematurely, because underlying object is shutting down,...
Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...
Shared_name build_conventional_shared_name_prefix(const Shared_name &resource_type, const Shared_name &srv_app_name)
Return the prefix common to all calls to either build_conventional_shared_name() overload with the ar...
void ensure_resource_owner_is_app(flow::log::Logger *logger_ptr, const fs::path &path, const App &app, Error_code *err_code)
Utility, used internally but exposed in public API in case it is of general use, that checks that the...
Shared_name next_cli_namespace()
Returns a client-namespace value (see Session_base::cli_namespace()) not previously returned in this ...
std::ostream & operator<<(std::ostream &os, const Msg_batch_in< Mutable_buffer_sequence_t, Msg_resource_t > &val)
Serializes a Msg_batch_in to a standard output stream.
@ S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER
Async completion handler is being called prematurely, because underlying object is shutting down,...
flow::async::Task Task
Short-hand for polymorphic function (a-la std::function<>) that takes no arguments and returns nothin...
const Open_or_create OPEN_OR_CREATE
Tag value indicating an open-if-exists-else-create operation.
const boost::array< Permissions, size_t(Permissions_level::S_END_SENTINEL)> PRODUCER_CONSUMER_RESOURCE_PERMISSIONS_LVL_MAP
Maps general Permissions_level specifier to low-level Permissions value, when the underlying resource...
void op_with_possible_bipc_exception(flow::log::Logger *logger_ptr, Error_code *err_code, const Error_code &misc_bipc_lib_error, String_view context, const Func &func)
Internal (to ipc) utility that invokes the given function that invokes a boost.interprocess operation...
Permissions shared_resource_permissions(Permissions_level permissions_lvl)
Maps general Permissions_level specifier to low-level Permissions value, when the underlying resource...
void set_resource_permissions(flow::log::Logger *logger_ptr, const fs::path &path, const Permissions &perms, Error_code *err_code)
Utility that sets the permissions of the given resource (at the supplied file system path) to specifi...
flow::util::String_view String_view
Short-hand for Flow's String_view.
Log_component
The flow::log::Component payload enumeration containing various log components used by Flow-IPC inter...
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
flow::Function< Signature > Function
Short-hand for polymorphic functor holder which is very common. This is essentially std::function.
#define TEMPLATE_SESSION_SERVER_IMPL
Internally used macro; public API users should disregard (same deal as in struc/channel....
#define CLASS_SESSION_SERVER_IMPL
Internally used macro; public API users should disregard (same deal as in struc/channel....
util::group_id_t m_group_id
The application must run as this group ID (GID). Files and other shared resources shall have this own...
std::string m_name
Brief application name, readable to humans and unique across all other applications' names; used both...
util::user_id_t m_user_id
The application must run as this user ID (UID). Files and other shared resources shall have this owne...
fs::path m_exec_path
Absolute, lexically normalized canonical path to the executable entity (which is not a directory),...
An App that is used as a client in at least one client-server IPC split.
boost::unordered_map< std::string, Client_app > Master_set
Type for storing master repository of all Client_apps. See App doc header for discussion.
An App that is used as a server in at least one client-server IPC split.
util::Permissions_level m_permissions_level_for_client_apps
Specifies level of access for Client_apps (which must, also, be in m_allowed_client_apps at any rate)...
This is the friend facade of Server_session or any more-advanced (e.g., SHM-capable) variant thereof ...
const Session_base_obj & base() const
See Base counterpart.
typename Base::Session_base_obj Session_base_obj
See Base counterpart.
static auto ct_base(Ctor_args &&... ctor_args) -> Base
Forwards to Base ctor(s).
void async_accept_log_in(Args &&... args)
See Base counterpart.
All internal mutable state of Session_server_impl.
boost::condition_variable m_handlers_done
Notified whenever m_n_handlers_active reaches 0. Used with m_mutex.
bool m_stopping
false until dtor_stop_accepting() fences off the log-in handlers from m_incomplete_sessions; true the...
boost::movelib::unique_ptr< transport::Native_socket_stream_acceptor > m_master_sock_acceptor
transport::Native_socket_stream acceptor avail throughout *this to accept init please-open-session re...
Mutex m_mutex
Protects m_incomplete_sessions, m_stopping, m_n_handlers_active.
Incomplete_sessions m_incomplete_sessions
The set of all Incomplete_session objects such that each one comes from a distinct async_accept() req...
unsigned int m_n_handlers_active
The number of log-in handlers (threads Ws) that have passed the m_stopping check (it being false) and...
boost::movelib::unique_ptr< flow::async::Single_thread_task_loop > m_incomplete_session_graveyard
Mostly-idle thread that solely destroys objects removed from m_incomplete_sessions in the case where ...