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