25RCSID(
"$Id: bfff00040856d6db11cbd363c142bed189dd0340 $")
27#include <freeradius-devel/io/atomic_queue.h>
28#include <freeradius-devel/io/control.h>
29#include <freeradius-devel/util/debug.h>
30#include <freeradius-devel/util/strerror.h>
31#include <freeradius-devel/util/syserror.h>
32#include <freeradius-devel/util/misc.h>
33#include <freeradius-devel/util/rand.h>
35#ifdef HAVE_STDATOMIC_H
38# include <freeradius-devel/util/stdatomic.h>
45#include <sys/socket.h>
51#define CACHE_LINE_BYTES (128)
67static_assert(
sizeof(
fr_control_header_t) == 8,
"control message data must stay 8 byte aligned");
77 _Alignas(
struct cmsghdr)
uint8_t buf[CMSG_SPACE(
sizeof(
int))];
86#define CONTROL_SOCKET_MSGS_PER_READ (64)
94#define CONTROL_SOCKET_DATAGRAMS (512)
120 unsigned int num = 0;
122 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0)
return;
124 (void) setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &bufsize,
sizeof(bufsize));
125 (void) setsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &bufsize,
sizeof(bufsize));
130 iov.iov_len =
sizeof(hdr);
132 memset(&cmsg, 0,
sizeof(cmsg));
133 memset(&mh, 0,
sizeof(mh));
136 mh.msg_control = &cmsg;
137 mh.msg_controllen =
sizeof(cmsg);
139 ch = CMSG_FIRSTHDR(&mh);
140 ch->cmsg_level = SOL_SOCKET;
141 ch->cmsg_type = SCM_RIGHTS;
142 ch->cmsg_len = CMSG_LEN(
sizeof(
int));
143 memcpy(CMSG_DATA(ch), &sv[1],
sizeof(
int));
145 while (sendmsg(sv[1], &mh, 0) ==
sizeof(hdr)) num++;
152 len =
sizeof(sndbuf);
153 if (getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, &
len) < 0) sndbuf = bufsize;
154 len =
sizeof(rcvbuf);
155 if (getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, &
len) < 0) rcvbuf = bufsize;
161 mh.msg_controllen =
sizeof(cmsg);
162 if (recvmsg(sv[0], &mh, 0) < 0)
break;
163 ch = CMSG_FIRSTHDR(&mh);
164 if (ch && (ch->cmsg_level == SOL_SOCKET) && (ch->cmsg_type == SCM_RIGHTS)) {
165 memcpy(&sock, CMSG_DATA(ch),
sizeof(sock));
179#ifdef MSG_CMSG_CLOEXEC
180# define CONTROL_RECV_FLAGS MSG_CMSG_CLOEXEC
182# define CONTROL_RECV_FLAGS 0
257 return &eventset->
events[ev];
278 unsigned int num = 0;
303 struct msghdr *mh = &mmh->msg_hdr;
304 struct cmsghdr *ch = CMSG_FIRSTHDR(mh);
308 if (!ch || (ch->cmsg_level != SOL_SOCKET) || (ch->cmsg_type != SCM_RIGHTS) ||
309 (ch->cmsg_len != CMSG_LEN(
sizeof(
int)))) {
310 fr_cond_assert_msg(!(mh->msg_flags & MSG_CTRUNC),
"Control socket message truncated");
313 memcpy(&sock, CMSG_DATA(ch),
sizeof(sock));
314#ifndef MSG_CMSG_CLOEXEC
318 if (!
fr_cond_assert_msg(!(mh->msg_flags & (MSG_TRUNC | MSG_CTRUNC)),
"Control socket message truncated") ||
319 !
fr_cond_assert_msg(mmh->msg_len ==
sizeof(*hdr),
"Malformed control message with socket")) {
347 c->
socket.msgs[i].msg_hdr.msg_controllen =
sizeof(c->
socket.cmsg[i]);
348 c->
socket.msgs[i].msg_hdr.msg_flags = 0;
353 }
while ((num < 0) && (errno == EINTR));
460 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, c->
fd) < 0) {
473 (void) setsockopt(c->
fd[0], SOL_SOCKET, SO_RCVBUF, &bufsize,
sizeof(bufsize));
474 (void) setsockopt(c->
fd[1], SOL_SOCKET, SO_SNDBUF, &bufsize,
sizeof(bufsize));
502 c->
socket.msgs[i].msg_hdr.msg_iovlen = 1;
503 c->
socket.msgs[i].msg_hdr.msg_control = &c->
socket.cmsg[i];
545 void const *
data,
size_t data_size)
556 if (!cctx)
return -1;
562 if (!
fr_cond_assert_msg(data_size <= c->max_size,
"Control message of %zu bytes exceeds max_size %u",
568 memcpy(rec +
sizeof(*hdr),
data, data_size);
583 slen = write(c->
fd[1],
".", 1);
584 }
while ((slen < 0) && (errno == EINTR));
586 if ((slen < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != ENOBUFS)) {
621 iov.iov_len =
sizeof(hdr);
623 memset(&cmsg, 0,
sizeof(cmsg));
624 memset(&mh, 0,
sizeof(mh));
627 mh.msg_control = &cmsg;
628 mh.msg_controllen =
sizeof(cmsg);
630 ch = CMSG_FIRSTHDR(&mh);
631 ch->cmsg_level = SOL_SOCKET;
632 ch->cmsg_type = SCM_RIGHTS;
633 ch->cmsg_len = CMSG_LEN(
sizeof(sock));
634 memcpy(CMSG_DATA(ch), &sock,
sizeof(sock));
637 slen = sendmsg(c->
fd[1], &mh, 0);
638 }
while ((slen < 0) && (errno == EINTR));
793 fr_strerror_printf(
"Failed deleting unknown event %" PRIu16
" in event set %" PRIu16, ev, es);
820 fr_strerror_printf(
"Failed deleting unknown socket event %" PRIu16
" in event set %" PRIu16, ev, es);
891 talloc_set_destructor(c, NULL);
916 (void) poll(&fd, 1, -1);
bool fr_atomic_queue_mpmc_push(fr_atomic_queue_t *aq, void const *data)
Copy an element into the atomic queue, any number of producers.
fr_atomic_queue_t * fr_atomic_queue_talloc(TALLOC_CTX *ctx, size_t num, size_t elem_len)
Create a fixed-size atomic queue.
bool fr_atomic_queue_spsc_pop(fr_atomic_queue_t *aq, void *data)
Copy an element out of the atomic queue, one consumer only.
Structure to hold the atomic queue.
fr_control_eventset_t
Who defines control plane events.
@ FR_CONTROL_EVENTSET_MAX
void(* fr_control_socket_callback_t)(int sock, fr_time_t now, void *uctx)
A callback for a message which carries a socket.
void(* fr_control_callback_t)(void const *data, size_t data_size, fr_time_t now, void *uctx)
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define fr_event_fd_insert(...)
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
fr_atomic_queue_t * aq
the messages, header and data by value.
fr_control_socket_ctx_t * socket_events
callbacks for messages carrying a socket, indexed by event
#define CONTROL_SOCKET_DATAGRAMS
bool fr_control_sleeping(fr_control_t *c)
Record that the reader of this control plane is about to block.
static void control_socket_dgram_size_measure(void)
Measure what one datagram carrying a socket takes up in a socket buffer.
static unsigned int control_service(fr_control_t *c)
Dispatch every queued message.
#define CONTROL_RECV_FLAGS
void fr_control_awake(fr_control_t *c)
Record that the reader of this control plane is running again.
void * uctx
context for the callback
static int _control_free(fr_control_t *c)
Free a control structure.
static void control_socket_dispatch(fr_control_t *c, struct mmsghdr *mmh, fr_control_header_t const *hdr, fr_time_t now)
Dispatch one datagram taken off the wakeup socket.
static int control_pre_event(UNUSED fr_time_t now, UNUSED fr_time_delta_t wake, void *uctx)
Announce that the reader is about to block, and drain the queue one last time.
bool same_thread
are the two ends in the same thread
fr_event_list_t * el
our event list
fr_control_eventset_table_t eventsets[FR_CONTROL_EVENTSET_MAX]
one table per event set
uint16_t max_size
the largest payload a message may carry.
static void control_read(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Take a batch of datagrams off the wakeup socket, then dispatch every queued message.
#define CONTROL_SOCKET_MSGS_PER_READ
bool fr_control_is_sleeping(fr_control_t *c)
Check whether the reader of this control plane is blocked.
uint16_t socket_events_num
the size of the socket callback array
bool opened
has the control path been opened.
fr_control_ctx_t * events
callbacks, indexed by event
uint32_t data_size
size of the data we're sending
int fr_control_open(fr_control_t *c)
Open the control-plane signalling path.
uint16_t slots
messages the queue holds unread.
fr_control_eventset_t es
event set of this message
uint16_t event
event this message signals, within its event set
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, uint16_t slots, uint16_t max_size)
Create a control-plane signaling path.
int fd[2]
wakeup socketpair.
struct fr_control_s::@54 socket
uint16_t events_num
the size of the callback array
int fr_control_callback_add(fr_control_t *c, fr_control_eventset_t es, uint16_t ev, fr_control_callback_t callback, void *uctx)
Register a callback for a message.
fr_control_callback_t callback
the function to call
static size_t control_socket_dgram_size_actual
atomic_bool sleeping
the reader is blocked waiting for a signal.
void * uctx
context for the callback
static fr_control_ctx_t const * control_ctx_find(fr_control_t const *c, fr_control_eventset_t es, uint16_t ev)
Find the callback registered for a message.
static size_t control_message_size(fr_control_t const *c)
The size of one queued message: header and the largest payload.
int fr_control_socket_callback_delete(fr_control_t *c, fr_control_eventset_t es, uint16_t ev)
Delete a callback for a message which carries a socket.
int fr_control_socket_send(fr_control_t *c, fr_control_eventset_t es, uint16_t ev, int sock)
Send a control-plane message carrying a socket.
static void control_post_event(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
The reader is running again.
fr_control_socket_callback_t callback
the function to call
int fr_control_callback_delete(fr_control_t *c, fr_control_eventset_t es, uint16_t ev)
Delete a callback for a message.
int fr_control_message_send(fr_control_t *c, fr_control_eventset_t es, uint16_t ev, void const *data, size_t data_size)
Send a control-plane message.
int fr_control_same_thread(fr_control_t *c)
static pthread_once_t control_socket_dgram_size_once
int fr_control_socket_callback_add(fr_control_t *c, fr_control_eventset_t es, uint16_t ev, fr_control_socket_callback_t callback, void *uctx)
Register a callback for a message which carries a socket.
void fr_control_wait(fr_control_t *c)
Wait for a plane control to become readable.
Ancillary data for the one socket a datagram may carry.
The dispatch table for one event set.
int fr_event_pre_delete(fr_event_list_t *el, fr_event_status_cb_t callback, void *uctx)
Delete a pre-event callback from the event list.
int fr_event_fd_unarmour(fr_event_list_t *el, int fd, fr_event_filter_t filter, uintptr_t armour)
Unarmour an FD.
int fr_event_post_delete(fr_event_list_t *el, fr_event_post_cb_t callback, void *uctx)
Delete a post-event callback from the event list.
int fr_event_pre_insert(fr_event_list_t *el, fr_event_status_cb_t callback, void *uctx)
Add a pre-event callback to the event list.
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
int fr_event_post_insert(fr_event_list_t *el, fr_event_post_cb_t callback, void *uctx)
Add a post-event callback to the event list.
int fr_event_fd_armour(fr_event_list_t *el, int fd, fr_event_filter_t filter, uintptr_t armour)
Armour an FD.
Stores all information relating to an event list.
int fr_nonblock(UNUSED int fd)
int fr_cloexec(UNUSED int fd)
int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, UNUSED struct timespec *timeout)
Emulates the real recvmmsg in userland.
#define atomic_load_explicit(object, order)
#define atomic_thread_fence(order)
#define atomic_store_explicit(object, desired, order)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
A time delta, a difference in time measured in nanoseconds.
static fr_event_list_t * el
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_const_push(_msg)
#define fr_strerror_const(_msg)
static fr_sbuff_err_t char size_t * len