blob: d52d60123b0cfd945783b69f7cb1f60c054ebbce [file] [log] [blame]
Harald Welte8857f3b2022-11-18 13:54:44 +01001/*! \file osmo_io.c
2 * New osmocom async I/O API.
3 *
Harald Welte1047ed72023-11-18 18:51:58 +01004 * (C) 2022-2024 by Harald Welte <laforge@osmocom.org>
5 * (C) 2022-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
Harald Welte8857f3b2022-11-18 13:54:44 +01006 * Author: Daniel Willmann <dwillmann@sysmocom.de>
7 *
8 * All Rights Reserved.
9 *
10 * SPDX-License-Identifier: GPL-2.0+
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 */
22
23#include "../config.h"
24#if defined(__linux__)
25
26#include <fcntl.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <talloc.h>
30#include <unistd.h>
31#include <string.h>
32#include <stdbool.h>
33#include <errno.h>
34
35#include <osmocom/core/osmo_io.h>
36#include <osmocom/core/linuxlist.h>
37#include <osmocom/core/logging.h>
38#include <osmocom/core/msgb.h>
39#include <osmocom/core/socket.h>
40#include <osmocom/core/talloc.h>
41#include <osmocom/core/utils.h>
42
43#include "osmo_io_internal.h"
44
45/*! This environment variable can be set to manually set the backend used in osmo_io */
46#define OSMO_IO_BACKEND_ENV "LIBOSMO_IO_BACKEND"
47
48const struct value_string osmo_io_backend_names[] = {
49 { OSMO_IO_BACKEND_POLL, "poll" },
Daniel Willmannf91d2aa2023-01-04 18:20:55 +010050 { OSMO_IO_BACKEND_IO_URING, "io_uring" },
Harald Welte8857f3b2022-11-18 13:54:44 +010051 { 0, NULL }
52};
53
Harald Welte09ab0412024-03-07 10:11:52 +010054const struct value_string osmo_iofd_mode_names[] = {
55 { OSMO_IO_FD_MODE_READ_WRITE, "read/write" },
56 { OSMO_IO_FD_MODE_RECVFROM_SENDTO, "recvfrom/sendto" },
57 { OSMO_IO_FD_MODE_RECVMSG_SENDMSG, "recvmsg/sendmsg" },
58 { 0, NULL }
59};
60
Harald Welte8857f3b2022-11-18 13:54:44 +010061static enum osmo_io_backend g_io_backend;
62
63/* Used by some tests, can't be static */
64struct iofd_backend_ops osmo_iofd_ops;
65
Daniel Willmannf91d2aa2023-01-04 18:20:55 +010066#if defined(HAVE_URING)
67void osmo_iofd_uring_init(void);
68#endif
69
Harald Welte8857f3b2022-11-18 13:54:44 +010070/*! initialize osmo_io for the current thread */
71void osmo_iofd_init(void)
72{
73 switch (g_io_backend) {
74 case OSMO_IO_BACKEND_POLL:
75 break;
Daniel Willmannf91d2aa2023-01-04 18:20:55 +010076#if defined(HAVE_URING)
77 case OSMO_IO_BACKEND_IO_URING:
78 osmo_iofd_uring_init();
79 break;
80#endif
Harald Welte8857f3b2022-11-18 13:54:44 +010081 default:
82 OSMO_ASSERT(0);
83 break;
84 }
85}
86
87/* ensure main thread always has pre-initialized osmo_io
88 * priority 103: run after on_dso_load_select */
89static __attribute__((constructor(103))) void on_dso_load_osmo_io(void)
90{
91 char *backend = getenv(OSMO_IO_BACKEND_ENV);
92 if (backend == NULL)
93 backend = OSMO_IO_BACKEND_DEFAULT;
94
95 if (!strcmp("POLL", backend)) {
96 g_io_backend = OSMO_IO_BACKEND_POLL;
97 osmo_iofd_ops = iofd_poll_ops;
Daniel Willmannf91d2aa2023-01-04 18:20:55 +010098#if defined(HAVE_URING)
99 } else if (!strcmp("IO_URING", backend)) {
100 g_io_backend = OSMO_IO_BACKEND_IO_URING;
101 osmo_iofd_ops = iofd_uring_ops;
102#endif
Harald Welte8857f3b2022-11-18 13:54:44 +0100103 } else {
104 fprintf(stderr, "Invalid osmo_io backend requested: \"%s\"\nCheck the environment variable %s\n", backend, OSMO_IO_BACKEND_ENV);
105 exit(1);
106 }
107
Harald Welte257e7892024-03-07 10:39:05 +0100108 OSMO_ASSERT(osmo_iofd_ops.close);
109 OSMO_ASSERT(osmo_iofd_ops.write_enable);
110 OSMO_ASSERT(osmo_iofd_ops.write_disable);
111 OSMO_ASSERT(osmo_iofd_ops.read_enable);
112 OSMO_ASSERT(osmo_iofd_ops.read_disable);
113 OSMO_ASSERT(osmo_iofd_ops.notify_connected);
114
Harald Welte8857f3b2022-11-18 13:54:44 +0100115 osmo_iofd_init();
116}
117
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200118/*! Allocate the msghdr.
Harald Welte8857f3b2022-11-18 13:54:44 +0100119 * \param[in] iofd the osmo_io file structure
120 * \param[in] action the action this msg(hdr) is for (read, write, ..)
121 * \param[in] msg the msg buffer to use. Will allocate a new one if NULL
Harald Welte1047ed72023-11-18 18:51:58 +0100122 * \param[in] cmsg_size size (in bytes) of iofd_msghdr.cmsg buffer. Can be 0 if cmsg is not used.
Harald Welte8857f3b2022-11-18 13:54:44 +0100123 * \returns the newly allocated msghdr or NULL in case of error */
Harald Welte1047ed72023-11-18 18:51:58 +0100124struct iofd_msghdr *iofd_msghdr_alloc(struct osmo_io_fd *iofd, enum iofd_msg_action action, struct msgb *msg,
125 size_t cmsg_size)
Harald Welte8857f3b2022-11-18 13:54:44 +0100126{
Daniel Willmann012d9042023-08-10 10:47:25 +0200127 bool free_msg = false;
128 struct iofd_msghdr *hdr;
129
Harald Welte8857f3b2022-11-18 13:54:44 +0100130 if (!msg) {
131 msg = iofd_msgb_alloc(iofd);
Daniel Willmann012d9042023-08-10 10:47:25 +0200132 if (!msg)
Harald Welte8857f3b2022-11-18 13:54:44 +0100133 return NULL;
Daniel Willmann012d9042023-08-10 10:47:25 +0200134 free_msg = true;
Daniel Willmannf0833822023-07-27 18:00:32 +0200135 } else {
Daniel Willmann012d9042023-08-10 10:47:25 +0200136 talloc_steal(iofd, msg);
137 }
138
Harald Welte1047ed72023-11-18 18:51:58 +0100139 hdr = talloc_zero_size(iofd, sizeof(struct iofd_msghdr) + cmsg_size);
Daniel Willmann012d9042023-08-10 10:47:25 +0200140 if (!hdr) {
141 if (free_msg)
142 talloc_free(msg);
143 return NULL;
Harald Welte8857f3b2022-11-18 13:54:44 +0100144 }
145
146 hdr->action = action;
147 hdr->iofd = iofd;
148 hdr->msg = msg;
149
150 return hdr;
151}
152
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200153/*! Free the msghdr.
Harald Welte8857f3b2022-11-18 13:54:44 +0100154 * \param[in] msghdr the msghdr to free
155 */
156void iofd_msghdr_free(struct iofd_msghdr *msghdr)
157{
158 /* msghdr->msg is never owned by msghdr, it will either be freed in the send path or
159 * or passed on to the read callback which takes ownership. */
160 talloc_free(msghdr);
161}
162
163/*! convenience wrapper to call msgb_alloc with parameters from osmo_io_fd */
164struct msgb *iofd_msgb_alloc(struct osmo_io_fd *iofd)
165{
166 uint16_t headroom = iofd->msgb_alloc.headroom;
167
168 OSMO_ASSERT(iofd->msgb_alloc.size < 0xffff - headroom);
Andreas Eversberga4ac5b82024-02-28 16:36:29 +0100169 return msgb_alloc_headroom_c(iofd, iofd->msgb_alloc.size + headroom, headroom, "osmo_io_msgb");
Harald Welte8857f3b2022-11-18 13:54:44 +0100170}
171
172/*! return the pending msgb in iofd or NULL if there is none*/
173struct msgb *iofd_msgb_pending(struct osmo_io_fd *iofd)
174{
175 struct msgb *msg = NULL;
176
177 msg = iofd->pending;
178 iofd->pending = NULL;
179
180 return msg;
181}
182
183/*! Return the pending msgb or allocate and return a new one */
184struct msgb *iofd_msgb_pending_or_alloc(struct osmo_io_fd *iofd)
185{
186 struct msgb *msg = NULL;
187
188 msg = iofd_msgb_pending(iofd);
189 if (!msg)
190 msg = iofd_msgb_alloc(iofd);
191
192 return msg;
193}
194
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200195/*! Enqueue a message to be sent.
Harald Welte8857f3b2022-11-18 13:54:44 +0100196 *
197 * Enqueues the message at the back of the queue provided there is enough space.
198 * \param[in] iofd the file descriptor
199 * \param[in] msghdr the message to enqueue
200 * \returns 0 if the message was enqueued succcessfully,
201 * -ENOSPC if the queue already contains the maximum number of messages
202 */
203int iofd_txqueue_enqueue(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr)
204{
205 if (iofd->tx_queue.current_length >= iofd->tx_queue.max_length)
206 return -ENOSPC;
207
208 llist_add_tail(&msghdr->list, &iofd->tx_queue.msg_queue);
209 iofd->tx_queue.current_length++;
210
Daniel Willmanne4ecd992023-06-30 10:52:11 +0200211 if (iofd->tx_queue.current_length == 1 && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
Harald Welte8857f3b2022-11-18 13:54:44 +0100212 osmo_iofd_ops.write_enable(iofd);
213
214 return 0;
215}
216
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200217/*! Enqueue a message at the front.
Harald Welte8857f3b2022-11-18 13:54:44 +0100218 *
219 * Used to enqueue a msgb from a partial send again. This function will always
220 * enqueue the message, even if the maximum number of messages is reached.
221 * \param[in] iofd the file descriptor
222 * \param[in] msghdr the message to enqueue
223 */
224void iofd_txqueue_enqueue_front(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr)
225{
226 llist_add(&msghdr->list, &iofd->tx_queue.msg_queue);
227 iofd->tx_queue.current_length++;
Daniel Willmanne4ecd992023-06-30 10:52:11 +0200228
229 if (iofd->tx_queue.current_length == 1 && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
230 osmo_iofd_ops.write_enable(iofd);
Harald Welte8857f3b2022-11-18 13:54:44 +0100231}
232
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200233/*! Dequeue a message from the front.
Harald Welte8857f3b2022-11-18 13:54:44 +0100234 *
235 * \param[in] iofd the file descriptor
236 * \returns the msghdr from the front of the queue or NULL if the queue is empty
237 */
238struct iofd_msghdr *iofd_txqueue_dequeue(struct osmo_io_fd *iofd)
239{
240 struct llist_head *lh;
241
242 if (iofd->tx_queue.current_length == 0)
243 return NULL;
244
245 lh = iofd->tx_queue.msg_queue.next;
246
247 OSMO_ASSERT(lh);
248 iofd->tx_queue.current_length--;
249 llist_del(lh);
250
251 if (iofd->tx_queue.current_length == 0)
252 osmo_iofd_ops.write_disable(iofd);
253
254 return llist_entry(lh, struct iofd_msghdr, list);
255}
256
257/*! Handle segmentation of the msg. If this function returns *_HANDLE_ONE or MORE then the data in msg will contain
258 * one complete message.
259 * If there are bytes left over, *pending_out will point to a msgb with the remaining data.
260*/
261static enum iofd_seg_act iofd_handle_segmentation(struct osmo_io_fd *iofd, struct msgb *msg, struct msgb **pending_out)
262{
arehbeinc0aa4bd2023-06-16 22:31:32 +0200263 int extra_len, received_len;
Harald Welte8857f3b2022-11-18 13:54:44 +0100264 struct msgb *msg_pending;
265
Daniel Willmann7b59bab2023-07-07 11:17:59 +0200266 /* Save the start of message before segmentation_cb (which could change it) */
267 uint8_t *data = msg->data;
268
arehbeinc0aa4bd2023-06-16 22:31:32 +0200269 received_len = msgb_length(msg);
Harald Welte8857f3b2022-11-18 13:54:44 +0100270
271 if (!iofd->io_ops.segmentation_cb) {
272 *pending_out = NULL;
273 return IOFD_SEG_ACT_HANDLE_ONE;
274 }
275
arehbeinc0aa4bd2023-06-16 22:31:32 +0200276 int expected_len = iofd->io_ops.segmentation_cb(msg);
277 if (expected_len == -EAGAIN) {
Daniel Willmannd4d03702023-05-17 12:38:14 +0200278 goto defer;
arehbeinc0aa4bd2023-06-16 22:31:32 +0200279 } else if (expected_len < 0) {
Daniel Willmannd4d03702023-05-17 12:38:14 +0200280 /* Something is wrong, skip this msgb */
arehbeinc0aa4bd2023-06-16 22:31:32 +0200281 LOGPIO(iofd, LOGL_ERROR, "segmentation_cb returned error (%d), skipping msg of size %d\n",
282 expected_len, received_len);
Harald Welte8857f3b2022-11-18 13:54:44 +0100283 *pending_out = NULL;
Daniel Willmannd4d03702023-05-17 12:38:14 +0200284 msgb_free(msg);
Harald Welte8857f3b2022-11-18 13:54:44 +0100285 return IOFD_SEG_ACT_DEFER;
286 }
287
arehbeinc0aa4bd2023-06-16 22:31:32 +0200288 extra_len = received_len - expected_len;
Daniel Willmannd4d03702023-05-17 12:38:14 +0200289 /* No segmentation needed, return the whole msgb */
290 if (extra_len == 0) {
291 *pending_out = NULL;
292 return IOFD_SEG_ACT_HANDLE_ONE;
293 /* segment is incomplete */
294 } else if (extra_len < 0) {
295 goto defer;
296 }
297
298 /* msgb contains more than one segment */
299 /* Copy the trailing data over */
Harald Welte8857f3b2022-11-18 13:54:44 +0100300 msg_pending = iofd_msgb_alloc(iofd);
Daniel Willmann7b59bab2023-07-07 11:17:59 +0200301 memcpy(msgb_data(msg_pending), data + expected_len, extra_len);
Daniel Willmannd4d03702023-05-17 12:38:14 +0200302 msgb_put(msg_pending, extra_len);
Harald Welte8857f3b2022-11-18 13:54:44 +0100303 *pending_out = msg_pending;
304
Daniel Willmann7b59bab2023-07-07 11:17:59 +0200305 /* Trim the original msgb to size. Don't use msgb_trim because we need to reference
306 * msg->data from before it might have been modified by the segmentation_cb(). */
Daniel Willmann7b59bab2023-07-07 11:17:59 +0200307 msg->tail = data + expected_len;
Daniel Willmann97d21442023-07-18 09:46:27 +0200308 msg->len = msg->tail - msg->data;
Harald Welte8857f3b2022-11-18 13:54:44 +0100309 return IOFD_SEG_ACT_HANDLE_MORE;
Daniel Willmannd4d03702023-05-17 12:38:14 +0200310
311defer:
312 *pending_out = msg;
313 return IOFD_SEG_ACT_DEFER;
Harald Welte8857f3b2022-11-18 13:54:44 +0100314}
315
316/*! Restore message boundaries on read() and pass individual messages to the read callback
317 */
318void iofd_handle_segmented_read(struct osmo_io_fd *iofd, struct msgb *msg, int rc)
319{
320 int res;
321 struct msgb *pending = NULL;
322
Harald Welteb365b1d2024-02-23 16:08:49 +0100323 OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE);
324
Harald Welte8857f3b2022-11-18 13:54:44 +0100325 if (rc <= 0) {
326 iofd->io_ops.read_cb(iofd, rc, msg);
327 return;
328 }
329
330 do {
331 res = iofd_handle_segmentation(iofd, msg, &pending);
332 if (res != IOFD_SEG_ACT_DEFER || rc < 0)
333 iofd->io_ops.read_cb(iofd, rc, msg);
334 if (res == IOFD_SEG_ACT_HANDLE_MORE)
335 msg = pending;
336 } while (res == IOFD_SEG_ACT_HANDLE_MORE);
337
338 OSMO_ASSERT(iofd->pending == NULL);
339 iofd->pending = pending;
340}
341
Harald Welte987a86a2023-11-18 18:46:24 +0100342/*! completion handler: Called by osmo_io backend after a given I/O operation has completed
343 * \param[in] iofd I/O file-descriptor on which I/O has completed
344 * \param[in] msg message buffer containing data related to completed I/O
Andreas Eversberg76f76782024-02-14 14:33:10 +0100345 * \param[in] rc result code with read size or error (-errno)
Harald Welte987a86a2023-11-18 18:46:24 +0100346 * \param[in] hdr serialized msghdr containing state of completed I/O */
Daniel Willmann2b34e922023-08-23 18:02:13 +0200347void iofd_handle_recv(struct osmo_io_fd *iofd, struct msgb *msg, int rc, struct iofd_msghdr *hdr)
348{
Daniel Willmann012d9042023-08-10 10:47:25 +0200349 talloc_steal(iofd->msgb_alloc.ctx, msg);
Daniel Willmann2b34e922023-08-23 18:02:13 +0200350 switch (iofd->mode) {
351 case OSMO_IO_FD_MODE_READ_WRITE:
352 iofd_handle_segmented_read(iofd, msg, rc);
353 break;
354 case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
355 iofd->io_ops.recvfrom_cb(iofd, rc, msg, &hdr->osa);
356 break;
Harald Welte1047ed72023-11-18 18:51:58 +0100357 case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
358 iofd->io_ops.recvmsg_cb(iofd, rc, msg, &hdr->hdr);
359 break;
360 default:
Daniel Willmann2b34e922023-08-23 18:02:13 +0200361 OSMO_ASSERT(false);
362 break;
363 }
364}
365
Daniel Willmann84611882023-11-21 10:17:00 +0100366/*! completion handler: Calld by osmo_io backend after a given I/O operation has completed
367 * \param[in] iofd I/O file-descriptor on which I/O has completed
368 * \param[in] rc return value of the I/O operation
369 * \param[in] msghdr serialized msghdr containing state of completed I/O
370 */
371void iofd_handle_send_completion(struct osmo_io_fd *iofd, int rc, struct iofd_msghdr *msghdr)
372{
373 struct msgb *msg = msghdr->msg;
374
375 /* Incomplete write */
376 if (rc > 0 && rc < msgb_length(msg)) {
377 /* Re-enqueue remaining data */
378 msgb_pull(msg, rc);
379 msghdr->iov[0].iov_len = msgb_length(msg);
380 iofd_txqueue_enqueue_front(iofd, msghdr);
381 return;
382 }
383
384 /* Reenqueue the complete msgb */
385 if (rc == -EAGAIN) {
386 iofd_txqueue_enqueue_front(iofd, msghdr);
387 return;
388 }
389
390 /* All other failure and success cases are handled here */
391 switch (msghdr->action) {
392 case IOFD_ACT_WRITE:
393 iofd->io_ops.write_cb(iofd, rc, msg);
394 break;
395 case IOFD_ACT_SENDTO:
396 iofd->io_ops.sendto_cb(iofd, rc, msg, &msghdr->osa);
397 break;
Harald Welte1047ed72023-11-18 18:51:58 +0100398 case IOFD_ACT_SENDMSG:
399 iofd->io_ops.sendmsg_cb(iofd, rc, msg);
400 break;
Daniel Willmann84611882023-11-21 10:17:00 +0100401 default:
402 OSMO_ASSERT(0);
403 }
404
405 msgb_free(msghdr->msg);
406 iofd_msghdr_free(msghdr);
407}
408
Harald Welte8857f3b2022-11-18 13:54:44 +0100409/* Public functions */
410
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200411/*! Send a message through a connected socket.
Harald Welte8857f3b2022-11-18 13:54:44 +0100412 *
413 * Appends the message to the internal transmit queue.
414 * If the function returns success (0) it will take ownership of the msgb and
415 * internally call msgb_free() after the write request completes.
416 * In case of an error the msgb needs to be freed by the caller.
417 * \param[in] iofd file descriptor to write to
418 * \param[in] msg message buffer to write
419 * \returns 0 in case of success; a negative value in case of error
420 */
421int osmo_iofd_write_msgb(struct osmo_io_fd *iofd, struct msgb *msg)
422{
423 int rc;
Daniel Willmann2386e9a2023-09-28 15:28:13 +0200424
Andreas Eversberg2ce17da2024-02-09 14:36:30 +0100425 if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
426 LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
427 return -EINVAL;
428 }
429
Daniel Willmannafdfc6a2023-11-21 10:10:37 +0100430 OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE);
Daniel Willmann2386e9a2023-09-28 15:28:13 +0200431 if (OSMO_UNLIKELY(!iofd->io_ops.write_cb)) {
432 LOGPIO(iofd, LOGL_ERROR, "write_cb not set, Rejecting msgb\n");
433 return -EINVAL;
434 }
435
Harald Welte1047ed72023-11-18 18:51:58 +0100436 struct iofd_msghdr *msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_WRITE, msg, 0);
Harald Welte8857f3b2022-11-18 13:54:44 +0100437 if (!msghdr)
438 return -ENOMEM;
439
Daniel Willmann92efac22023-08-01 09:55:13 +0200440 msghdr->flags = MSG_NOSIGNAL;
Harald Welte8857f3b2022-11-18 13:54:44 +0100441 msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
442 msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
443 msghdr->hdr.msg_iov = &msghdr->iov[0];
444 msghdr->hdr.msg_iovlen = 1;
445
446 rc = iofd_txqueue_enqueue(iofd, msghdr);
447 if (rc < 0) {
448 iofd_msghdr_free(msghdr);
449 LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
450 return rc;
451 }
452
453 return 0;
454}
455
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200456/*! Send a message through an unconnected socket.
Harald Welte8857f3b2022-11-18 13:54:44 +0100457 *
458 * Appends the message to the internal transmit queue.
459 * If the function returns success (0), it will take ownership of the msgb and
460 * internally call msgb_free() after the write request completes.
461 * In case of an error the msgb needs to be freed by the caller.
462 * \param[in] iofd file descriptor to write to
463 * \param[in] msg message buffer to send
464 * \param[in] sendto_flags Flags to pass to the send call
465 * \param[in] dest destination address to send the message to
466 * \returns 0 in case of success; a negative value in case of error
467 */
468int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendto_flags, const struct osmo_sockaddr *dest)
469{
470 int rc;
471
Andreas Eversberg2ce17da2024-02-09 14:36:30 +0100472 if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
473 LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
474 return -EINVAL;
475 }
476
Harald Welte8857f3b2022-11-18 13:54:44 +0100477 OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_RECVFROM_SENDTO);
Daniel Willmann2386e9a2023-09-28 15:28:13 +0200478 if (OSMO_UNLIKELY(!iofd->io_ops.sendto_cb)) {
479 LOGPIO(iofd, LOGL_ERROR, "sendto_cb not set, Rejecting msgb\n");
480 return -EINVAL;
481 }
Harald Welte8857f3b2022-11-18 13:54:44 +0100482
Harald Welte1047ed72023-11-18 18:51:58 +0100483 struct iofd_msghdr *msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_SENDTO, msg, 0);
Harald Welte8857f3b2022-11-18 13:54:44 +0100484 if (!msghdr)
485 return -ENOMEM;
486
487 if (dest) {
488 msghdr->osa = *dest;
489 msghdr->hdr.msg_name = &msghdr->osa.u.sa;
490 msghdr->hdr.msg_namelen = osmo_sockaddr_size(&msghdr->osa);
491 }
492 msghdr->flags = sendto_flags;
493 msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
494 msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
495 msghdr->hdr.msg_iov = &msghdr->iov[0];
496 msghdr->hdr.msg_iovlen = 1;
497
498 rc = iofd_txqueue_enqueue(iofd, msghdr);
499 if (rc < 0) {
500 iofd_msghdr_free(msghdr);
501 LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
502 return rc;
503 }
504
505 return 0;
506}
507
Harald Welte1047ed72023-11-18 18:51:58 +0100508/*! ismo_io equivalent of the sendmsg(2) socket API call
509 *
510 * Appends the message to the internal transmit queue.
511 * If the function returns success (0), it will take ownership of the msgb and
512 * internally call msgb_free() after the write request completes.
513 * In case of an error the msgb needs to be freed by the caller.
514 * \param[in] iofd file descriptor to write to
515 * \param[in] msg message buffer to send; is used to fill msgh->iov[]
516 * \param[in] sendmsg_flags Flags to pass to the send call
517 * \param[in] msgh 'struct msghdr' for name/control/flags. iov must be empty!
518 * \returns 0 in case of success; a negative value in case of error
519 */
520int osmo_iofd_sendmsg_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendmsg_flags, const struct msghdr *msgh)
521{
522 int rc;
523 struct iofd_msghdr *msghdr;
524
Andreas Eversberg2ce17da2024-02-09 14:36:30 +0100525 if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
526 LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
527 return -EINVAL;
528 }
529
Harald Welte1047ed72023-11-18 18:51:58 +0100530 OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG);
531 if (OSMO_UNLIKELY(!iofd->io_ops.sendmsg_cb)) {
532 LOGPIO(iofd, LOGL_ERROR, "sendmsg_cb not set, Rejecting msgb\n");
533 return -EINVAL;
534 }
535
536 if (OSMO_UNLIKELY(msgh->msg_namelen > sizeof(msghdr->osa))) {
537 LOGPIO(iofd, LOGL_ERROR, "osmo_iofd_sendmsg msg_namelen (%u) > supported %zu bytes\n",
538 msgh->msg_namelen, sizeof(msghdr->osa));
539 return -EINVAL;
540 }
541
542 if (OSMO_UNLIKELY(msgh->msg_iovlen)) {
543 LOGPIO(iofd, LOGL_ERROR, "osmo_iofd_sendmsg must have all in 'struct msgb', not in 'msg_iov'\n");
544 return -EINVAL;
545 }
546
547 msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_SENDMSG, msg, msgh->msg_controllen);
548 if (!msghdr)
549 return -ENOMEM;
550
551 /* copy over optional address */
552 if (msgh->msg_name) {
553 memcpy(&msghdr->osa, msgh->msg_name, msgh->msg_namelen);
554 msghdr->hdr.msg_name = &msghdr->osa.u.sa;
555 msghdr->hdr.msg_namelen = msgh->msg_namelen;
556 }
557
558 /* build iov from msgb */
559 msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
560 msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
561 msghdr->hdr.msg_iov = &msghdr->iov[0];
562 msghdr->hdr.msg_iovlen = 1;
563
564 /* copy over the cmsg from the msghdr */
565 if (msgh->msg_control && msgh->msg_controllen) {
566 msghdr->hdr.msg_control = msghdr->cmsg;
567 msghdr->hdr.msg_controllen = msgh->msg_controllen;
568 memcpy(msghdr->cmsg, msgh->msg_control, msgh->msg_controllen);
569 }
570
571 /* copy over msg_flags */
572 msghdr->hdr.msg_flags = sendmsg_flags;
573
574 rc = iofd_txqueue_enqueue(iofd, msghdr);
575 if (rc < 0) {
576 iofd_msghdr_free(msghdr);
577 LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
578 return rc;
579 }
580
581 return 0;
582}
583
Harald Welteb365b1d2024-02-23 16:08:49 +0100584static int check_mode_callback_compat(enum osmo_io_fd_mode mode, const struct osmo_io_ops *ops)
585{
586 switch (mode) {
587 case OSMO_IO_FD_MODE_READ_WRITE:
588 if (ops->recvfrom_cb || ops->sendto_cb)
589 return false;
Harald Welte1047ed72023-11-18 18:51:58 +0100590 if (ops->recvmsg_cb || ops->sendmsg_cb)
591 return false;
Harald Welteb365b1d2024-02-23 16:08:49 +0100592 break;
593 case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
594 if (ops->read_cb || ops->write_cb)
595 return false;
Harald Welte1047ed72023-11-18 18:51:58 +0100596 if (ops->recvmsg_cb || ops->sendmsg_cb)
597 return false;
598 break;
599 case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
600 if (ops->recvfrom_cb || ops->sendto_cb)
601 return false;
602 if (ops->read_cb || ops->write_cb)
603 return false;
Harald Welteb365b1d2024-02-23 16:08:49 +0100604 break;
605 default:
606 break;
607 }
608
609 return true;
610}
611
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200612/*! Allocate and setup a new iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100613 * \param[in] ctx the parent context from which to allocate
614 * \param[in] fd the underlying system file descriptor
615 * \param[in] name the name of the iofd
616 * \param[in] mode the mode of the iofd, whether it should use read()/write(), sendto()/recvfrom()
617 * \param[in] ioops structure with read/write/send/recv callbacks
618 * \param[in] data user data pointer accessible by the ioops callbacks
619 * \returns The newly allocated osmo_io_fd struct or NULL on failure
620 */
621struct osmo_io_fd *osmo_iofd_setup(const void *ctx, int fd, const char *name, enum osmo_io_fd_mode mode,
622 const struct osmo_io_ops *ioops, void *data)
623{
Harald Weltec380f292023-11-18 19:54:46 +0100624 struct osmo_io_fd *iofd;
625
626 /* reject unsupported/unknown modes */
627 switch (mode) {
628 case OSMO_IO_FD_MODE_READ_WRITE:
629 case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
Harald Welte1047ed72023-11-18 18:51:58 +0100630 case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
Harald Weltec380f292023-11-18 19:54:46 +0100631 break;
632 default:
633 return NULL;
634 }
635
Harald Welte09ab0412024-03-07 10:11:52 +0100636 if (ioops && !check_mode_callback_compat(mode, ioops)) {
637 LOGP(DLIO, LOGL_ERROR, "iofd(%s): rejecting call-backs incompatible with mode %s\n",
638 name ? name : "unknown", osmo_iofd_mode_name(mode));
Harald Welteb365b1d2024-02-23 16:08:49 +0100639 return NULL;
Harald Welte09ab0412024-03-07 10:11:52 +0100640 }
Harald Welteb365b1d2024-02-23 16:08:49 +0100641
Harald Weltec380f292023-11-18 19:54:46 +0100642 iofd = talloc_zero(ctx, struct osmo_io_fd);
Harald Welte8857f3b2022-11-18 13:54:44 +0100643 if (!iofd)
644 return NULL;
645
646 iofd->fd = fd;
647 iofd->mode = mode;
Daniel Willmannf89162f2023-06-26 19:24:46 +0200648 IOFD_FLAG_SET(iofd, IOFD_FLAG_CLOSED);
Harald Welte8857f3b2022-11-18 13:54:44 +0100649
Pau Espin Pedrol63e45e62023-06-16 16:19:45 +0200650 if (name)
651 iofd->name = talloc_strdup(iofd, name);
Harald Welte8857f3b2022-11-18 13:54:44 +0100652
653 if (ioops)
654 iofd->io_ops = *ioops;
655
656 iofd->pending = NULL;
657
658 iofd->data = data;
659
660 iofd->msgb_alloc.ctx = ctx;
661 iofd->msgb_alloc.size = OSMO_IO_DEFAULT_MSGB_SIZE;
662 iofd->msgb_alloc.headroom = OSMO_IO_DEFAULT_MSGB_HEADROOM;
663
664 iofd->tx_queue.max_length = 32;
665 INIT_LLIST_HEAD(&iofd->tx_queue.msg_queue);
666
667 return iofd;
668}
669
Harald Welte1047ed72023-11-18 18:51:58 +0100670/*! Set the size of the control message buffer allocated when submitting recvmsg */
671int osmo_iofd_set_cmsg_size(struct osmo_io_fd *iofd, size_t cmsg_size)
672{
673 if (iofd->mode != OSMO_IO_FD_MODE_RECVMSG_SENDMSG)
674 return -EINVAL;
675
676 iofd->cmsg_size = cmsg_size;
677 return 0;
678}
679
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200680/*! Register the fd with the underlying backend.
Harald Welte8857f3b2022-11-18 13:54:44 +0100681 *
682 * \param[in] iofd the iofd file descriptor
683 * \param[in] fd the system fd number that will be registeres. If negative will use the one already set.
684 * \returns zero on success, a negative value on error
685*/
686int osmo_iofd_register(struct osmo_io_fd *iofd, int fd)
687{
Daniel Willmanneb9edba2023-06-06 16:53:38 +0200688 int rc = 0;
689
Harald Welte8857f3b2022-11-18 13:54:44 +0100690 if (fd >= 0)
691 iofd->fd = fd;
Harald Welte04e6f6e2024-03-07 09:57:22 +0100692 else if (iofd->fd < 0) {
693 /* this might happen if both osmo_iofd_setup() and osmo_iofd_register() are called with -1 */
694 LOGPIO(iofd, LOGL_ERROR, "Cannot register io_fd using invalid fd == %d\n", iofd->fd);
695 return -EBADF;
696 }
Harald Welte8857f3b2022-11-18 13:54:44 +0100697
698 if (osmo_iofd_ops.register_fd)
Daniel Willmanneb9edba2023-06-06 16:53:38 +0200699 rc = osmo_iofd_ops.register_fd(iofd);
Daniel Willmannacb97762023-06-07 17:02:31 +0200700 if (rc)
701 return rc;
Harald Welte8857f3b2022-11-18 13:54:44 +0100702
Daniel Willmannf89162f2023-06-26 19:24:46 +0200703 IOFD_FLAG_UNSET(iofd, IOFD_FLAG_CLOSED);
Harald Welteb365b1d2024-02-23 16:08:49 +0100704 if ((iofd->mode == OSMO_IO_FD_MODE_READ_WRITE && iofd->io_ops.read_cb) ||
Harald Welte1047ed72023-11-18 18:51:58 +0100705 (iofd->mode == OSMO_IO_FD_MODE_RECVFROM_SENDTO && iofd->io_ops.recvfrom_cb) ||
706 (iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG && iofd->io_ops.recvmsg_cb)) {
Daniel Willmann2386e9a2023-09-28 15:28:13 +0200707 osmo_iofd_ops.read_enable(iofd);
Harald Welteb365b1d2024-02-23 16:08:49 +0100708 }
Daniel Willmanne2a8dc42023-06-30 10:51:53 +0200709
710 if (iofd->tx_queue.current_length > 0)
711 osmo_iofd_ops.write_enable(iofd);
Daniel Willmanneb9edba2023-06-06 16:53:38 +0200712
713 return rc;
Harald Welte8857f3b2022-11-18 13:54:44 +0100714}
715
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200716/*! Unregister the fd from the underlying backend.
Harald Welte8857f3b2022-11-18 13:54:44 +0100717 *
718 * \param[in] iofd the file descriptor
719 * \returns zero on success, a negative value on error
720 */
721int osmo_iofd_unregister(struct osmo_io_fd *iofd)
722{
723 if (osmo_iofd_ops.unregister_fd)
724 return osmo_iofd_ops.unregister_fd(iofd);
Daniel Willmannf89162f2023-06-26 19:24:46 +0200725 IOFD_FLAG_SET(iofd, IOFD_FLAG_CLOSED);
Harald Welte8857f3b2022-11-18 13:54:44 +0100726
727 return 0;
728}
729
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200730/*! Get the number of messages in the tx queue.
Harald Welte8857f3b2022-11-18 13:54:44 +0100731 *
732 * \param[in] iofd the file descriptor
733 */
734unsigned int osmo_iofd_txqueue_len(struct osmo_io_fd *iofd)
735{
736 return iofd->tx_queue.current_length;
737}
738
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200739/*! Clear the transmit queue of the the iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100740 *
741 * This function frees all messages currently pending in the transmit queue
742 * \param[in] iofd the file descriptor
743 */
744void osmo_iofd_txqueue_clear(struct osmo_io_fd *iofd)
745{
746 struct iofd_msghdr *hdr;
747 while ((hdr = iofd_txqueue_dequeue(iofd))) {
748 msgb_free(hdr->msg);
749 iofd_msghdr_free(hdr);
750 }
751}
752
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200753/*! Free the iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100754 *
755 * This function is safe to use in the read/write callbacks and will defer freeing it until safe to do so.
756 * The iofd will be closed before.
757 * \param[in] iofd the file descriptor
758 */
759void osmo_iofd_free(struct osmo_io_fd *iofd)
760{
761 if (!iofd)
762 return;
763
764 osmo_iofd_close(iofd);
765
Daniel Willmannf89162f2023-06-26 19:24:46 +0200766 if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_IN_CALLBACK)) {
Harald Welte8857f3b2022-11-18 13:54:44 +0100767 talloc_free(iofd);
768 } else {
769 /* Prevent our parent context from freeing us prematurely */
770 talloc_steal(NULL, iofd);
Daniel Willmannf89162f2023-06-26 19:24:46 +0200771 IOFD_FLAG_SET(iofd, IOFD_FLAG_TO_FREE);
Harald Welte8857f3b2022-11-18 13:54:44 +0100772 }
773}
774
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200775/*! Close the iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100776 *
777 * This function closes the underlying fd and clears any messages in the tx queue
778 * The iofd is not freed and can be assigned a new file descriptor with osmo_iofd_register()
779 * \param[in] iofd the file descriptor
780 * \ returns 0 on success, a negative value otherwise
781 */
782int osmo_iofd_close(struct osmo_io_fd *iofd)
783{
784 int rc = 0;
785
Daniel Willmannf89162f2023-06-26 19:24:46 +0200786 if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
Harald Welte8857f3b2022-11-18 13:54:44 +0100787 return rc;
788
Daniel Willmannf89162f2023-06-26 19:24:46 +0200789 IOFD_FLAG_SET(iofd, IOFD_FLAG_CLOSED);
Harald Welte8857f3b2022-11-18 13:54:44 +0100790
791 /* Free pending msgs in tx queue */
792 osmo_iofd_txqueue_clear(iofd);
793 msgb_free(iofd->pending);
794
795 iofd->pending = NULL;
796
Harald Weltee1d48582024-03-07 10:34:21 +0100797 rc = osmo_iofd_ops.close(iofd);
Harald Welte8857f3b2022-11-18 13:54:44 +0100798 iofd->fd = -1;
799 return rc;
800}
801
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200802/*! Set the size and headroom of the msgb allocated when receiving messages.
Daniel Willmann4731e712023-07-07 11:21:15 +0200803 * \param[in] iofd the file descriptor
Harald Welte8857f3b2022-11-18 13:54:44 +0100804 * \param[in] size the size of the msgb when receiving data
805 * \param[in] headroom the headroom of the msgb when receiving data
806 */
807void osmo_iofd_set_alloc_info(struct osmo_io_fd *iofd, unsigned int size, unsigned int headroom)
808{
809 iofd->msgb_alloc.headroom = headroom;
810 iofd->msgb_alloc.size = size;
811}
812
Daniel Willmanna9303f32023-07-07 11:20:48 +0200813/*! Set the maximum number of messages enqueued for sending.
814 * \param[in] iofd the file descriptor
815 * \param[in] size the maximum size of the transmit queue
816 */
817void osmo_iofd_set_txqueue_max_length(struct osmo_io_fd *iofd, unsigned int max_length)
818{
819 iofd->tx_queue.max_length = max_length;
820}
821
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200822/*! Get the associated user-data from an iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100823 * \param[in] iofd the file descriptor
824 * \returns the data that was previously set with \ref osmo_iofd_setup()
825 */
826void *osmo_iofd_get_data(const struct osmo_io_fd *iofd)
827{
828 return iofd->data;
829}
830
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200831/*! Set the associated user-data from an iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100832 * \param[in] iofd the file descriptor
833 * \param[in] data the data to set
834 */
835void osmo_iofd_set_data(struct osmo_io_fd *iofd, void *data)
836{
837 iofd->data = data;
838}
839
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200840/*! Get the private number from an iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100841 * \param[in] iofd the file descriptor
842 * \returns the private number that was previously set with \ref osmo_iofd_set_priv_nr()
843 */
844unsigned int osmo_iofd_get_priv_nr(const struct osmo_io_fd *iofd)
845{
846 return iofd->priv_nr;
847}
848
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200849/*! Set the private number from an iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100850 * \param[in] iofd the file descriptor
851 * \param[in] priv_nr the private number to set
852 */
853void osmo_iofd_set_priv_nr(struct osmo_io_fd *iofd, unsigned int priv_nr)
854{
855 iofd->priv_nr = priv_nr;
856}
857
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200858/*! Get the underlying file descriptor from an iofd.
Harald Welte8857f3b2022-11-18 13:54:44 +0100859 * \param[in] iofd the file descriptor
860 * \returns the underlying file descriptor number */
861int osmo_iofd_get_fd(const struct osmo_io_fd *iofd)
862{
863 return iofd->fd;
864}
865
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200866/*! Get the name of the file descriptor.
Harald Welte8857f3b2022-11-18 13:54:44 +0100867 * \param[in] iofd the file descriptor
868 * \returns the name of the iofd as given in \ref osmo_iofd_setup() */
869const char *osmo_iofd_get_name(const struct osmo_io_fd *iofd)
870{
871 return iofd->name;
872}
873
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200874/*! Set the name of the file descriptor.
Pau Espin Pedrol63e45e62023-06-16 16:19:45 +0200875 * \param[in] iofd the file descriptor
876 * \param[in] name the name to set on the file descriptor */
877void osmo_iofd_set_name(struct osmo_io_fd *iofd, const char *name)
878{
879 osmo_talloc_replace_string(iofd, &iofd->name, name);
880}
881
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200882/*! Set the osmo_io_ops for an iofd.
arehbein0c374c62023-05-14 21:43:11 +0200883 * \param[in] iofd Target iofd file descriptor
884 * \param[in] ioops osmo_io_ops structure to be set */
Harald Welteb365b1d2024-02-23 16:08:49 +0100885int osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioops)
arehbein0c374c62023-05-14 21:43:11 +0200886{
Harald Welte09ab0412024-03-07 10:11:52 +0100887 if (!check_mode_callback_compat(iofd->mode, ioops)) {
888 LOGPIO(iofd, LOGL_ERROR, "rejecting call-backs incompatible with mode %s\n",
889 osmo_iofd_mode_name(iofd->mode));
Harald Welteb365b1d2024-02-23 16:08:49 +0100890 return -EINVAL;
Harald Welte09ab0412024-03-07 10:11:52 +0100891 }
Harald Welteb365b1d2024-02-23 16:08:49 +0100892
arehbein0c374c62023-05-14 21:43:11 +0200893 iofd->io_ops = *ioops;
Daniel Willmann2386e9a2023-09-28 15:28:13 +0200894
895 switch (iofd->mode) {
896 case OSMO_IO_FD_MODE_READ_WRITE:
897 if (iofd->io_ops.read_cb)
898 osmo_iofd_ops.read_enable(iofd);
899 else
900 osmo_iofd_ops.read_disable(iofd);
901 break;
902 case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
903 if (iofd->io_ops.recvfrom_cb)
904 osmo_iofd_ops.read_enable(iofd);
905 else
906 osmo_iofd_ops.read_disable(iofd);
907 break;
Harald Welte1047ed72023-11-18 18:51:58 +0100908 case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
909 if (iofd->io_ops.recvmsg_cb)
910 osmo_iofd_ops.read_enable(iofd);
911 else
912 osmo_iofd_ops.read_disable(iofd);
913 break;
Daniel Willmann2386e9a2023-09-28 15:28:13 +0200914 default:
915 OSMO_ASSERT(0);
916 }
Harald Welteb365b1d2024-02-23 16:08:49 +0100917
918 return 0;
arehbein0c374c62023-05-14 21:43:11 +0200919}
920
Harald Weltef574aea2024-02-23 12:07:03 +0100921/*! Get the osmo_io_ops for an iofd.
922 * \param[in] iofd Target iofd file descriptor
923 * \param[in] ioops caller-allocated osmo_io_ops structure to be filled */
924void osmo_iofd_get_ioops(struct osmo_io_fd *iofd, struct osmo_io_ops *ioops)
925{
926 *ioops = iofd->io_ops;
927}
928
Daniel Willmannd0d9ecb2023-07-12 11:55:52 +0200929/*! Notify the user if/when the socket is connected.
Daniel Willmanne2a8dc42023-06-30 10:51:53 +0200930 * When the socket is connected the write_cb will be called.
931 * \param[in] iofd the file descriptor */
932void osmo_iofd_notify_connected(struct osmo_io_fd *iofd)
933{
Harald Welte1047ed72023-11-18 18:51:58 +0100934 OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE ||
935 iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG);
Andreas Eversberg848faf92024-02-09 12:38:17 +0100936 osmo_iofd_ops.notify_connected(iofd);
Daniel Willmanne2a8dc42023-06-30 10:51:53 +0200937}
938
939
Harald Welte8857f3b2022-11-18 13:54:44 +0100940#endif /* defined(__linux__) */