aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2024-02-23 16:08:49 +0100
committerHarald Welte <laforge@osmocom.org>2024-02-23 18:01:36 +0100
commitb365b1d094163c2bcb297488ca55562d9fa090b4 (patch)
treecb07371d6065b4419d20fdd88beac4d1dd01397c /src
parente8ab1b77d880d54b9ecdb0ba35b413e43e2df34e (diff)
osmo_io: Change struct osmo_io_ops to contain struct, not union
As we introduce more modes, and each mode aliases call-back function pointers to those of another mode, we have more and more error cases where we (for exampele) access read_cb, but in reality the user has populated recvfrom_cb. Let's use a struct, meaning that call-backs of one mode no longer alias to the same memory locations of call-backs fro another mode. This allows us to properly check if the user actually provided the right callbacks for the given mode of the iofd. This breaks ABI, but luckily not API. So a simple recompile of higher-layer library + application code will work. Change-Id: I9d302df8d00369e7b30437a52deb205f75882be3
Diffstat (limited to 'src')
-rw-r--r--src/core/osmo_io.c34
-rw-r--r--src/core/osmo_io_poll.c14
2 files changed, 43 insertions, 5 deletions
diff --git a/src/core/osmo_io.c b/src/core/osmo_io.c
index e059f873..9de9e2ef 100644
--- a/src/core/osmo_io.c
+++ b/src/core/osmo_io.c
@@ -306,6 +306,8 @@ void iofd_handle_segmented_read(struct osmo_io_fd *iofd, struct msgb *msg, int r
int res;
struct msgb *pending = NULL;
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE);
+
if (rc <= 0) {
iofd->io_ops.read_cb(iofd, rc, msg);
return;
@@ -473,6 +475,24 @@ int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendto_
return 0;
}
+static int check_mode_callback_compat(enum osmo_io_fd_mode mode, const struct osmo_io_ops *ops)
+{
+ switch (mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ if (ops->recvfrom_cb || ops->sendto_cb)
+ return false;
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ if (ops->read_cb || ops->write_cb)
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
/*! Allocate and setup a new iofd.
* \param[in] ctx the parent context from which to allocate
* \param[in] fd the underlying system file descriptor
@@ -496,6 +516,9 @@ struct osmo_io_fd *osmo_iofd_setup(const void *ctx, int fd, const char *name, en
return NULL;
}
+ if (!check_mode_callback_compat(mode, ioops))
+ return NULL;
+
iofd = talloc_zero(ctx, struct osmo_io_fd);
if (!iofd)
return NULL;
@@ -543,8 +566,10 @@ int osmo_iofd_register(struct osmo_io_fd *iofd, int fd)
return rc;
IOFD_FLAG_UNSET(iofd, IOFD_FLAG_CLOSED);
- if (iofd->io_ops.read_cb)
+ if ((iofd->mode == OSMO_IO_FD_MODE_READ_WRITE && iofd->io_ops.read_cb) ||
+ (iofd->mode == OSMO_IO_FD_MODE_RECVFROM_SENDTO && iofd->io_ops.recvfrom_cb)) {
osmo_iofd_ops.read_enable(iofd);
+ }
if (iofd->tx_queue.current_length > 0)
osmo_iofd_ops.write_enable(iofd);
@@ -722,8 +747,11 @@ void osmo_iofd_set_name(struct osmo_io_fd *iofd, const char *name)
/*! Set the osmo_io_ops for an iofd.
* \param[in] iofd Target iofd file descriptor
* \param[in] ioops osmo_io_ops structure to be set */
-void osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioops)
+int osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioops)
{
+ if (!check_mode_callback_compat(iofd->mode, ioops))
+ return -EINVAL;
+
iofd->io_ops = *ioops;
switch (iofd->mode) {
@@ -743,6 +771,8 @@ void osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioop
default:
OSMO_ASSERT(0);
}
+
+ return 0;
}
/*! Notify the user if/when the socket is connected.
diff --git a/src/core/osmo_io_poll.c b/src/core/osmo_io_poll.c
index 5000dca9..d7f80c01 100644
--- a/src/core/osmo_io_poll.c
+++ b/src/core/osmo_io_poll.c
@@ -81,10 +81,18 @@ static void iofd_poll_ofd_cb_recvmsg_sendmsg(struct osmo_fd *ofd, unsigned int w
rc = sendmsg(ofd->fd, &msghdr->hdr, msghdr->flags);
iofd_handle_send_completion(iofd, rc, msghdr);
} else {
- if (iofd->mode == OSMO_IO_FD_MODE_READ_WRITE)
- /* Socket is writable, but we have no data to send. A non-blocking/async
- connect() is signalled this way. */
+ /* Socket is writable, but we have no data to send. A non-blocking/async
+ connect() is signalled this way. */
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
iofd->io_ops.write_cb(iofd, 0, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd->io_ops.sendto_cb(iofd, 0, NULL, NULL);
+ break;
+ default:
+ break;
+ }
if (osmo_iofd_txqueue_len(iofd) == 0)
iofd_poll_ops.write_disable(iofd);
}