Flow-IPC 1.0.2
Flow-IPC project: Full implementation reference.
asio_local_stream_socket.cpp
Go to the documentation of this file.
1/* Flow-IPC: Core
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
21#include <flow/common.hpp>
22#include <boost/array.hpp>
23
24#ifndef FLOW_OS_LINUX // Sanity-re-check. We'll be sending sockets through sockets, etc., which requires Linux.
25static_assert(false, "Should not have gotten to this line; should have required Linux; this .cpp file assumes it. "
26 "Might work in other POSIX OS (e.g., macOS) but must be checked/tested.");
27#endif
28#include <sys/types.h>
29#include <sys/socket.h>
30
32{
33
34// Free function implementations.
35
36size_t nb_write_some_with_native_handle(flow::log::Logger* logger_ptr,
37 Peer_socket* peer_socket_ptr,
38 Native_handle payload_hndl, const util::Blob_const& payload_blob,
39 Error_code* err_code)
40{
41 namespace bind_ns = flow::util::bind_ns;
42 using boost::system::system_category;
43 using boost::array;
44 namespace sys_err_codes = boost::system::errc;
45 using ::sendmsg;
46 using ::msghdr;
47 using ::iovec;
48 using ::cmsghdr;
49 // using ::SOL_SOCKET; // It's a macro apparently.
50 using ::SCM_RIGHTS;
51 using ::MSG_DONTWAIT;
52 using ::MSG_NOSIGNAL;
53 // using ::errno; // It's a macro apparently.
54
55 FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR(size_t, nb_write_some_with_native_handle,
56 logger_ptr, peer_socket_ptr, payload_hndl, bind_ns::cref(payload_blob), _1);
57 // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
58
59 /* To reader/maintainer: The below isn't that difficult, but if you need exact understanding and definitely if you
60 * plan to make changes -- even just clarifications or changes in the doc header -- then read the entire doc header
61 * of nb_read_some_with_native_handle() first. */
62
63 assert(peer_socket_ptr);
64 auto& peer_socket = *peer_socket_ptr;
65
66 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_TRANSPORT);
67 FLOW_LOG_TRACE("Connected local peer socket wants to write from location @ [" << payload_blob.data() << "] "
68 "plus native handle [" << payload_hndl << "]. Will try to send.");
69
70 /* Let us send as much as possible of payload_blob; and the native socket payload_hndl.
71 * As explicitly documented in boost.asio docs: it does not provide an API for the latter (fairly hairy
72 * and Linux-specific) feature, but it is doable by using sendmsg() natively via peer_socket.native_handle()
73 * which gets the native handle (a/k/a FD).
74 *
75 * Side note: After I (ygoldfel) had written this and async_write_with_native_handle() (which calls us),
76 * I found validation for the approach in boost.asio docs in their example for Peer_socket::native_non_blocking(),
77 * which shows how to implement async_sendfile(), spiritually similar to our thing which is essentially
78 * a case of an analogous hypothetical async_sendmsg(). Subtlety: we, too, could have used native_non_blocking(),
79 * as their example does; and the only reason we did not is that `sendmsg()` happens to support a per-call flag
80 * that forces a non-blocking transmission, so one needn't deal with socket state. Subtlety: realize (as per
81 * boost.asio docs) that Peer_socket::non_blocking() is orthogonal to this entirely; it is orthogonal to
82 * native_non_blocking() and the per-call flag thing. */
83
84 iovec native_buf1 = { const_cast<void*>(payload_blob.data()), payload_blob.size() };
85 // (const_cast<> is OK, as sendmsg() is read-only. Probably the API is const-janky due to iovec's reuse in recvmsg().)
86
87 msghdr sendmsg_hdr =
88 {
89 0, // msg_name - Address; not used; we are connected.
90 0, // msg_namelen - Ditto.
91 /* msg_iov - Scatter/gather array. We just put the one blob here. It's natural to instead put a
92 * a buffer sequence, meaning N>=1 buffers, here. There's a to-do elsewhere as of this writing to
93 * make that API as well; as of now it's not needed. Take a look inside boost.asio for how they convert
94 * between the 2; it's not rocket science, but we can either reuse their code or learn from it;
95 * for example there's the matter of MAX_IOV (max size of this array). */
96 &native_buf1,
97 1, // msg_iovlen - # elements in msg_iov.
98
99 /* The other 3 fields can be uninitialized: msg_flags is unused; msg_control[len] are set below.
100 * The language won't let me leave them as garbage though so: */
101 0, 0, 0
102 };
103
104 /* What's left is to set up msg_control[len] which is how we send the native handle (payload_hndl).
105 * This is based on example snippet taken from Linux `man cmsg`. FWIW it's rather difficult to figure out
106 * how to write it from the rest of the documentation, which is likely complete and correct but opaque; the
107 * example is invaluable. Note we are particularly using the SOL_SOCKET/SCM_RIGHTS technique. */
108
109 constexpr size_t N_PAYLOAD_FDS = 1;
110 union
111 {
112 /* (Comment taken verbatim from aforementioned `man`.)
113 * Ancillary data buffer, wrapped in a union in order to ensure it is suitably aligned.
114 *
115 * ...If I (ygoldfel) understand correctly, and I think I do, the idea is that:
116 * - msg_control points to a contiguous data area (ancillary data); which is split up into variable-length
117 * sub-areas, each of which starts with a header, in the form of a `cmsghdr`, which contains a couple of
118 * enum-like ints, a cmsg_len specifying the length of the rest of that sub-area (within the sequence),
119 * and then that area which contains the stuff to be transmitted (whose exact size/meaning depends on the
120 * enums; in our case it'll be native handles).
121 * - A key caveat is that in order to be properly interpreted, the header -- probably cast by the kernel/something
122 * to `cmsghdr` -- must begin on an aligned address. So, in particular, if whatever comes before it
123 * (could be the preceding ancillary data sub-area, depending) ends just before an odd address, then
124 * the data sub-area must be a byte or a few to the right.
125 * - So this trick explicitly puts a cmsghdr there; so that its co-inhabitant m_buf (the actual area where we'll
126 * shove handles) will also begin on the same address/byte.
127 * - CMSG_ALIGN() is a Linux extension that might be usable to avoid having to use the trick, but seeing as
128 * how `man cmsg` in my Linux says it's not portable and itself foregoes it in favor of the trick in the
129 * example, I'll just also use the trick. */
130 array<uint8_t, CMSG_SPACE(sizeof(Native_handle::handle_t) * N_PAYLOAD_FDS)> m_buf;
131 cmsghdr m_align;
132 } msg_control_as_union;
133
134 sendmsg_hdr.msg_control = msg_control_as_union.m_buf.c_array();
135 sendmsg_hdr.msg_controllen = sizeof(msg_control_as_union.m_buf);
136 cmsghdr* const sendmsg_hdr_cmsg_ptr = CMSG_FIRSTHDR(&sendmsg_hdr); // Or cast from &m_buf; or use &m_align....
137 sendmsg_hdr_cmsg_ptr->cmsg_level = SOL_SOCKET;
138 sendmsg_hdr_cmsg_ptr->cmsg_type = SCM_RIGHTS;
139 sendmsg_hdr_cmsg_ptr->cmsg_len = CMSG_LEN(sizeof(Native_handle::handle_t) * N_PAYLOAD_FDS);
140
141 // Copy the FDs. We have just the one; simply assign (omit `memcpy`) but static_assert() to help future-proof.
142 static_assert(N_PAYLOAD_FDS == 1, "Should be only passing one native handle into sendmsg() as of this writing.");
143 *(reinterpret_cast<Native_handle::handle_t*>(CMSG_DATA(sendmsg_hdr_cmsg_ptr))) = payload_hndl.m_native_handle;
144
145 const auto n_sent_or_error
146 = sendmsg(peer_socket.native_handle(), &sendmsg_hdr,
147 /* If socket is un-writable then don't block;
148 * EAGAIN/EWOULDBLOCK instead. Doing it this way is easier than messing with fcntl(), particularly
149 * since we're working with a boost.asio-managed socket; that's actually fine -- there's a portable
150 * API native_non_blocking() in boost.asio for setting this -- but feels like the less interaction between
151 * portable boost.asio code we use and this native stuff, the better -- so just keep it local here. */
152 MSG_DONTWAIT | MSG_NOSIGNAL);
153 // ^-- Dealing with SIGPIPE is a pointless pain; if conn closed that way just give as an EPIPE error.
154
155 if (n_sent_or_error == -1)
156 {
157 /* Not even 1 byte of the blob was sent; and hence nor was payload_hndl. (Docs don't explicitly say that 2nd
158 * part after the semicolon, but it's essentially impossible that it be otherwise, as then it'd be unknowable.
159 * Update: Confirmed in kernel source and supported by this delightfully reassuring (in several ways) link:
160 * [ https://gist.github.com/kentonv/bc7592af98c68ba2738f4436920868dc ] (Googled "SCM_RIGHTS gist").) */
161
162 const Error_code sys_err_code(errno, system_category());
163 if ((sys_err_code == sys_err_codes::operation_would_block) || // EWOULDBLOCK
164 (sys_err_code == sys_err_codes::resource_unavailable_try_again)) // EAGAIN (same meaning)
165 {
166 FLOW_LOG_TRACE("Write attempt indicated would-block; not an error condition. Nothing sent.");
167 /* Subtlety: We could just set it to sys_err_code; but we specifically promised in contract we'd set it to the
168 * boost::asio::error::would_block representation of would-block condition. Why did we promise that? 2 reasons:
169 * 1, that is what boost.asio's own spiritually similar Peer_socket::write_some() would do in non_blocking() mode.
170 * 2, then we can promise a specific code instead of making them check for the above 2 errno values.
171 *
172 * Subtlety: net_flow::Peer_socket::sync_receive uses somewhat different semantics; it indicates would-block by
173 * returning 0 *but* a falsy *err_code. Why are we inconsistent with that? Answer: Because net_flow is not
174 * trying to be a boost.asio extension; we are. In net_flow's context (as of this writing) no one is surprised
175 * when semantics are somewhat different from boost.asio; but in our context they might be quite surprised
176 * indeed. */
177 *err_code = boost::asio::error::would_block;
178 return 0;
179 }
180 // else
181
182 assert(sys_err_code);
183
184 // All other errors are fatal.
185 FLOW_ERROR_SYS_ERROR_LOG_WARNING(); // Log based on sys_err_code.
186 FLOW_LOG_WARNING("Connected local peer socket tried to write from "
187 "location @ [" << payload_blob.data() << "] plus native handle [" << payload_hndl << "]; "
188 "but an unrecoverable error occurred. Nothing sent.");
189 *err_code = sys_err_code;
190 return 0;
191 } // if (n_sent_or_error == -1)
192 // else if (n_sent_or_error != -1)
193
194 assert (n_sent_or_error > 0);
195
196 FLOW_LOG_TRACE("sendmsg() reports the native handle [" << payload_hndl << "] was successfully sent; as "
197 "were [" << n_sent_or_error << "] of the blob's [" << payload_blob.size() << "] bytes.");
198 err_code->clear();
199 return n_sent_or_error;
200} // nb_write_some_with_native_handle()
201
202size_t nb_read_some_with_native_handle(flow::log::Logger* logger_ptr,
203 Peer_socket* peer_socket_ptr,
204 Native_handle* target_payload_hndl_ptr,
205 const util::Blob_mutable& target_payload_blob,
206 Error_code* err_code,
207 int message_flags)
208{
209 namespace bind_ns = flow::util::bind_ns;
210 using boost::system::system_category;
211 using boost::array;
212 namespace sys_err_codes = boost::system::errc;
213 using ::sendmsg;
214 using ::msghdr;
215 using ::iovec;
216 using ::cmsghdr;
217 // using ::SOL_SOCKET; // It's a macro apparently.
218 using ::SCM_RIGHTS;
219 using ::MSG_DONTWAIT;
220 using ::MSG_CTRUNC;
221 // using ::errno; // It's a macro apparently.
222
223 FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR(size_t, nb_read_some_with_native_handle,
224 logger_ptr, peer_socket_ptr, target_payload_hndl_ptr,
225 bind_ns::cref(target_payload_blob), _1, message_flags);
226 // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
227
228 /* To reader/maintainer: The below isn't that difficult, but if you need exact understanding and definitely if you
229 * plan to make changes -- even just clarifications or changes in the doc header -- then read the entire doc header
230 * of nb_read_some_with_native_handle() first. */
231
232 assert(peer_socket_ptr);
233 assert(target_payload_hndl_ptr);
234
235 auto& peer_socket = *peer_socket_ptr;
236 auto& target_payload_hndl = *target_payload_hndl_ptr;
237
238 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_TRANSPORT);
239 FLOW_LOG_TRACE("Connected local peer socket wants to read up to [" << target_payload_blob.size() << "] bytes "
240 "to location @ [" << target_payload_blob.data() << "] "
241 "plus possibly a native handle. Will try to receive.");
242 target_payload_hndl = Native_handle();
243 assert(target_payload_hndl.null()); // We promised to set to this if no socket received (including on error).
244
245 /* Let us receive as much as possible into target_payload_blob up to its size; and a native socket (if any) into
246 * target_payload_hndl (if none, then it'll remain null()).
247 * As explicitly documented in boost.asio docs: it does not provide an API for the latter (fairly hairy
248 * and Linux-specific) feature, but it is doable by using recvmsg() natively via peer_socket.native_handle()
249 * which gets the native handle (a/k/a FD).
250 *
251 * Recommending first looking at nb_write_some_with_native_handle(), as we operate symmetrically/similarly. */
252
253 iovec native_buf1 = { target_payload_blob.data(), target_payload_blob.size() };
254 msghdr recvmsg_hdr =
255 {
256 0, // msg_name - Address; not used; we are connected.
257 0, // msg_namelen - Ditto.
258 /* msg_iov - Scatter/gather array. We just put the one target blob here. See similarly-themed notes at similar
259 * spot in nb_write_some_with_native_handle(); they apply here too, more or less. */
260 &native_buf1,
261 1, // msg_iovlen - # elements in msg_iov. This is an *input* arg only; output is total length read: the ret value.
262 /* The following fields may or may need not be initialized; but let's discuss them generally:
263 * msg_control, msg_controllen - These are out-args, indirectly set/reserved and interpreted with CMSG* below.
264 * msg_flags - This is an out-arg containing special feature flags. These may be checked below after call.
265 * The language won't let me leave them as garbage though so: */
266 0, 0, 0
267 };
268 // May not be needed, but at least some examples online do it; seems prudent and cheap.
269 recvmsg_hdr.msg_flags = 0;
270
271 /* Set up .msg_control*. This, and then interpreting the output after the call, is based on the out-equivalent in
272 * nb_write_some_with_native_handle() as well as cross-referencing with some Internet sources.
273 *
274 * Before the call, we must reserve space for the ancillary out-data, if any; set msg_control to point to that;
275 * and msg_controllen to the size of that thing. */
276
277 constexpr size_t N_PAYLOAD_FDS = 1;
278 union
279 {
280 // See nb_write_some_with_native_handle(). It explains the m_align thing.
281 array<uint8_t, CMSG_SPACE(sizeof(Native_handle::handle_t) * N_PAYLOAD_FDS)> m_buf;
282 cmsghdr m_align;
283 } msg_control_as_union;
284 // Probably paranoia. Just in case pre-fill it with zeroes (0x00... is not a valid FD) for sanity check later.
285 msg_control_as_union.m_buf.fill(0);
286
287 recvmsg_hdr.msg_control = msg_control_as_union.m_buf.c_array();
288 recvmsg_hdr.msg_controllen = sizeof(msg_control_as_union.m_buf);
289
290 const auto n_rcvd_or_error
291 = recvmsg(peer_socket.native_handle(), &recvmsg_hdr,
292 /* If socket is un-writable then don't block; EAGAIN/EWOULDBLOCK instead. ...Further comment omitted;
293 * see sendmsg() elsewhere in this .cpp. Same thing here. */
294 MSG_DONTWAIT | message_flags); // <-- Re. message_flags please read notes in our doc header.
295
296 // Carefully check all the outputs. Return value first.
297
298 if (n_rcvd_or_error == -1)
299 {
300 /* Not even 1 byte of a blob was read; and hence nor was any target_payload_hndl. (See comment in similar
301 * sport in nb_write_some_with_native_handle(); applies here equally.) */
302
303 const Error_code sys_err_code(errno, system_category());
304 if ((sys_err_code == sys_err_codes::operation_would_block) || // EWOULDBLOCK
305 (sys_err_code == sys_err_codes::resource_unavailable_try_again)) // EAGAIN (same meaning)
306 /* Reader: "But, ygoldfel, why didn't you just check errno itself against those 2 actual Evalues?"
307 * ygoldfel: "Because it makes me feel better about portable-ish style. Shut up, that's why. Get off my lawn." */
308 {
309 FLOW_LOG_TRACE("Read attempt indicated would-block; not an error condition. Nothing received.");
310 // Subtlety x 2: ...omitted. See similar spot in nb_write_some_with_native_handle(). Same here.
311 *err_code = boost::asio::error::would_block;
312 return 0; // target_payload_hndl already set.
313 }
314 // else
315
316 assert(sys_err_code);
317
318 // All other errors are fatal.
319 FLOW_ERROR_SYS_ERROR_LOG_WARNING(); // Log based on sys_err_code.
320 FLOW_LOG_WARNING("Connected local peer socket tried to read up to [" << target_payload_blob.size() << "] bytes at "
321 "location @ [" << target_payload_blob.data() << "] plus possibly a native handle; "
322 "but an unrecoverable error occurred. Nothing received.");
323 *err_code = sys_err_code;
324 return 0; // target_payload_hndl already set.
325 } // if (n_rcvd_or_error == -1)
326 // else if (n_rcvd_or_error != -1)
327
328 if (n_rcvd_or_error == 0)
329 {
330 /* WARNING doesn't feel right: it's a graceful connection end.
331 * INFO could be good, but it might be too verbose depending on the application.
332 * Use TRACE to be safe; caller can always log differently if desired. */
333 FLOW_LOG_TRACE("Connected local peer socket tried to read up to [" << target_payload_blob.size() << "] bytes at "
334 "location @ [" << target_payload_blob.data() << "] plus possibly a native handle; "
335 "but it returned EOF meaning orderly connection shutdown by peer. Nothing received.");
336 *err_code = boost::asio::error::eof;
337 return 0; // target_payload_hndl already set.
338 }
339 // else
340 assert(n_rcvd_or_error > 0);
341
342 /* Next, target_payload_blob... which is already written to (its first n_rcvd_or_error bytes).
343 *
344 * Next, recvmsg_hdr.msg_flags. Basically only the following is relevant: */
345 if (recvmsg_hdr.msg_flags != 0)
346 {
347 FLOW_LOG_INFO("Connected local peer socket tried to read up to [" << target_payload_blob.size() << "] bytes at "
348 "location @ [" << target_payload_blob.data() << "] plus possibly a native handle; "
349 "and it returned it read [" << n_rcvd_or_error << "] bytes successfully but also returned raw "
350 "out-flags value [0x" << std::hex << recvmsg_hdr.msg_flags << std::dec << "]. "
351 "Will check for relevant flags but otherwise "
352 "ignoring if nothing bad. Logging at elevated level because it's interesting; please investigate.");
353
354 if ((recvmsg_hdr.msg_flags & MSG_CTRUNC) != 0)
355 {
356 FLOW_LOG_WARNING("Connected local peer socket tried to read up to [" << target_payload_blob.size() << "] bytes "
357 "at location @ [" << target_payload_blob.data() << "] plus possibly a native handle; "
358 "and it returned it read [" << n_rcvd_or_error << "] bytes successfully but also returned raw "
359 "out-flags value [0x" << recvmsg_hdr.msg_flags << "] which includes MSG_CTRUNC. "
360 "That flag indicates more stuff was sent as ancillary data; but we expect at most 1 native "
361 "handle. Other side sent something strange. Acting as if nothing received + error.");
363 return 0; // target_payload_hndl already set.
364 }
365 // else
366 }
367
368 /* Lastly examine ancillary data (and note MSG_CTRUNC already eliminated above).
369 * Use, basically, the method from `man cmsg` in Linux. */
370 cmsghdr* const recvmsg_hdr_cmsg_ptr = CMSG_FIRSTHDR(&recvmsg_hdr);
371 if (recvmsg_hdr_cmsg_ptr)
372 {
373 // There is some ancillary data. It can only (validly according to our expected protocol) be one thing.
374 if ((recvmsg_hdr_cmsg_ptr->cmsg_level == SOL_SOCKET) &&
375 (recvmsg_hdr_cmsg_ptr->cmsg_type == SCM_RIGHTS))
376 {
377 static_assert(N_PAYLOAD_FDS == 1, "Should be only dealing with one native handle with recvmsg() "
378 "as of this writing.");
379 target_payload_hndl.m_native_handle
380 = *(reinterpret_cast<const Native_handle::handle_t*>(CMSG_DATA(recvmsg_hdr_cmsg_ptr)));
381 }
382 else
383 {
384 FLOW_LOG_WARNING("Connected local peer socket tried to read up to [" << target_payload_blob.size() << "] bytes "
385 "at location @ [" << target_payload_blob.data() << "] plus possibly a native handle; "
386 "and it returned it read [" << n_rcvd_or_error << "] bytes successfully but also "
387 "unexpected ancillary data of csmg_level|cmsg_type "
388 "[" << recvmsg_hdr_cmsg_ptr->cmsg_level << '|' << recvmsg_hdr_cmsg_ptr->cmsg_type << "]. "
389 "Acting as if nothing received + error.");
391 return 0; // target_payload_hndl already set.
392 }
393 // else
394 if (CMSG_NXTHDR(&recvmsg_hdr, recvmsg_hdr_cmsg_ptr))
395 {
396 /* This is rather strange: we didn't provide enough space for more ancillary data; yet there is somehow more,
397 * even though we didn't detect MSG_CTRUNC earlier. Well, whatever. It's bad just like MSG_CTRUNC. */
398 FLOW_LOG_WARNING("Connected local peer socket tried to read up to [" << target_payload_blob.size() << "] bytes "
399 "at location @ [" << target_payload_blob.data() << "] plus possibly a native handle; "
400 "and it returned it read [" << n_rcvd_or_error << "] bytes and native handle "
401 "[" << target_payload_hndl << "] but also more ancillary data; but we expect at most 1 native "
402 "handle. Other side sent something strange. Acting as if nothing received + error.");
403 target_payload_hndl = Native_handle(); // Undo the above (to match promised semantics on error).
405 return 0;
406 }
407 // else { No more ancillary data, as expected. }
408 }
409 // else { No ancillary data, meaning no native handle; that's quite normal. }
410
411 FLOW_LOG_TRACE("recvmsg() reports receipt of possible native handle [" << target_payload_hndl << "]; as well as "
412 "[" << n_rcvd_or_error << "] of the blob's [" << target_payload_blob.size() << "]-byte capacity.");
413 err_code->clear();
414 return n_rcvd_or_error;
415} // nb_read_some_with_native_handle()
416
417void release_native_peer_socket(Native_handle&& peer_socket_native_or_null)
418{
419 using flow::util::Task_engine;
420
421 // As promised:
422 if (peer_socket_native_or_null.null())
423 {
424 return;
425 }
426 // else
427
428 /* Purely for style reasons let's wrap it in a boost.asio `socket` and let its dtor
429 * take care of it. ::close() would have worked too, but I suppose this is less "native" and more
430 * consistent. */
431 Task_engine task_engine;
432 [[maybe_unused]] Peer_socket sock(task_engine, Protocol(), peer_socket_native_or_null.m_native_handle);
433
434 peer_socket_native_or_null = Native_handle(); // As promised nullify it.
435
436 // Now destroy `sock`, then destroy task_engine.
437} // release_native_peer_socket()
438
439// Opt_peer_process_credentials implementations.
440
442Opt_peer_process_credentials::Opt_peer_process_credentials(const Opt_peer_process_credentials&) = default;
443Opt_peer_process_credentials& Opt_peer_process_credentials::operator=(const Opt_peer_process_credentials&) = default;
444
445} // namespace ipc::transport::asio_local_stream_socket
Opt_peer_process_credentials()
Default ctor: each value is initialized to zero or equivalent.
Opt_peer_process_credentials & operator=(const Opt_peer_process_credentials &src)
Boring copy assignment.
Additional (versus boost.asio) APIs for advanced work with local stream (Unix domain) sockets includi...
local_ns::stream_protocol Protocol
Short-hand for boost.asio Unix domain stream-socket protocol.
size_t nb_write_some_with_native_handle(flow::log::Logger *logger_ptr, Peer_socket *peer_socket_ptr, Native_handle payload_hndl, const util::Blob_const &payload_blob, Error_code *err_code)
boost.asio extension similar to peer_socket->non_blocking(true); auto n = peer_socket->write_some(pay...
Protocol::socket Peer_socket
Short-hand for boost.asio Unix domain peer stream-socket (usually-connected-or-empty guy).
size_t nb_read_some_with_native_handle(flow::log::Logger *logger_ptr, Peer_socket *peer_socket_ptr, Native_handle *target_payload_hndl_ptr, const util::Blob_mutable &target_payload_blob, Error_code *err_code, int message_flags)
boost.asio extension similar to peer_socket->non_blocking(true); auto n = peer_socket->read_some(targ...
void release_native_peer_socket(Native_handle &&peer_socket_native_or_null)
Little utility that returns the raw Native_handle suitable for Peer_socket to the OS.
@ S_LOW_LVL_UNEXPECTED_STREAM_PAYLOAD_BEYOND_HNDL
Unable to receive incoming traffic: message contains more than: 1 blob plus 0-1 native handles.
util::Native_handle Native_handle
Convenience alias for the commonly used type util::Native_handle.
boost::asio::mutable_buffer Blob_mutable
Short-hand for an mutable blob somewhere in memory, stored as exactly a void* and a size_t.
Definition: util_fwd.hpp:140
boost::asio::const_buffer Blob_const
Short-hand for an immutable blob somewhere in memory, stored as exactly a void const * and a size_t.
Definition: util_fwd.hpp:134
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Definition: common.hpp:298
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
int handle_t
The native handle type. Much logic relies on this type being light-weight (fast to copy).
handle_t m_native_handle
The native handle (possibly equal to S_NULL_HANDLE), the exact payload of this Native_handle.