Flow-IPC 3.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, 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 *
48 * @warning Right off the bat, hopefully where it's visible, we alert you to a gotcha. It is implied below,
49 * but to put a fine point on it: Once you've set up your App, Client_app, Server_app objects,
50 * they need to stay alive at least past any session::Client_session and session::Session_server
51 * objects into which you pass their reference(s). They will not be copied, and if they're destroyed
52 * early, you may be hit by nasty accessing-invalid-memory undefined behaviors including but not limited
53 * to crashing.
54 *
55 * Back to the conventions:
56 *
57 * - Maintain a global (to the applications in your IPC universe) registry of known applications. It could be
58 * stored in JSON or XML form in some central shared software component and read-into a master `set` or
59 * `unordered_set` (by App::m_name; see its doc header) available in a centrally-available library;
60 * or this `*set` could simply be a hard-coded C++ `extern const`; whatever works.
61 * - In the current model supported by ipc::session: Suppose app Ap wants to communicate with app Bp; this is
62 * called the A-B *split*. In any A-B split, one must be chosen as the *client*, the other as the *server*;
63 * let's by convention here assume they're always listed in server-client order. However, it is allowed that
64 * two splits A-B and C-A co-exist in the same universe; meaning A is server when speaking to B but
65 * client when speaking to C. Lastly, for each server app X, there is a global list of distinct client
66 * apps that are allowed to initiate IPC sessions with it (see ipc::Session concept). Given that:
67 * - An App (in the above-described global registry) that is a client in any split shall be copied-into a
68 * (separate) Client_app object.
69 * - This global registry of known client applications shall similarly be maintained as a global `*set`.
70 * (The information regarding which apps are clients, too, can be read from JSON/XML/whatever or hard-coded.)
71 * - Similarly an App that is a server in any split shall be copied-into a (separate) Server_app object,
72 * forming the 3rd and final global `*set` of applications.
73 * - For each such Server_app, save the App::m_name of each Client_app allowed to initiate sessions
74 * with it into its Server_app::m_allowed_client_apps. (Again, this mapping can be read from JSON/XML/whatever
75 * or be hard-coded in some fashion. In the end, though, it would go into `m_allowed_client_apps`.)
76 *
77 * Thus one would end up with, after initialization of each process wishing to participate in a given IPC universe:
78 * - An immutable `*set` of `App`s (global App registry).
79 * - An immutable `*set` of `Client_app`s (global Client_app registry).
80 * - An immutable `*set` of `Server_app`s (global Server_app registry), each Server_app listing the `m_name`s
81 * of `Client_app`s referring to the applications that may initiate sessions with it in its immutable
82 * Server_app::m_allowed_client_apps.
83 *
84 * There should be no need, from this moment on, to copy any App, Client_app, or Server_app. It shall be passed
85 * by `const` reference.
86 */
87struct App
88{
89 // Types.
90
91 /**
92 * Type for storing master repository of all `App`s. See App doc header for discussion.
93 *
94 * @internal
95 *
96 * ### Rationale: Chosen type (also applies to Client_app::Master_set and Server_app::Master_set) ###
97 * It is `unordered_`, simply because there was not any particularly compelling reason to impose an order.
98 * (Admittedly: alphabetical order by `m_name` might have been nice in some contexts, like if someone wanted
99 * to pretty-print a `Master_set`. Ah, well. It is set now. Prints can be tweaked to sort anyway; as of
100 * this writing Flow-IPC does not print a `Master_set` anywhere.)
101 *
102 * Using `unordered_map` (instead of `unordered_flat_map`) is pretty good here, as this way there is
103 * pointer stability. On one hand the implied -- not as of this writing formally required -- preferred pattern
104 * of use is for the user to set up their IPC universe, including these `Master_set`s, before any IPC happens --
105 * and then never change any of the data therein. On the other hand, "implied preferred pattern" be damned,
106 * it is conceivable a user would at some point, after giving some API a `Master_set`, add an App to that set.
107 * In that case pointer stability is an asset; any memorized `App` addresses won't become invalid on rehash.
108 *
109 * Performance is not a concern here: lookups are rare; sizes are small. Hence no need to mess
110 * with `_node_map` and so forth.
111 */
112 using Master_set = boost::unordered_map<std::string, App>;
113
114 // Data.
115
116 /**
117 * Brief application name, readable to humans and unique across all other applications' names; used
118 * both as a short programmatic key in shared resource naming (including as a folder in #Shared_name for
119 * kernel-persistent resources and addresses; and in path names -- such as PID file name -- in the file system).
120 *
121 * ### Convention: mandated use as key ###
122 * Neither App nor any nearby sub-class or alias of App (Client_app, Server_app) shall be used as the key type
123 * in an associative container (e.g., `set`, `unordered_map`). Instead App::m_name shall be used as the key
124 * when lookup by distinct application is desired. Two scenarios come to mind:
125 * - You want to store a set of applications fitting some criterion/a, but this is not some master repository
126 * of App objects in the first place -- rather it is referring to some subset of such a master repository.
127 * - Then, store simply a `set` or `unordered_set` (etc.) of `std::string m_name`s.
128 * - Rationale: The basic desired operation here is an existence-check (is application X present, based on
129 * some definition of present?). #m_name is by definition guaranteed to be distinct from all others.
130 * Hence storing such strings alone is sufficient and reasonably efficient while also not relying on
131 * App address uniqueness for lookup (which, while efficient, is a pain to maintain).
132 * - You want to store some master repository of App objects, with all their config such as UID, GID, etc.
133 * - Then, store a `map` or `unordered_map`, with the keys being `std::string m_name`s,
134 * while the values are filled-out App objects (or objects of App sub-class) -- with the guarantee
135 * that `M[name].m_name == name` for all `name`s in any such `M`.
136 * - Rationale: Same as above; but in addition to being able to perform existence-check by #m_name,
137 * one can also iterate through the master details of all the applications and/or
138 * lookup a given application's master details.
139 *
140 * Due to these conventions:
141 * - We do not supply the usual complement of associative-container "stuff" for App and buddies:
142 * equality, less-than, hashing. We do not want to encourage direct App storage that way.
143 * - The idea is: When such storage is desired, use that usual complement of associative-container "stuff"
144 * that is already supplied for `std::string`, as applied to App::m_name.
145 *
146 * Digression regarding rationale: Why use #m_name, and not (say) `const App*`, as the key in associative
147 * lookups? Answer: It was considered. Due to the infrequency of such lookups, combined with the
148 * debug-friendliness/expressiveness of referring to a distinct app by its user-readable name, the lookup-by-name
149 * convention felt more straightforward and convenient. Plus, the address of an object stored in a container
150 * may change while it is being built, which would add an element of brittleness. One could also use an
151 * iterator as a key, but then the registry container would be mandated to be iterator-stable-under-insertion,
152 * and that brings an annoying host of worries. Indexing by string name is solid by comparison, if a bit slower.
153 *
154 * ### Conventions: Naming ###
155 * Must begin with an ASCII alphabetical character and otherwise consist of only ASCII alphanumerics
156 * (so, notably, *no underscores*: use camelCase to separate words, per the convention for #Shared_name
157 * components; see its doc header). While that is partially style-driven, the following
158 * is an important guarantee for related #Shared_name and `fs::path` semantics:
159 * - Must not contain a util::Shared_name::S_SEPARATOR (which, notably, is the underscore).
160 * - Must not contain a forward-slash or any other file system path separator.
161 * - Must not contain a dot or any other conceivable extension separator.
162 *
163 * Therefore:
164 * - In #Shared_name paths one can embed #m_name by preceding and/or succeeding it with a
165 * util::Shared_name::S_SEPARATOR, thus making it a full or partial folder name.
166 * - In file system paths one can embed #m_name by preceding and/or succeeding it with a
167 * forward-slash, thus making it a full or partial directory name.
168 * - In either: one can embed #m_name by preceding and/or succeeding it with a dot as an extension separator.
169 *
170 * @note Enforced, in part: Session_server ctor (all variants) emits error::Code::S_INVALID_ARGUMENT, if the
171 * Server_app's -- or any registered Client_app's -- #m_name is empty or contains
172 * util::Shared_name::S_SEPARATOR.
173 *
174 * So if #m_name is "abcDef" one can do things like `/some/dir/abcDef/some_file` and `/some/dir/abcDef.pid/file`
175 * and `/some/dir/prefix.abcDef.3` (file-system example) or "_some_dir_abcDef_moreStuff" (`Shared_name` example).
176 * I.e., #m_name can be safely used as a conceptual "token," even when combined with path/name separators and
177 * dot-extension naming.
178 */
179 std::string m_name;
180
181 /**
182 * Absolute, lexically normalized canonical path to the executable entity (which is not a directory), as it
183 * would appear in a command line or `exec()`-like invocation when actually invoking the application.
184 * Formally (refer to cppreference.com `filesystem::path` doc page):
185 * - There is no root-name: No Windows drive letters ("C:"), no network file share ("//share_name"), etc.
186 * - It is a Unix path: Only forward-slash is used as separator, and that character always is a separator.
187 * - It is absolute: It starts with forward-slash.
188 * - It is lexically normalized: See reference, but essentially it means no more than one slash in a row,
189 * no dot components, no dot-dot components. (Note: dots and dot-dots can *appear* -- but an *entire*
190 * component cannot comprise solely such a sequence; where component means: a thing preceded by slash and
191 * succeeded by slash or end-of-path.)
192 *
193 * Do note that because of the "as it would appear in a command line..." requirement this indirectly restricts
194 * how processes are to be invoked in this system: always via 1, absolute, lexically normal path to the executable.
195 * (Do also note this still does not necessarily force an unambiguous way to invoke an application installed at
196 * a given location: sym-links, at least, introduce an infinite number of ways to refer to the same binary.
197 * That said, at least lexical normalization can be done programmatically in `fs`, and sym-link following
198 * path resolution is also separately supplied by `fs`.)
199 */
200 fs::path m_exec_path;
201
202 /// The application must run as this user ID (UID). Files and other shared resources shall have this owner.
204
205 /// The application must run as this group ID (GID). Files and other shared resources shall have this owner.
207}; // struct App
208
209/**
210 * An App that is used as a client in at least one client-server IPC split. This is explained in the
211 * "App, Client_app, Server_app registry conventions" section of the `struct` App doc header.
212 *
213 * @see `struct` App doc header.
214 *
215 * As of this writing a Client_app is just an App with no added stored information. That said a Server_app
216 * may exist whose App base object equals a Client_app `*this`'s App base object (in particular App::m_name
217 * but also all other members).
218 */
219struct Client_app : public App
220{
221 // Types.
222
223 /// Short-hand for base type.
224 using Base = App;
225
226 /// Type for storing master repository of all `Client_app`s. See App doc header for discussion.
227 using Master_set = boost::unordered_map<std::string, Client_app>;
228};
229
230/**
231 * An App that is used as a server in at least one client-server IPC split. This is explained in the
232 * "App, Client_app, Server_app registry conventions" section of the `struct` App doc header.
233 *
234 * @see `struct` App doc header.
235 *
236 * As explained there, each Server_app -- in addition to the source App fields copied from an App in the master
237 * App repository -- ultimately stores references (by App::m_name) to the `Client_app`s describing client apps
238 * that may initiate sessions with that Server_app.
239 */
240struct Server_app : public App
241{
242 // Types.
243
244 /// Type for storing master repository of all `Server_app`s. See App doc header for discussion.
245 using Master_set = boost::unordered_map<std::string, Server_app>;
246
247 /// Short-hand for existence-checkable set of `Client_app`s via App::m_name.
248 using Client_app_set = boost::unordered_set<std::string>;
249
250 // Data.
251
252 /**
253 * A given Client_app (as identified by its distinct App::m_name) may request to establish an IPC session
254 * with an instance of `*this` Server_app as the conversation partner process if and only if
255 * it is in this set.
256 */
258
259 /**
260 * Absolute path to the directory (without trailing separator) in the file system where kernel-persistent
261 * runtime, but not temporary, information shall be placed for this particular application; leave empty to
262 * use the default system path instead. Informally: In production it is recommended to leave this
263 * empty, as the default system path is a well known location (namely `/var/run`) where one would expect to find
264 * such files.
265 *
266 * Kernel-persistent means that it'll disappear at reboot; runtime, but not temporary, means it's... not a
267 * thing that might be wiped spuriously anytime (informally: not `/tmp`). Informally: just know that it is normally
268 * `/var/run`, and that it stores things such as process-ID (PID) files.
269 *
270 * It shall be a Unix path and absolute (starts with forward-slash) (and lacks a root-name). Informally it is
271 * probably best for it to be lexically normal as well.
272 *
273 * ### Rationale ###
274 * Why allow this override, if the default is sensible? Answer: The default usually requires admin privileges
275 * for writing. For test/debug purposes a location like `/tmp` or `/tmp/var/run` can be convenient.
276 */
278
279 /**
280 * Specifies level of access for `Client_app`s (which must, also, be in #m_allowed_client_apps at any rate)
281 * as enforced by file system permissions.
282 *
283 * Specifically, as of this writing, it determines at least the following items. Note that ownership is
284 * identified by the UID/GID settings (matching `*this` App::m_user_id and App::m_group_id) of various
285 * files and other shared resources created by the server* application in the ipc::session framework.
286 * (* - Actually, at times, resources are created by the client application too. See below.)
287 * - File permissions set on the CNS (PID file) established by Session_server. A given #Client_session
288 * (etc.) must read this file in order to know to which Session_server to issue a session-open request
289 * (establish a Session); if it cannot access that file, it cannot open a session against this
290 * Server_app.
291 * - If this check fails, and everything is functioning as generally intended internally, then the below
292 * items are moot (as opening a session is a prerequisite for anything else).
293 * - Permissions set on any SHM pool created for a given `"shm::*::Server_session"` and
294 * `"shm::*::Session_server"`. A Client_app may not be able to complete its session-open attempt
295 * (shm::classic::Client_session::sync_connect(), etc.) if its user lacks the permissions to open the
296 * underlying SHM-pool(s) resource in the file system. (It also conceivably may be able to complete the
297 * session-open but fail if opening a pool on-demand later; the session will then likely end
298 * prematurely. This depends on the inner workings of the particular SHM system chosen;
299 * shm::classic::Client_session opens pools during session-open procedure exclusively, but other
300 * systems may behave differently.)
301 * - Arena-lending SHM-providers -- as of this writing namely ipc::shm::arena_lend::jemalloc -- involve
302 * both sides (session-server and session-client) each creating SHM pool(s) for allocations from
303 * within their respective processes. #m_permissions_level_for_client_apps applies to the
304 * server-created pools, yes; but also client-created pools. At the moment it seems to me
305 * (ygoldfel) that nevertheless a common setting in Server_app still makes sense. In practice, it
306 * means this: If this is set to util::Permissions_level::S_USER_ACCESS, then the Client_app and
307 * Server_app UID/GID shall need to be equal; if util::Permissions_level::S_GROUP_ACCESS then just
308 * the GID (while UIDs ideally would be different). (Obviously if `S_UNRESTRICTED` then does not
309 * matter.) Don't see the sense in making this a separate knob in Client_app. Maybe though? Not a
310 * formal to-do for now.
311 * - Similarly (to preceding bullet point, minus the `arena_lend` caveat) for bipc MQs.
312 * ipc::session opens such MQs (if so configured at compile-time) during session-open; hence on permissions
313 * failure the session-open will itself fail.
314 * - Similarly (to preceding bullet point) for POSIX MQs.
315 *
316 * As of this writing the list is complete; however it is conceivable it may be analogously extended to more
317 * resources.
318 *
319 * @internal
320 *
321 * That list is actually not quite complete even as of this writing. The CNS (PID) file's shared-mutex
322 * is also subject to this, as are other similar shared-mutexes (in Linux, semaphores). However that's very
323 * Inside Baseball to mention in public docs.
324 */
326}; // struct Server_app
327
328// Free functions: in *_fwd.hpp.
329
330} // namespace ipc::session
Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...
Definition: app.cpp:29
Permissions_level
Simple specifier of desired access permissions, usually but not necessarily translated into a Permiss...
Definition: util_fwd.hpp:91
::gid_t group_id_t
Syntactic-sugary type for POSIX group ID (integer).
Definition: util_fwd.hpp:146
::uid_t user_id_t
Syntactic-sugary type for POSIX user ID (integer).
Definition: util_fwd.hpp:143
A description of an application in this ipc::session inter-process communication universe.
Definition: app.hpp:88
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:206
std::string m_name
Brief application name, readable to humans and unique across all other applications' names; used both...
Definition: app.hpp:179
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:203
fs::path m_exec_path
Absolute, lexically normalized canonical path to the executable entity (which is not a directory),...
Definition: app.hpp:200
boost::unordered_map< std::string, App > Master_set
Type for storing master repository of all Apps.
Definition: app.hpp:112
An App that is used as a client in at least one client-server IPC split.
Definition: app.hpp:220
An App that is used as a server in at least one client-server IPC split.
Definition: app.hpp:241
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:257
boost::unordered_set< std::string > Client_app_set
Short-hand for existence-checkable set of Client_apps via App::m_name.
Definition: app.hpp:248
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:277
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:325