Flow-IPC 1.0.0
Flow-IPC project: Full implementation reference.
app.hpp
Go to the documentation of this file.
1/* Flow-IPC: Sessions
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
21#include "ipc/util/util_fwd.hpp"
22#include <boost/unordered_set.hpp>
23
24namespace ipc::session
25{
26
27// Types.
28
29/**
30 * A *description* of an application in this ipc::session inter-process communication universe. An application is,
31 * at least roughly speaking, 1-1 with a distinct executable presumably interested in communicating with another such
32 * executable. A process is an instance of an application that has begun execution at some point.
33 *
34 * An App is a *description* of an application, and typically used (at least after program initialization, before
35 * actual IPC begins) only as supertype of Client_app and/or Server_app, it is possible that 2+ `App`s exist but
36 * describe one actual application but in different roles. 2+ such `App`s must have all members equal to each
37 * other respectively. In particular see App::m_name.
38 *
39 * This is a data store (and a simple one). The same holds of Client_app and Server_app. As of this writing
40 * there are no rigid rules as to the existence of an App/Client_app/Server_app with this or that stored inside.
41 * Make them all day long, if you want! More restrictions shall come into play only when passing various
42 * App objects and sub-objects (in some form) to ipc::session APIs, most notably the Session concept and buddies.
43 *
44 * However, to be useful, we suggest the following convention. Some key ipc::session APIs may rely on it.
45 *
46 * ### App, Client_app, Server_app registry conventions ###
47 * - Maintain a global (to the applications in your IPC universe) registry of known applications. It could be
48 * stored in JSON or XML form in some central shared software component and read-into a master `set` or
49 * `unordered_set` (by App::m_name; see its doc header) available in a centrally-available library;
50 * or this `*set` could simply be a hard-coded C++ `extern const`; whatever works.
51 * - In the current model supported by ipc::session: Suppose app Ap wants to communicate with app Bp; this is
52 * called the A-B *split*. In any A-B split, one must be chosen as the *client*, the other as the *server*;
53 * let's by convention here assume they're always listed in server-client order. However, it is allowed that
54 * two splits A-B and C-A co-exist in the same universe; meaning A is server when speaking to B but
55 * client when speaking to C. Lastly, for each server app X, there is a global list of distinct client
56 * apps that are allowed to initiate IPC sessions with it (see ipc::Session concept). Given that:
57 * - An App (in the above-described global registry) that is a client in any split shall be copied-into a
58 * (separate) Client_app object.
59 * - This global registry of known client applications shall similarly be maintained as a global `*set`.
60 * (The information regarding which apps are clients, too, can be read from JSON/XML/whatever or hard-coded.)
61 * - Similarly an App that is a server in any split shall be copied-into a (separate) Server_app object,
62 * forming the 3rd and final global `*set` of applications.
63 * - For each such Server_app, save the Client::m_name of each Client_app allowed to initiate sessions
64 * with it into its Server_app::m_allowed_client_apps. (Again, this mapping can be read from JSON/XML/whatever
65 * or be hard-coded in some fashion. In the end, though, it would go into `m_allowed_client_apps`.)
66 *
67 * Thus one would end up with, after initialization of each process wishing to participate in a given IPC universe:
68 * - An immutable `*set` of `App`s (global App registry).
69 * - An immutable `*set` of `Client_app`s (global Client_app registry).
70 * - An immutable `*set` of `Server_app`s (global Server_app registry), each Server_app listing the `m_name`s
71 * of `Client_app`s referring to the applications that may initiate sessions with it in its immutable
72 * Server_app::m_allowed_client_apps.
73 *
74 * There should be no need, from this moment on, to copy any App, Client_app, or Server_app. It shall be passed
75 * by `const` reference.
76 */
77struct App
78{
79 // Types.
80
81 /// Suggested type for storing master repository or all `Apps`s. See App doc header for discussion.
82 using Master_set = boost::unordered_map<std::string, App>;
83
84 // Data.
85
86 /**
87 * Brief application name, readable to humans and unique across all other applications' names; used
88 * both as a short programmatic key in shared resource naming (including as a folder in #Shared_name for
89 * kernel-persistent resources and addresses; and in path names -- such as PID file name -- in the file system).
90 *
91 * ### Convention: mandated use as key ###
92 * Neither App nor any nearby sub-class or alias of App (Client_app, Server_app) shall be used as the key type
93 * in an associative container (e.g., `set`, `unordered_map`). Instead App::m_name shall be used as the key
94 * when lookup by distinct application is desired. Two scenarios come to mind:
95 * - You want to store a set of applications fitting some criterion/a, but this is not some master repository
96 * of App objects in the first place -- rather it is referring to some subset of such a master repository.
97 * - Then, store simply a `set` or `unordered_set` (etc.) of `std::string m_name`s.
98 * - Rationale: The basic desired operation here is an exitence-check (is application X present, based on
99 * some definition of present?). #m_name is by definition guaranteed to be distinct from all others.
100 * Hence storing such strings alone is sufficient and reasonably efficient while also not relying on
101 * App address uniqueness for lookup (which, while efficient, is a pain to maintain).
102 * - You want to store some master repository of App objects, with all their config such as UID, GID, etc.
103 * - Then, store a `map` or `unordered_map`, with the keys being `std::string m_name`s,
104 * while the values are filled-out App objects (or objects of App sub-class) -- with the guarantee
105 * that `M[name].m_name == name` for all `name`s in any such `M`.
106 * - Rationale: Same as above; but in addition to being able to perform existence-check by #m_name,
107 * one can also iterate through the master details of all the applications and/or
108 * lookup a given application's master details.
109 *
110 * Due to these conventions:
111 * - We do not supply the usual complement of associative-container "stuff" for App and buddies:
112 * equality, less-than, hashing. We do not want to encourage direct App storage that way.
113 * - The idea is: When such storage is desired, use that usual complement of associative-container "stuff"
114 * that is already supplied for `std::string`, as applied to App::m_name.
115 *
116 * Digression regarding rationale: Why use #m_name, and not (say) `const App*`, as the key in associative
117 * lookups? Answer: It was considered. Due to the infrequency of such lookups, combined with the
118 * debug-friendliness/expressiveness of referring to a distinct app by its user-readable name, the lookup-by-name
119 * convention felt more straightforward and convenient. Plus, the address of an object stored in a container
120 * may change while it is being built, which would add an element of brittleness. One could also use an
121 * iterator as a key, but then the registry container would be mandated to be iteratator-stable-under-insertion,
122 * and that brings an annoying host of worries. Indexing by string name is solid by comparison, if a bit slower.
123 *
124 * ### Conventions: Naming ###
125 * Must begin with an ASCII alphabetical character and otherwise consist of only ASCII alphanumerics and underscores.
126 * While these are partially conventions driven by style (for example, dashes could also be allowed -- we just
127 * do not want them to be, so that underscores are always the word-separator used consistently), the following
128 * is an important guarantee for related #Shared_name and `fs::path` semantics:
129 * - Must not contain a util::Shared_name::S_SEPARATOR.
130 * - Must not contain a forward-slash or any other file system path separator.
131 * - Must not contain a dot or any other conceivable extension separator (other than underscore).
132 *
133 * Therefore:
134 * - In #Shared_name paths one can embed #m_name by preceding and/or succeeding it with a
135 * util::Shared_name::S_SEPARATOR, thus making it a full or partial folder name.
136 * - In file system paths one can embed #m_name by preceding and/or succeeding it with a
137 * forward-slash, thus making it a full or partial directory name.
138 * - In either: one can embed #m_name by preceding and/or succeeding it with a dot as an extension separator.
139 *
140 * So if #m_name is "abc_def" one can do things like "/some/dir/abc_def/some_file" and "/some/dir/abc_def.pid/file"
141 * and "/some/dir/prefix.abc_def.3". I.e., #m_name can be safely used as a conceptual "token," even when
142 * combined with path/name separators and dot-extension naming.
143 */
144 std::string m_name;
145
146 /**
147 * Absolute, lexically normalized canonical path to the executable entity (which is not a directory), as it
148 * would appear in a command line or `exec()`-like invocation when actually invoking the application.
149 * Formally (refer to cppreference.com `filesystem::path` doc page):
150 * - There is no root-name: No Windows drive letters ("C:"), no network file share ("//share_name"), etc.
151 * - It is a Unix path: Only forward-slash is used as separator, and that character always is a separator.
152 * - It is absolute: It starts with forward-slash.
153 * - It is lexically normalized: See reference, but essentially it means no more than one slash in a row,
154 * no dot components, no dot-dot components. (Note: dots and dot-dots can *appear* -- but an *entire*
155 * component cannot comprise solely such a sequence; where component means: a thing preceded by slash and
156 * succeeded by slash or end-of-path.)
157 *
158 * Do note that because of the "as it would appear in a command line..." requirement this indirectly restricts
159 * how processes are to be invoked in this system: always via 1, absolute, lexically normal path to the executable.
160 * (Do also note this is still not necessary force an unambiguous way to invoke an application installed at
161 * a given location: sym-links, at least, introduce an infinite number of ways to refer to the same binary.
162 * That said, at least lexical normalization can be done programmatically in `fs`, and sym-link following
163 * path resolution is also separately supplied by `fs`.)
164 */
165 fs::path m_exec_path;
166
167 /// The application must run as this user ID (UID). Files and other shared resources shall have this owner.
169
170 /// The application must run as this group ID (GID). Files and other shared resources shall have this owner.
172}; // struct App
173
174/**
175 * An App that is used as a client in at least one client-server IPC split. This is explained in the
176 * "App, Client_app, Server_app registry conventions" section of the `struct` App doc header.
177 *
178 * @see `struct` App doc header.
179 *
180 * As of this writing a Client_app is just an App with no added stored information. That said a Server_app
181 * may exist whose App base object is equals to a Client_app `*this`'s App base object (in particular App::m_name
182 * but also all other members).
183 */
184struct Client_app : public App
185{
186 // Types.
187
188 /// Short-hand for base type.
189 using Base = App;
190
191 /// Suggested type for storing master repository or all `Client_apps`s. See App doc header for discussion.
192 using Master_set = boost::unordered_map<std::string, Client_app>;
193};
194
195/**
196 * An App that is used as a server in at least one client-server IPC split. This is explained in the
197 * "App, Client_app, Server_app registry conventions" section of the `struct` App doc header.
198 *
199 * @see `struct` App doc header.
200 *
201 * As explained there, each Server_app -- in addition to the source App fields copied from an App in the master
202 * App repository -- ultimately stores references (by App::m_name) to the `Client_app`s describing client apps
203 * that may initiate sessions with that Server_app.
204 */
205struct Server_app : public App
206{
207 // Types.
208
209 /// Suggested type for storing master repository or all `Server_apps`s. See App doc header for discussion.
210 using Master_set = boost::unordered_map<std::string, Server_app>;
211
212 /// Short-hand for existence-checkable set of `Client_app`s via App::m_name.
213 using Client_app_set = boost::unordered_set<std::string>;
214
215 // Data.
216
217 /**
218 * A given Client_app (as identified by its distinct App::m_name) may request to establish an IPC session
219 * with an instance of `*this` Server_app as the conversation partner process if and only if
220 * it is in this set.
221 */
223
224 /**
225 * Absolute path to the directory (without trailing separator) in the file system where kernel-persistent
226 * runtime, but not temporary, information shall be placed for this particular application; leave empty to
227 * use the default system path instead. Informally: In production it is recommended to leave this
228 * empty, as the default system path is a well known location (namely /var/run) where one would expect to find
229 * such files.
230 *
231 * Kernel-persistent means that it'll disappear at reboot; runtime, but not temporary, means it's... not a
232 * thing that might be wiped spuriously anytime (informally: not /tmp). Informally: just know that it is normally
233 * /var/run, and that it stores things such as process-ID (PID) files.
234 *
235 * It shall be a Unix path and absolute (starts with forward-slash) (and lacks a root-name). Informally it is
236 * probably best for it to be lexically normal as well.
237 *
238 * ### Rationale ###
239 * Why allow this override, if the default is sensible? Answer: The default usually requires admin privileges
240 * for writing. For test/debug purposes a location like /tmp or /tmp/var/run can be convenient.
241 */
243
244 /**
245 * Specifies level of access for `Client_app`s (which must, also, be in #m_allowed_client_apps at any rate)
246 * as enforced by file system permissions.
247 *
248 * Specifically, as of this writing, it determines at least the following items. Note that ownership is
249 * identified by the UID/GID settings (matching `*this` App::m_user_id and App::m_group_id) of various
250 * files and other shared resources created by the server* application in the ipc::session framework.
251 * (* - Actually, at times, resources are created by the client application too. See below.)
252 * - File permissions set on the CNS (PID) file established by Session_server. A given #Client_session
253 * (etc.) must read this file in order to know to which Session_server to issue a session-open request
254 * (establish a Session); if it cannot access that file, it cannot open a session against this
255 * Server_app.
256 * - If this check fails, and everything is functioning as generally intended internally, then the below
257 * items are moot (as opening a session is a prerequisite for anything else).
258 * - Permissions set on any SHM pool created for a given `"shm::*::Server_session"`. and
259 * `"shm::*::Session_server"`. A Client_app may not be able to complete its session-open attempt
260 * (shm::classic::Client_session::sync_connect(), etc.) if its user lacks the permissions to open the
261 * underlying SHM-pool(s) resource in the file system.(It also conceivably may be able to complete the
262 * session-open but fail if opening a pool on-demand later; the session will then likely end
263 * prematurely. This depends on the inner workings of the particular SHM system chosen;
264 * shm::classic::Client_session opens pools during session-open procedure exclusively, but other
265 * systems may behave differently.)
266 * - Arena-lending SHM providers -- as of this writing namely ipc::shm::arena_lend::jemalloc -- involve
267 * both sides (session-server and session-client) each creating SHM pool(s) for allocations from
268 * within their respective processes. #m_permissions_level_for_client_apps applies to the
269 * server-created pools, yes; but also client-created pools. At the moment it seems to me
270 * (ygoldfel) that nevertheless a common setting in Server_app still makes sense. In practice, it
271 * means this: If this is set to util::Permissions_level::S_USER_ACCESS, then the Client_app and
272 * Server_app UID/GID shall need to be equal; if util::Permissions_level::S_GROUP_ACCESS then just
273 * the GID (while UIDs ideally would be different).(Obviously if `S_UNRESTRICTED` then does not
274 * matter.) Don't see the sense in making this a separate knob in Client_app. Maybe though? Not a
275 * formal to-do for now.
276 * - Similarly (to preceding bullet point, minus the `arena_lend` caveat) for bipc MQs.
277 * ipc::session opens such MQs (if so configured at compile-time) during session-open; hence on permissions
278 * failure the session-open will itself fail.
279 * - Similarly (to preceding bullet point) for POSIX MQs.
280 *
281 * As of this writing the list is complete; however it is conceivable it may be analogously extended to more
282 * resources.
283 * @internal
284 * That list is actually not quite complete even as of this writing. The CNS (PID) file's shared-mutex
285 * is also subject to this, as are other similar shared-mutexes (in Linux, semaphores). However that's very
286 * Inside Baseball to mention in public docs.
287 */
289}; // struct Server_app
290
291// Free functions: in *_fwd.hpp.
292
293} // namespace ipc::session
Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...
Definition: app.cpp:27
Permissions_level
Simple specifier of desired access permissions, usually but not necessarily translated into a Permiss...
Definition: util_fwd.hpp:88
::gid_t group_id_t
Syntactic-sugary type for POSIX group ID (integer).
Definition: util_fwd.hpp:143
::uid_t user_id_t
Syntactic-sugary type for POSIX user ID (integer).
Definition: util_fwd.hpp:140
A description of an application in this ipc::session inter-process communication universe.
Definition: app.hpp:78
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...
Definition: app.hpp:171
std::string m_name
Brief application name, readable to humans and unique across all other applications' names; used both...
Definition: app.hpp:144
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...
Definition: app.hpp:168
fs::path m_exec_path
Absolute, lexically normalized canonical path to the executable entity (which is not a directory),...
Definition: app.hpp:165
boost::unordered_map< std::string, App > Master_set
Suggested type for storing master repository or all Appss. See App doc header for discussion.
Definition: app.hpp:82
An App that is used as a client in at least one client-server IPC split.
Definition: app.hpp:185
An App that is used as a server in at least one client-server IPC split.
Definition: app.hpp:206
Client_app_set m_allowed_client_apps
A given Client_app (as identified by its distinct App::m_name) may request to establish an IPC sessio...
Definition: app.hpp:222
boost::unordered_set< std::string > Client_app_set
Short-hand for existence-checkable set of Client_apps via App::m_name.
Definition: app.hpp:213
fs::path m_kernel_persistent_run_dir_override
Absolute path to the directory (without trailing separator) in the file system where kernel-persisten...
Definition: app.hpp:242
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)...
Definition: app.hpp:288