25#include <flow/error/error.hpp>
26#include <flow/log/log.hpp>
27#include <flow/common.hpp>
28#include <boost/array.hpp>
29#include <boost/io/ios_state.hpp>
30#include <boost/noncopyable.hpp>
42#include <sys/socket.h>
45static_assert(
false,
"Should not have gotten to this line; should have required Linux; this header assumes it. "
46 "Might work in other POSIX OS (e.g., macOS) but must be checked/tested.");
56static_assert(
false,
"asio_local_stream_socket::Msg_batch_in is based on Linux-specific recvmmsg() API.");
214template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
216 private boost::noncopyable
236#ifdef IPC_DOXYGEN_ONLY
466 Error_code* err_code =
nullptr,
int message_flags = 0,
467 size_t* total_rcvd_bytes_or_null =
nullptr);
506 template<
typename Is_unused_func>
540 boost::asio::detail::buffer_sequence_adapter<util::Blob_mutable, Mutable_buffer_sequence>
m_buf_seq_adapter;
632static_assert(
false,
"Flow-IPC must define Opt_peer_process_credentials w/ Linux SO_PEERCRED semantics. "
633 "Build in Linux only.");
691 template<
typename Protocol>
692 int level(
const Protocol& proto)
const;
708 template<
typename Protocol>
709 int name(
const Protocol& proto)
const;
725 template<
typename Protocol>
726 void*
data(
const Protocol& proto);
742 template<
typename Protocol>
743 size_t size(
const Protocol& proto)
const;
761 template<
typename Protocol>
762 void resize(
const Protocol& proto,
size_t new_size_but_really_must_equal_current)
const;
767static_assert(std::is_standard_layout_v<util::Process_credentials>
769 "util::Process_credentials must remain layout-identical to a lone `::ucred`; "
770 "Opt_peer_process_credentials::data()/size() rely on it.");
776template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
782 m_buf_seq_adapter(buf_seq),
783 m_resource(std::move(msg_resource))
794template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
798 m_mdts.reserve(max_msg_count);
801 assert((
m_mdts.capacity() == max_msg_count) &&
"We use .capacity() to query max_msg_count.");
802 assert((
m_mmsg_hdrs.capacity() == max_msg_count) &&
"We use .capacity() to query max_msg_count.");
805template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
809 using Buf_seq_adapter =
decltype(Mdt_per_payload::m_buf_seq_adapter);
811 if (idx ==
size_t(-1))
814 assert((!initialized())
815 &&
"At this time the fully-initialized batch size shall be permanently set via ctor; cannot grow.");
817 m_mdts.emplace_back(target_blob, std::move(msg_resource));
818 auto& mdt = m_mdts.back();
823 m_mmsg_hdrs.emplace_back();
824 auto& mmsg_hdr = m_mmsg_hdrs.back();
825 auto& msg_hdr = mmsg_hdr.msg_hdr;
828 msg_hdr.msg_name =
nullptr;
829 msg_hdr.msg_namelen = 0;
831 msg_hdr.msg_iov = mdt.m_buf_seq_adapter.buffers();
833 msg_hdr.msg_iovlen = mdt.m_buf_seq_adapter.count();
834 msg_hdr.msg_control = mdt.m_msg_control_as_union.m_buf.data();
838 msg_hdr.msg_flags = S_PER_MSG_FLAGS;
843 else if (idx < m_mdts.size())
845 auto& mdt = m_mdts[idx];
851 mdt.m_buf_seq_adapter = Buf_seq_adapter{target_blob};
853 mdt.m_resource = std::move(msg_resource);
869 auto& msg_hdr = m_mmsg_hdrs[idx].msg_hdr;
870 msg_hdr.msg_iovlen = mdt.m_buf_seq_adapter.count();
872 assert((msg_hdr.msg_controllen ==
sizeof(mdt.m_msg_control_as_union))
873 &&
"It is initialized to this constant value, and while recv*() shall modify it, nb_read() should have "
874 "immediately reset it again.");
878 assert(
false &&
"prepare_target_payload(x) must either replace an existing slot x or add slot via `x = -1`.");
882template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
886 assert(idx < m_mmsg_hdrs.size());
889 if (msg_resource_ptr)
891 *msg_resource_ptr = &(m_mdts[idx].m_resource);
894 return m_mmsg_hdrs[idx].msg_len;
897template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
900 assert(idx < m_mdts.size());
903 return m_mdts[idx].m_result_hndl;
906template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
909 return m_mdts.size() == m_mdts.capacity();
912template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
918template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
921 return m_n_used == m_mdts.size();
924template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
927 m_n_used = new_n_used;
930template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
968template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
969template<
typename Is_unused_func>
972 const Is_unused_func& is_unused_func)
983 auto& first = start_idx;
985 for (; (first !=
m_n_used) && (!is_unused_func(first)); ++first) {}
993 auto result = first++;
996 if (!is_unused_func(first))
1063 auto& msg_hdr1 = mmsg_hdr1.msg_hdr;
1064 auto& msg_hdr2 = mmsg_hdr2.msg_hdr;
1066 swap(mmsg_hdr1.msg_len, mmsg_hdr2.msg_len);
1067 swap(msg_hdr1.msg_iovlen, msg_hdr2.msg_iovlen);
1074 &&
"It is initialized to this constant value, and while recv*() shall modify it, nb_read() should have "
1075 "immediately reset it again.");
1111template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
1117 size_t* total_rcvd_bytes_or_null)
1119 using flow::log::Sev;
1120 using boost::system::system_category;
1121 using boost::io::ios_all_saver;
1122 namespace sys_err_codes = boost::system::errc;
1131 using ::MSG_DONTWAIT;
1136 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
bool,
nb_read, logger_ptr, peer_socket_ptr, graceful_close, _1, message_flags,
1137 total_rcvd_bytes_or_null);
1140 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_TRANSPORT);
1142 assert((
m_mmsg_hdrs.size() ==
m_mdts.size()) &&
"Gotta be a bug somewhere in Msg_batch_in.");
1145 FLOW_LOG_WARNING(
"Local_stream_batch [" << *
this <<
"]: "
1146 "Attempt to batch-read without adding all [" <<
m_mdts.capacity() <<
"] slots; so far "
1147 "only [" <<
m_mdts.size() <<
"] slots have been added via prepare_target_payload(). "
1148 "This is the API user's error, though since this API is used internally in the library "
1149 "too, it might be a bug in the library. We can't tell from here. Emitting invalid-argument "
1158 if (n_payloads == 0)
1184 assert(peer_socket_ptr);
1185 auto& peer_socket = *peer_socket_ptr;
1194 const bool do_log_trace = logger_ptr && logger_ptr->should_log(Sev::S_TRACE, get_log_component());
1197 int n_rcvd_msgs_or_error;
1201 message_flags = (message_flags | (MSG_DONTWAIT | MSG_TRUNC));
1203 if (n_payloads == 1)
1207 = recvmsg(peer_hndl.m_native_handle, &recvmmsg_hdr_ptr->msg_hdr, message_flags);
1210 FLOW_LOG_TRACE_WITHOUT_CHECKING
1211 (
"Local_stream_batch [" << *
this <<
"]: Connected local peer socket [" << peer_hndl <<
"] "
1212 "tried to batch-read up-to 1 in-message, "
1213 "potentially into a scattered-blob plus possibly a native handle; "
1214 "for efficiency used recvmsg() instead of recvmmsg(); it yielded return value "
1215 "[" << n_rcvd <<
"]; we shall now continue as-if this was done using recvmmsg().");
1220 n_rcvd_msgs_or_error = -1;
1224 n_rcvd_msgs_or_error = 1;
1225 recvmmsg_hdr_ptr->msg_len =
static_cast<unsigned int>(n_rcvd);
1231 n_rcvd_msgs_or_error
1232 = recvmmsg(peer_hndl.m_native_handle, recvmmsg_hdr_ptr, n_payloads, message_flags,
1240 if (n_rcvd_msgs_or_error == -1)
1245 const Error_code sys_err_code{errno, system_category()};
1246 if ((sys_err_code == sys_err_codes::operation_would_block) ||
1247 (sys_err_code == sys_err_codes::resource_unavailable_try_again))
1251 FLOW_LOG_TRACE_WITHOUT_CHECKING
1252 (
"Local_stream_batch [" << *
this <<
"]: Connected local peer socket [" << peer_hndl <<
"] "
1253 "tried to batch-read up-to [" << n_payloads <<
"] in-messages, each "
1254 "potentially into a scattered-blob plus possibly a native handle per each; but read attempt "
1255 "indicated would-block; not an error condition. No in-messages received.");
1257 *err_code = boost::asio::error::would_block;
1262 assert(sys_err_code);
1264 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
1265 FLOW_LOG_WARNING(
"Local_stream_batch [" << *
this <<
"]: "
1266 "Connected local peer socket [" << peer_hndl <<
"] "
1267 "tried to batch-read up-to [" << n_payloads <<
"] in-messages, each "
1268 "potentially into a scattered-blob plus possibly a native handle per each; "
1269 "but an unrecoverable error occurred. No in-messages received.");
1270 *err_code = sys_err_code;
1275 if (n_rcvd_msgs_or_error == 0)
1277 assert((n_payloads > 1) &&
"We should have returned false on 0; and used recvmsg() on 1, resulting in "
1278 "n_rcvd_msgs_or_error being -1 or 1, not zero.");
1282 (
"Local_stream_batch [" << *
this <<
"]: Connected local peer socket [" << peer_hndl <<
"] "
1283 "tried to batch-read up-to [" << n_payloads <<
"] in-messages, each "
1284 "potentially into a scattered-blob plus possibly a native handle per each; "
1285 "but recvmmsg() returned 0; this is an unexpected semantic; EOF (graceful-close) should result in "
1286 "an in-message with msg_len=0 instead. Aborting; please look into this situation; it could be a bug.");
1288 &&
"Tried to batch-read up-to 2+ in-messages, each "
1289 "potentially into a scattered-blob plus possibly a native handle per each; "
1290 "but recvmmsg() returned 0; this is an unexpected semantic; EOF (graceful-close) should result in "
1291 "an in-message with msg_len=0 instead. Aborting; please look into this situation; it could be a bug.");
1303 const auto recvmmsg_hdr_end = recvmmsg_hdr_ptr + n_rcvd_msgs_or_error;
1322 const auto clean_remaining_func = [&]() ->
bool
1324 assert((recvmmsg_hdr_ptr != recvmmsg_hdr_end) &&
"Internal pre-condition violated.");
1333 auto& recvmsg_hdr = recvmmsg_hdr_ptr->msg_hdr;
1335 const auto recvmsg_hdr_cmsg_ptr = CMSG_FIRSTHDR(&recvmsg_hdr);
1336 if (recvmsg_hdr_cmsg_ptr
1337 && (recvmsg_hdr_cmsg_ptr->cmsg_level == SOL_SOCKET)
1338 && (recvmsg_hdr_cmsg_ptr->cmsg_type == SCM_RIGHTS))
1353 while ((++recvmmsg_hdr_ptr) != recvmmsg_hdr_end);
1362 while (mdt_ptr != mdt_ptr_first)
1364 (--mdt_ptr)->m_result_hndl.close();
1370 size_t n_rcvd_msgs_actual = n_rcvd_msgs_or_error;
1371 size_t total_rcvd_bytes = 0;
1374 auto& recvmsg_hdr = recvmmsg_hdr_ptr->msg_hdr;
1375 auto& mdt = *mdt_ptr;
1382 const size_t target_payload_blob_sz = mdt.m_buf_seq_adapter.total_size();
1383 const auto msg_len = recvmmsg_hdr_ptr->msg_len;
1384 if (msg_len > target_payload_blob_sz)
1386 FLOW_LOG_WARNING(
"Local_stream_batch [" << *
this <<
"]: "
1387 "Connected local peer socket [" << peer_hndl <<
"] "
1388 "tried to batch-read up-to [" << n_payloads <<
"] in-messages, "
1389 "each potentially into a scattered-blob plus possibly a native handle per each; "
1390 "and it returned it read [" << n_rcvd_msgs_or_error <<
"] in-messages; and for message "
1391 "index [" << (n_rcvd_msgs_or_error - (recvmmsg_hdr_end - recvmmsg_hdr_ptr)) <<
"] (0-based) "
1392 "got [" << msg_len <<
"] bytes successfully, but this overflows "
1393 "the blob's capacity [" << target_payload_blob_sz <<
"]. "
1394 "Acting as if nothing received + error.");
1395 assert((recvmsg_hdr.msg_flags & MSG_TRUNC)
1396 &&
"In-dgram size overflows user-supplied buffer <=> MSG_TRUNC out-flag supposed to be set.");
1407 return clean_remaining_func();
1417 swap(out_flags, recvmsg_hdr.msg_flags);
1421 if (logger_ptr && logger_ptr->should_log(Sev::S_INFO, get_log_component()))
1423 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
1424 FLOW_LOG_INFO_WITHOUT_CHECKING
1425 (
"Local_stream_batch [" << *
this <<
"]: Connected local peer socket [" << peer_hndl <<
"] "
1426 "tried to batch-read up-to [" << n_payloads <<
"] in-messages, each "
1427 "potentially into a scattered-blob plus possibly a native handle per each; "
1428 "and it returned it read [" << n_rcvd_msgs_or_error <<
"] in-messages; and for message "
1429 "index [" << (n_rcvd_msgs_or_error - (recvmmsg_hdr_end - recvmmsg_hdr_ptr)) <<
"] (0-based) "
1430 "got [" << msg_len <<
"] bytes successfully but also returned raw "
1431 "out-flags value [0x" << std::hex << out_flags <<
"]. "
1432 "Will check for relevant flags but otherwise ignoring if nothing bad. "
1433 "Logging at elevated level because it's interesting; please investigate.");
1436 if ((out_flags & MSG_CTRUNC) != 0)
1438 if (logger_ptr && logger_ptr->should_log(Sev::S_WARNING, get_log_component()))
1440 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
1441 FLOW_LOG_WARNING_WITHOUT_CHECKING
1442 (
"Local_stream_batch [" << *
this <<
"]: Connected local peer socket [" << peer_hndl <<
"] "
1443 "tried to batch-read up-to [" << n_payloads <<
"] in-messages, "
1444 "each potentially into a scattered-blob plus possibly a native handle per each; "
1445 "and it returned it read [" << n_rcvd_msgs_or_error <<
"] in-messages; and for message "
1446 "index [" << (n_rcvd_msgs_or_error - (recvmmsg_hdr_end - recvmmsg_hdr_ptr)) <<
"] (0-based) "
1447 "got [" << msg_len <<
"] bytes successfully but also returned raw "
1448 "out-flags value [0x" << std::hex << out_flags <<
"] which includes MSG_CTRUNC. "
1449 "That flag indicates more stuff was sent as ancillary data; but we expect at most 1 native "
1450 "handle. Other side sent something strange. Acting as if nothing received + error.");
1456 return clean_remaining_func();
1468 cmsghdr*
const recvmsg_hdr_cmsg_ptr = CMSG_FIRSTHDR(&recvmsg_hdr);
1469 if (recvmsg_hdr_cmsg_ptr)
1471 if ((recvmsg_hdr_cmsg_ptr->cmsg_level == SOL_SOCKET) &&
1472 (recvmsg_hdr_cmsg_ptr->cmsg_type == SCM_RIGHTS))
1474 static_assert(
N_PAYLOAD_FDS == 1,
"Should be only dealing with one native handle with recvmsg() "
1475 "as of this writing.");
1476 memcpy(&mdt.m_result_hndl.m_native_handle, CMSG_DATA(recvmsg_hdr_cmsg_ptr),
1477 sizeof(mdt.m_result_hndl.m_native_handle));
1481 FLOW_LOG_WARNING(
"Local_stream_batch [" << *
this <<
"]: "
1482 "Connected local peer socket [" << peer_hndl <<
"] "
1483 "tried to batch-read up-to [" << n_payloads <<
"] in-messages, "
1484 "each potentially into a scattered-blob plus possibly a native handle per each; "
1485 "and it returned it read [" << n_rcvd_msgs_or_error <<
"] in-messages; and for message "
1486 "index [" << (n_rcvd_msgs_or_error - (recvmmsg_hdr_end - recvmmsg_hdr_ptr)) <<
"] (0-based) "
1487 "got [" << msg_len <<
"] bytes successfully but also "
1488 "unexpected ancillary data of cmsg_level|cmsg_type "
1489 "[" << recvmsg_hdr_cmsg_ptr->cmsg_level <<
'|' << recvmsg_hdr_cmsg_ptr->cmsg_type <<
"]. "
1490 "Acting as if nothing received + error.");
1495 return clean_remaining_func();
1502 mdt.m_result_hndl = {};
1517 const bool eof_marker = (msg_len == 0) && mdt.m_result_hndl.null();
1542 n_rcvd_msgs_actual = std::min(n_rcvd_msgs_actual,
1544 size_t(
size_t(n_rcvd_msgs_or_error)
1545 -
size_t(recvmmsg_hdr_end - recvmmsg_hdr_ptr)));
1550 FLOW_LOG_TRACE_WITHOUT_CHECKING
1551 (
"Local_stream_batch [" << *
this <<
"]: "
1552 "recvmmsg[" << peer_hndl <<
"] reports for message index "
1553 "[" << (n_rcvd_msgs_or_error - (recvmmsg_hdr_end - recvmmsg_hdr_ptr)) <<
"] (0-based) receipt of possible "
1554 "native handle [" << mdt.m_result_hndl <<
"]; as well as "
1555 "[" << msg_len <<
"] bytes of the blob's capacity [" << target_payload_blob_sz <<
"]; "
1556 "EOF-marker? = [" << eof_marker <<
"]. "
1557 "Total in-message records received: [" << n_rcvd_msgs_or_error <<
"] out of max [" << n_payloads <<
"]. "
1558 "Potentially corrected (due to 1st EOF-marker record) in-message count: [" << n_rcvd_msgs_actual <<
"].");
1561 total_rcvd_bytes += msg_len;
1565 while ((++mdt_ptr, ++recvmmsg_hdr_ptr) != recvmmsg_hdr_end);
1567 assert(graceful_close);
1568 if (n_rcvd_msgs_actual ==
size_t(n_rcvd_msgs_or_error))
1571 *graceful_close =
false;
1577 assert((n_rcvd_msgs_or_error > 0) &&
"n=0 and n=-1 should have been dealt with earlier + return.");
1578 *graceful_close =
true;
1582 FLOW_LOG_TRACE_WITHOUT_CHECKING
1583 (
"Local_stream_batch [" << *
this <<
"]: Connected local peer socket [" << peer_hndl <<
"] "
1584 "tried to batch-read up-to [" << n_payloads <<
"] in-messages, each "
1585 "potentially into a scattered-blob plus possibly a native handle per each; "
1586 "and got ostensibly [" << n_rcvd_msgs_or_error <<
"] of those; but only [" << n_rcvd_msgs_actual <<
"] were "
1587 "actual in-messages (0 = allowed), while the rest were EOF-markers; therefore emitting "
1588 "graceful-close=1 flag.");
1594 if (total_rcvd_bytes_or_null)
1596 *total_rcvd_bytes_or_null = total_rcvd_bytes;
1603template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
1607 os <<
"Lcl[slots-total/rdy/rcvd [" <<
m_mdts.size() <<
'/' <<
m_mdts.capacity() <<
'/' <<
m_n_used <<
"]]@" <<
this;
1610template<
typename Mutable_buffer_sequence_t,
typename Msg_resource_t>
1620template<
typename Protocol,
typename Const_buffer_sequence>
1624 const Const_buffer_sequence& payload_blob,
1628 using boost::system::system_category;
1630 using boost::asio::detail::buffer_sequence_adapter;
1631 using std::is_same_v;
1633 namespace sys_err_codes = boost::system::errc;
1639 using ::MSG_DONTWAIT;
1640 using ::MSG_NOSIGNAL;
1643 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
size_t, (nb_write_some_with_native_handle<Protocol, Const_buffer_sequence>),
1644 logger_ptr, peer_socket_ptr, payload_hndl, payload_blob, _1);
1651 assert(peer_socket_ptr);
1652 auto& peer_socket = *peer_socket_ptr;
1654 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_TRANSPORT);
1662 buffer_sequence_adapter<Blob_const, Const_buffer_sequence> buf_seq_adapter{payload_blob};
1663 const size_t payload_blob_sz = buf_seq_adapter.total_size();
1665 FLOW_LOG_TRACE(
"Connected local peer socket wants to write scattered-blob "
1666 "([" << buf_seq_adapter.count() <<
"] sub-blobs, total size [" << payload_blob_sz <<
"], "
1667 "first sub-blob at @[" << buf_seq_adapter.buffers()->iov_base <<
"]) "
1668 "plus native handle [" << payload_hndl <<
"]. Will try to send.");
1683 msghdr sendmsg_hdr =
1690 buf_seq_adapter.buffers(),
1691 buf_seq_adapter.count(),
1693 msg_control_as_union.
m_buf.data(),
1700 cmsghdr*
const sendmsg_hdr_cmsg_ptr = CMSG_FIRSTHDR(&sendmsg_hdr);
1701 sendmsg_hdr_cmsg_ptr->cmsg_level = SOL_SOCKET;
1702 sendmsg_hdr_cmsg_ptr->cmsg_type = SCM_RIGHTS;
1705 static_assert(
N_PAYLOAD_FDS == 1,
"Should be only passing one native handle into sendmsg() as of this writing.");
1708 const auto n_sent_or_error
1709 = sendmsg(peer_socket.native_handle(), &sendmsg_hdr,
1715 MSG_DONTWAIT | MSG_NOSIGNAL);
1718 if (n_sent_or_error == -1)
1725 const Error_code sys_err_code{errno, system_category()};
1726 if ((sys_err_code == sys_err_codes::operation_would_block) ||
1727 (sys_err_code == sys_err_codes::resource_unavailable_try_again))
1729 FLOW_LOG_TRACE(
"Write attempt indicated would-block; not an error condition. Nothing sent.");
1740 *err_code = boost::asio::error::would_block;
1745 assert(sys_err_code);
1748 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
1749 FLOW_LOG_WARNING(
"Connected local peer socket tried to write scattered-blob "
1750 "([" << buf_seq_adapter.count() <<
"] sub-blobs, total size [" << payload_blob_sz <<
"], "
1751 "first sub-blob at @[" << buf_seq_adapter.buffers()->iov_base <<
"]) "
1752 "plus native handle [" << payload_hndl <<
"]; "
1753 "but an unrecoverable error occurred. Nothing sent.");
1754 *err_code = sys_err_code;
1759 if constexpr(is_same_v<Protocol, Protocol_pkt_stream>)
1761 assert((
static_cast<size_t>(n_sent_or_error) == payload_blob_sz)
1762 &&
"sendmsg() returned neither -1 nor full-dgram-sent length; unexpected behavior for SEQPACKET! "
1763 "Dgram too large? Misunderstood/misdocumented OS API?");
1765 FLOW_LOG_TRACE(
"sendmsg() reports the native handle [" << payload_hndl <<
"] was successfully sent; as "
1766 "were all of the dgram blob's [" << payload_blob_sz <<
"] bytes.");
1770 assert((n_sent_or_error > 0)
1771 &&
"sendmsg() should return either -1 or a positive number (indicates success).");
1773 FLOW_LOG_TRACE(
"sendmsg() reports the native handle [" << payload_hndl <<
"] was successfully sent; as "
1774 "were [" << n_sent_or_error <<
"] of the blob's [" << payload_blob_sz <<
"] bytes.");
1778 return static_cast<size_t>(n_sent_or_error);
1781template<
typename Protocol,
typename Mutable_buffer_sequence>
1789 using flow::log::Sev;
1790 using boost::asio::detail::buffer_sequence_adapter;
1791 using boost::io::ios_all_saver;
1792 using boost::system::system_category;
1794 using std::is_same_v;
1796 namespace sys_err_codes = boost::system::errc;
1802 using ::MSG_DONTWAIT;
1807 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
size_t, (nb_read_some_with_native_handle<Protocol, Mutable_buffer_sequence>),
1808 logger_ptr, peer_socket_ptr, target_payload_hndl_ptr,
1809 target_payload_blob, _1, message_flags);
1816 assert(peer_socket_ptr);
1817 assert(target_payload_hndl_ptr);
1819 buffer_sequence_adapter<util::Blob_mutable, Mutable_buffer_sequence> buf_seq{target_payload_blob};
1821 auto& peer_socket = *peer_socket_ptr;
1822 auto& target_payload_hndl = *target_payload_hndl_ptr;
1824 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_TRANSPORT);
1825 target_payload_hndl = {};
1835 const size_t target_payload_blob_sz = buf_seq.total_size();
1852 msghdr recvmsg_hdr =
1870 msg_control_as_union.
m_buf.data(),
1879 if constexpr(is_same_v<Protocol, Protocol_pkt_stream>)
1882 message_flags = (message_flags | (MSG_DONTWAIT | MSG_TRUNC));
1886 message_flags = (message_flags | MSG_DONTWAIT);
1889 const auto n_rcvd_or_error
1890 = recvmsg(peer_socket.native_handle(), &recvmsg_hdr, message_flags);
1894 if (n_rcvd_or_error == -1)
1899 const Error_code sys_err_code{errno, system_category()};
1900 if ((sys_err_code == sys_err_codes::operation_would_block) ||
1901 (sys_err_code == sys_err_codes::resource_unavailable_try_again))
1905 FLOW_LOG_TRACE(
"Connected local peer socket tried to read into scattered-blob "
1906 "([" << buf_seq.count() <<
"] sub-blobs, "
1907 "up to total size [" << target_payload_blob_sz <<
"], "
1908 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
1909 "plus possibly a native handle; and read attempt indicated would-block; "
1910 "not an error condition. Nothing received.");
1912 *err_code = boost::asio::error::would_block;
1917 assert(sys_err_code);
1920 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
1921 FLOW_LOG_WARNING(
"Connected local peer socket tried to read into scattered-blob "
1922 "([" << buf_seq.count() <<
"] sub-blobs, "
1923 "up to total size [" << target_payload_blob_sz <<
"], "
1924 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
1925 "plus possibly a native handle; but an unrecoverable error occurred. Nothing received.");
1926 *err_code = sys_err_code;
1931 if (n_rcvd_or_error == 0)
1936 FLOW_LOG_TRACE(
"Connected local peer socket tried to read into scattered-blob "
1937 "([" << buf_seq.count() <<
"] sub-blobs, "
1938 "up to total size [" << target_payload_blob_sz <<
"], "
1939 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
1940 "plus possibly a native handle; "
1941 "but it returned EOF meaning orderly connection shutdown by peer. Nothing received.");
1942 *err_code = boost::asio::error::eof;
1950 if constexpr(is_same_v<Protocol, Protocol_pkt_stream>)
1952 if (
size_t(n_rcvd_or_error) > target_payload_blob_sz)
1956 FLOW_LOG_WARNING(
"Connected local peer socket tried to read into scattered-blob "
1957 "([" << buf_seq.count() <<
"] sub-blobs, "
1958 "up to total size [" << target_payload_blob_sz <<
"], "
1959 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
1960 "plus possibly a native handle; "
1961 "and it returned it read [" << n_rcvd_or_error <<
"] bytes successfully, but this overflows "
1962 "the aforementioned total-size; emitting error accordingly. Socket remains operational, but "
1963 "the overflowing in-dgram is lost.");
1966 assert((recvmsg_hdr.msg_flags & MSG_TRUNC)
1967 &&
"In-dgram size overflows user-supplied buffer <=> MSG_TRUNC out-flag supposed to be set.");
1982 if ((!*err_code) && (recvmsg_hdr.msg_flags != 0))
1984 if (logger_ptr && logger_ptr->should_log(Sev::S_INFO, get_log_component()))
1986 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
1987 FLOW_LOG_INFO_WITHOUT_CHECKING
1988 (
"Connected local peer socket tried to read into scattered-blob "
1989 "([" << buf_seq.count() <<
"] sub-blobs, "
1990 "up to total size [" << target_payload_blob_sz <<
"], "
1991 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
1992 "plus possibly a native handle; "
1993 "and it returned it read [" << n_rcvd_or_error <<
"] bytes successfully but also returned raw "
1994 "out-flags value [0x" << std::hex << recvmsg_hdr.msg_flags <<
"]. "
1995 "Will check for relevant flags but otherwise "
1996 "ignoring if nothing bad. Logging at elevated level because it's interesting; please investigate.");
1999 if ((recvmsg_hdr.msg_flags & MSG_CTRUNC) != 0)
2001 if (logger_ptr && logger_ptr->should_log(Sev::S_WARNING, get_log_component()))
2003 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
2004 FLOW_LOG_WARNING_WITHOUT_CHECKING
2005 (
"Connected local peer socket tried to read into scattered-blob "
2006 "([" << buf_seq.count() <<
"] sub-blobs, "
2007 "up to total size [" << target_payload_blob_sz <<
"], "
2008 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
2009 "plus possibly a native handle; "
2010 "and it returned it read [" << n_rcvd_or_error <<
"] bytes successfully but also returned raw "
2011 "out-flags value [0x" << std::hex << recvmsg_hdr.msg_flags <<
"] which includes MSG_CTRUNC. "
2012 "That flag indicates more stuff was sent as ancillary data; but we expect at most 1 native "
2013 "handle. Other side sent something strange. Acting as if nothing received + error.");
2026 cmsghdr*
const recvmsg_hdr_cmsg_ptr = CMSG_FIRSTHDR(&recvmsg_hdr);
2027 if (recvmsg_hdr_cmsg_ptr)
2030 if ((recvmsg_hdr_cmsg_ptr->cmsg_level == SOL_SOCKET) &&
2031 (recvmsg_hdr_cmsg_ptr->cmsg_type == SCM_RIGHTS))
2034 "Should be only dealing with one native handle with recvmsg() as of this writing.");
2035 memcpy(&target_payload_hndl.m_native_handle, CMSG_DATA(recvmsg_hdr_cmsg_ptr),
2036 sizeof(target_payload_hndl.m_native_handle));
2040 target_payload_hndl.close();
2044 else if (!*err_code)
2046 FLOW_LOG_WARNING(
"Connected local peer socket tried to read into scattered-blob "
2047 "([" << buf_seq.count() <<
"] sub-blobs, "
2048 "up to total size [" << target_payload_blob_sz <<
"], "
2049 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
2050 "plus possibly a native handle; "
2051 "and it returned it read [" << n_rcvd_or_error <<
"] bytes successfully but also "
2052 "unexpected ancillary data of cmsg_level|cmsg_type "
2053 "[" << recvmsg_hdr_cmsg_ptr->cmsg_level <<
'|' << recvmsg_hdr_cmsg_ptr->cmsg_type <<
"]. "
2054 "Acting as if nothing received + error.");
2073 if constexpr(is_same_v<Protocol, Protocol_pkt_stream>)
2075 FLOW_LOG_TRACE(
"Connected local peer socket tried to read into scattered-blob "
2076 "([" << buf_seq.count() <<
"] sub-blobs, "
2077 "up to total size [" << target_payload_blob_sz <<
"], "
2078 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
2079 "plus possibly a native handle; recvmsg() reports receipt of possible "
2080 "native handle [" << target_payload_hndl <<
"]; as well as "
2081 "an in-dgram taking [" << n_rcvd_or_error <<
"] of the "
2082 "blob's [" << target_payload_blob_sz <<
"]-byte capacity.");
2086 FLOW_LOG_TRACE(
"Connected local peer socket tried to read into scattered-blob "
2087 "([" << buf_seq.count() <<
"] sub-blobs, "
2088 "up to total size [" << target_payload_blob_sz <<
"], "
2089 "first sub-blob at @[" << buf_seq.buffers()->iov_base <<
"]) "
2090 "plus possibly a native handle; recvmsg() reports receipt of possible "
2091 "native handle [" << target_payload_hndl <<
"]; as well as "
2092 "[" << n_rcvd_or_error <<
"] of the blob's [" << target_payload_blob_sz <<
"]-byte capacity.");
2095 return static_cast<size_t>(n_rcvd_or_error);
2100template<
typename Protocol>
2106template<
typename Protocol>
2112template<
typename Protocol>
2118template<
typename Protocol>
2124template<
typename Protocol>
2127 using flow::util::ostream_op_string;
2128 using std::length_error;
2130 if (new_size_but_really_must_equal_current != size(proto))
2132 throw length_error(ostream_op_string
2133 (
"Opt_peer_process_credentials does not actually support resizing; requested size [",
2134 new_size_but_really_must_equal_current,
"] differs from forever-size [",
2135 size(proto),
"]. boost.asio internal bug or misuse?"));
Similarly to the transport::Msg_batch_in concept (as taken by API {Blob|Native_handle}_receiver::asyn...
std::ostream & operator<<(std::ostream &os, const Msg_batch_in< Mutable_buffer_sequence_t, Msg_resource_t > &val)
Serializes a Msg_batch_in to a standard output stream.
Native_handle result_payload_hndl(size_t idx) const
Obtains a copy of the Native_handle (potentially .null()) in an earlier-received-to slot.
bool full() const
Identical to Generic_msg_batch_in.
Msg_batch_in(size_t max_msg_count)
Identical to ctor of Generic_msg_batch_in.
void to_ostream(std::ostream *os) const
Identical to Generic_msg_batch_in.
void reuse_result_payloads(size_t start_idx, const Is_unused_func &is_unused_func)
Scans every in-message slot in range [start_idx, this->n_used()); performantly rearranges this range ...
static constexpr int S_PER_MSG_FLAGS
Value we give for msghdr::msg_flags as in-arg. It is also an out-arg, so sometimes we must reassign t...
Msg_resource_t Msg_resource
Alias for template param Msg_resource_t.
size_t result_payload_blob(size_t idx, Msg_resource **msg_resource_ptr=nullptr)
Obtains information, chiefly the size of received data <= total-size of corresponding target_blob fro...
~Msg_batch_in()
Clears all resources – including any Msg_resource currently attached to any receive-slot.
size_t n_used() const
In range [0, max_msg_count] (see ctor), this is the number of slots, starting with the 1st slot ([0])...
void clear_used(size_t new_n_used=0)
Makes it so that n_used() would return zero (or, in advanced form, another value).
bool initialized() const
Identical to Generic_msg_batch_in.
size_t m_n_used
See n_used().
std::vector< Mdt_per_payload > m_mdts
The slots, excluding what is in m_mmsg_hdrs.
std::vector<::mmsghdr > m_mmsg_hdrs
The slots, excluding what is in m_mdts; a sub-array of this is passed by nb_read() to recvmmsg().
void prepare_target_payload(const Mutable_buffer_sequence &target_blob, Msg_resource &&msg_resource, size_t idx=-1)
Identical to Generic_msg_batch_in (though the type of target_blob is potentially more complex).
bool nb_read(flow::log::Logger *logger_ptr, Peer_socket< Protocol_pkt_stream > *peer_socket_ptr, bool *graceful_close, Error_code *err_code=nullptr, int message_flags=0, size_t *total_rcvd_bytes_or_null=nullptr)
Performs a high-perf non-blocking batched-receive into the batch area with indices [n_used(),...
Mutable_buffer_sequence_t Mutable_buffer_sequence
Buffer(s) description type (conforming to boost.asio MutableBufferSequence concept) supported by *thi...
Gettable (read-only) socket option for use with asio_local_stream_socket::Peer_socket ....
size_t size(const Protocol &proto) const
For internal boost.asio use, to enable Peer_socket::get_option(Opt_peer_process_credentials&) to work...
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.
int level(const Protocol &proto) const
For internal boost.asio use, to enable Peer_socket::get_option(Opt_peer_process_credentials&) to work...
Opt_peer_process_credentials(const Opt_peer_process_credentials &src)
Boring copy ctor.
void * data(const Protocol &proto)
For internal boost.asio use, to enable Peer_socket::get_option(Opt_peer_process_credentials&) to work...
int name(const Protocol &proto) const
For internal boost.asio use, to enable Peer_socket::get_option(Opt_peer_process_credentials&) to work...
void resize(const Protocol &proto, size_t new_size_but_really_must_equal_current) const
For internal boost.asio use, to enable Peer_socket::get_option(Opt_peer_process_credentials&) to work...
A process's credentials (PID, UID, GID as of this writing).
Additional (versus boost.asio) APIs for advanced work with local (Unix domain) stream and connected-d...
typename 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< Protocol > *peer_socket_ptr, Native_handle *target_payload_hndl_ptr, const Mutable_buffer_sequence &target_payload_blob, Error_code *err_code, int message_flags)
boost.asio extension similar to peer_socket->non_blocking(true); auto n = peer_socket->receive(target...
constexpr size_t N_PAYLOAD_FDS
Our transport::asio_local_stream_socket utilities support transmitting at most this many native handl...
size_t nb_write_some_with_native_handle(flow::log::Logger *logger_ptr, Peer_socket< Protocol > *peer_socket_ptr, Native_handle payload_hndl, const Const_buffer_sequence &payload_blob, Error_code *err_code)
boost.asio extension similar to peer_socket->non_blocking(true); auto n = peer_socket->send(payload_b...
@ S_LOW_LVL_UNEXPECTED_STREAM_PAYLOAD_BEYOND_HNDL
Unable to receive incoming traffic: message contains more than: 1 blob plus 0-1 native handles.
@ S_INVALID_ARGUMENT
User called an API with 1 or more arguments against the API spec.
@ S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE
User protocol-code mismatch: local user-provided storage cannot fit entire message received from oppo...
void swap(Bipc_mq_handle &val1, Bipc_mq_handle &val2)
Implements Persistent_mq_handle related concept: Swaps two objects.
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.
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
The Msg_resource type attached to each slot of our internal m_batch.
Per-batch-slot information beyond what is stored in the corresponding slot's native mmsghdr m_mmsg_hd...
Mdt_per_payload(const Mutable_buffer_sequence &buf_seq, Msg_resource &&msg_resource)
Ctor.
Native_handle m_result_hndl
The target/result Native_handle to receive-to for this slot; null originally; null or not upon a rece...
void swap(Mdt_per_payload *that)
Implements maximally efficient swapping, as needed by reuse_result_payloads().
Msg_control_as_union m_msg_control_as_union
Stores this slot's ancillary-data cmsg* structure, into which ancillary data if any are received.
boost::asio::detail::buffer_sequence_adapter< util::Blob_mutable, Mutable_buffer_sequence > m_buf_seq_adapter
Efficient (via adapt-as-we-go technique) object that adapts the boost.asio portable Mutable_buffer_se...
Msg_resource m_resource
Per-slot resource moved from user's object via prepare_target_payload().
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
void close() noexcept
Little utility that returns m_native_handle to the OS.
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.
Ancillary data (for recv[m]msg() and send[m]msg()) buffer, used in our case for storing transmitted n...
boost::array< uint8_t, CMSG_SPACE(sizeof(Native_handle::handle_t) *N_PAYLOAD_FDS)> m_buf
The ancillary-data buffer.