blob: 1bb354b9fe7d121297dd2209b9293c31498d5b09 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file select.c
2 * select filedescriptor handling.
3 * Taken from:
Harald Welteec8b4502010-02-20 20:34:29 +01004 * userspace logging daemon for the iptables ULOG target
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02005 * of the linux 2.4 netfilter subsystem. */
6/*
Harald Welteec8b4502010-02-20 20:34:29 +01007 * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org>
Harald Weltee08da972017-11-13 01:00:26 +09008 * All Rights Reserverd.
9 *
10 * SPDX-License-Identifier: GPL-2.0+
Harald Welteec8b4502010-02-20 20:34:29 +010011 *
12 * This program is free software; you can redistribute it and/or modify
Harald Welte9d92f0e2010-10-31 13:56:45 +010013 * 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.
Harald Welteec8b4502010-02-20 20:34:29 +010016 *
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 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010024 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 * MA 02110-1301, USA.
Harald Welteec8b4502010-02-20 20:34:29 +010026 */
27
28#include <fcntl.h>
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080029#include <stdio.h>
Harald Welteea91a512017-07-13 14:28:30 +020030#include <unistd.h>
Harald Welte9e166e82014-03-10 17:28:33 +010031#include <string.h>
Philipp Maierb2888532016-12-09 14:07:18 +010032#include <stdbool.h>
Harald Welteea4d8932017-12-03 16:13:39 +010033#include <errno.h>
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080034
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010035#include <osmocom/core/select.h>
36#include <osmocom/core/linuxlist.h>
37#include <osmocom/core/timer.h>
Harald Welte52a83752019-02-21 16:37:10 +010038#include <osmocom/core/logging.h>
Harald Welte2d906112019-03-18 17:17:43 +010039#include <osmocom/core/talloc.h>
40#include <osmocom/core/utils.h>
Harald Welteec8b4502010-02-20 20:34:29 +010041
Harald Welte54844802010-02-20 22:23:08 +010042#include "../config.h"
43
Harald Welteec8b4502010-02-20 20:34:29 +010044#ifdef HAVE_SYS_SELECT_H
Harald Welte1d604d82017-05-17 15:32:35 +010045#include <sys/select.h>
Harald Welteec8b4502010-02-20 20:34:29 +010046
Harald Welteba6988b2011-08-17 12:46:48 +020047/*! \addtogroup select
48 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020049 * select() loop abstraction
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020050 *
51 * \file select.c */
Harald Welteba6988b2011-08-17 12:46:48 +020052
Harald Welte7a010b12019-04-06 13:46:40 +020053/* keep a set of file descriptors per-thread, so that each thread can have its own
54 * distinct set of file descriptors to interact with */
55static __thread int maxfd = 0;
56static __thread struct llist_head osmo_fds; /* TLS cannot use LLIST_HEAD() */
57static __thread int unregistered_count;
Harald Welteec8b4502010-02-20 20:34:29 +010058
Harald Welte6c0a0e62017-08-12 11:43:14 +020059/*! Set up an osmo-fd. Will not register it.
60 * \param[inout] ofd Osmo FD to be set-up
61 * \param[in] fd OS-level file descriptor number
Harald Welte16886992019-03-20 10:26:39 +010062 * \param[in] when bit-mask of OSMO_FD_{READ,WRITE,EXECEPT}
Harald Welte6c0a0e62017-08-12 11:43:14 +020063 * \param[in] cb Call-back function to be called
64 * \param[in] data Private context pointer
65 * \param[in] priv_nr Private number
66 */
67void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
68 int (*cb)(struct osmo_fd *fd, unsigned int what),
69 void *data, unsigned int priv_nr)
70{
71 ofd->fd = fd;
72 ofd->when = when;
73 ofd->cb = cb;
74 ofd->data = data;
75 ofd->priv_nr = priv_nr;
76}
Philipp Maierb2888532016-12-09 14:07:18 +010077
Harald Welte7e657912020-10-18 22:07:21 +020078/*! Update the 'when' field of osmo_fd. "ofd->when = (ofd->when & when_mask) | when".
79 * Use this function instead of directly modifying ofd->when, as the latter will be
80 * removed soon. */
81void osmo_fd_update_when(struct osmo_fd *ofd, unsigned int when_mask, unsigned int when)
82{
83 ofd->when &= when_mask;
84 ofd->when |= when;
85}
86
Neels Hofmeyr87e45502017-06-20 00:17:59 +020087/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010088 * \param[in] fd osmocom file descriptor to be checked
89 * \returns true if registered; otherwise false
90 */
91bool osmo_fd_is_registered(struct osmo_fd *fd)
92{
93 struct osmo_fd *entry;
94 llist_for_each_entry(entry, &osmo_fds, list) {
95 if (entry == fd) {
96 return true;
97 }
98 }
99
100 return false;
101}
102
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200103/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200104 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200105 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +0200106 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200107int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100108{
109 int flags;
110
111 /* make FD nonblocking */
112 flags = fcntl(fd->fd, F_GETFL);
113 if (flags < 0)
114 return flags;
115 flags |= O_NONBLOCK;
116 flags = fcntl(fd->fd, F_SETFL, flags);
117 if (flags < 0)
118 return flags;
119
Harald Welte32e1f232011-06-26 13:07:18 +0200120 /* set close-on-exec flag */
121 flags = fcntl(fd->fd, F_GETFD);
122 if (flags < 0)
123 return flags;
124 flags |= FD_CLOEXEC;
125 flags = fcntl(fd->fd, F_SETFD, flags);
126 if (flags < 0)
127 return flags;
128
Harald Welteec8b4502010-02-20 20:34:29 +0100129 /* Register FD */
130 if (fd->fd > maxfd)
131 maxfd = fd->fd;
132
Pau Espin Pedrold05def02020-05-09 19:24:21 +0200133#ifdef OSMO_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +0100134 if (osmo_fd_is_registered(fd)) {
135 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
136 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800137 }
138#endif
139
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200140 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100141
142 return 0;
143}
144
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200145/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200146 * \param[in] fd osmocom file descriptor to be unregistered
147 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200148void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100149{
Philipp Maierb2888532016-12-09 14:07:18 +0100150 /* Note: when fd is inside the osmo_fds list (not registered before)
151 * this function will crash! If in doubt, check file descriptor with
152 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100153 unregistered_count++;
154 llist_del(&fd->list);
155}
156
Harald Welteea91a512017-07-13 14:28:30 +0200157/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
158 * \param[in] fd osmocom file descriptor to be unregistered + closed
159 *
160 * If \a fd is registered, we unregister it from the select() loop
161 * abstraction. We then close the fd and set it to -1, as well as
162 * unsetting any 'when' flags */
163void osmo_fd_close(struct osmo_fd *fd)
164{
165 if (osmo_fd_is_registered(fd))
166 osmo_fd_unregister(fd);
167 if (fd->fd != -1)
168 close(fd->fd);
169 fd->fd = -1;
170 fd->when = 0;
171}
172
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200173/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100174 * \param[in] _rset The readfds to populate
175 * \param[in] _wset The wrtiefds to populate
176 * \param[in] _eset The errorfds to populate
177 *
178 * \returns The highest file descriptor seen or 0 on an empty list
179 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100180inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100181{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100182 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
183 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100184 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100185
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200186 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welte16886992019-03-20 10:26:39 +0100187 if (ufd->when & OSMO_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100188 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100189
Harald Welte16886992019-03-20 10:26:39 +0100190 if (ufd->when & OSMO_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100191 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100192
Harald Welte16886992019-03-20 10:26:39 +0100193 if (ufd->when & OSMO_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100194 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100195
196 if (ufd->fd > highfd)
197 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100198 }
199
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100200 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100201}
Harald Welteec8b4502010-02-20 20:34:29 +0100202
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100203inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
204{
205 struct osmo_fd *ufd, *tmp;
206 int work = 0;
207 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100208
Harald Welteec8b4502010-02-20 20:34:29 +0100209restart:
210 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200211 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100212 int flags = 0;
213
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100214 if (FD_ISSET(ufd->fd, readset)) {
Harald Welte16886992019-03-20 10:26:39 +0100215 flags |= OSMO_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100216 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100217 }
218
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100219 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welte16886992019-03-20 10:26:39 +0100220 flags |= OSMO_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100221 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100222 }
223
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100224 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welte16886992019-03-20 10:26:39 +0100225 flags |= OSMO_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100226 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100227 }
228
229 if (flags) {
230 work = 1;
Harald Welte52a83752019-02-21 16:37:10 +0100231 /* make sure to clear any log context before processing the next incoming message
232 * as part of some file descriptor callback. This effectively prevents "context
233 * leaking" from processing of one message into processing of the next message as part
234 * of one iteration through the list of file descriptors here. See OS#3813 */
235 log_reset_context();
Harald Welteec8b4502010-02-20 20:34:29 +0100236 ufd->cb(ufd, flags);
237 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200238 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100239 * unregistered, they might have been consecutive and
240 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200241 /* this seems to happen with the last element of the list as well */
242 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100243 goto restart;
244 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100245
Harald Welteec8b4502010-02-20 20:34:29 +0100246 return work;
247}
248
Harald Welte2d906112019-03-18 17:17:43 +0100249static int _osmo_select_main(int polling)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100250{
251 fd_set readset, writeset, exceptset;
252 int rc;
253 struct timeval no_time = {0, 0};
254
255 FD_ZERO(&readset);
256 FD_ZERO(&writeset);
257 FD_ZERO(&exceptset);
258
259 /* prepare read and write fdsets */
260 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
261
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100262 if (!polling)
263 osmo_timers_prepare();
264 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
265 if (rc < 0)
266 return 0;
267
268 /* fire timers */
269 osmo_timers_update();
270
Harald Welte2d906112019-03-18 17:17:43 +0100271 OSMO_ASSERT(osmo_ctx->select);
272
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100273 /* call registered callback functions */
274 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
275}
276
Harald Welte2d906112019-03-18 17:17:43 +0100277/*! select main loop integration
278 * \param[in] polling should we pollonly (1) or block on select (0)
279 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
280 */
281int osmo_select_main(int polling)
282{
283 int rc = _osmo_select_main(polling);
284#ifndef EMBEDDED
285 if (talloc_total_size(osmo_ctx->select) != 0) {
286 osmo_panic("You cannot use the 'select' volatile "
287 "context if you don't use osmo_select_main_ctx()!\n");
288 }
289#endif
290 return rc;
291}
292
293#ifndef EMBEDDED
294/*! select main loop integration with temporary select-dispatch talloc context
295 * \param[in] polling should we pollonly (1) or block on select (0)
296 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
297 */
298int osmo_select_main_ctx(int polling)
299{
300 int rc = _osmo_select_main(polling);
301 /* free all the children of the volatile 'select' scope context */
302 talloc_free_children(osmo_ctx->select);
303 return rc;
304}
305#endif
306
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200307/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200308 * \param[in] fd file descriptor to use as search key
309 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100310struct osmo_fd *osmo_fd_get_by_fd(int fd)
311{
312 struct osmo_fd *ofd;
313
314 llist_for_each_entry(ofd, &osmo_fds, list) {
315 if (ofd->fd == fd)
316 return ofd;
317 }
318 return NULL;
319}
320
Harald Welte7a010b12019-04-06 13:46:40 +0200321/*! initialize the osmocom select abstraction for the current thread */
322void osmo_select_init(void)
323{
324 INIT_LLIST_HEAD(&osmo_fds);
325}
326
327/* ensure main thread always has pre-initialized osmo_fds */
328static __attribute__((constructor)) void on_dso_load_select(void)
329{
330 osmo_select_init();
331}
332
Harald Welteea4d8932017-12-03 16:13:39 +0100333#ifdef HAVE_SYS_TIMERFD_H
334#include <sys/timerfd.h>
335
336/*! disable the osmocom-wrapped timerfd */
337int osmo_timerfd_disable(struct osmo_fd *ofd)
338{
339 const struct itimerspec its_null = {
340 .it_value = { 0, 0 },
341 .it_interval = { 0, 0 },
342 };
343 return timerfd_settime(ofd->fd, 0, &its_null, NULL);
344}
345
Alexander Chemerise8ec2142020-05-09 01:57:16 +0300346/*! schedule the osmocom-wrapped timerfd to occur first at \a first, then periodically at \a interval
Harald Welteea4d8932017-12-03 16:13:39 +0100347 * \param[in] ofd Osmocom wrapped timerfd
348 * \param[in] first Relative time at which the timer should first execute (NULL = \a interval)
349 * \param[in] interval Time interval at which subsequent timer shall fire
350 * \returns 0 on success; negative on error */
351int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
352 const struct timespec *interval)
353{
354 struct itimerspec its;
355
356 if (ofd->fd < 0)
357 return -EINVAL;
358
359 /* first expiration */
360 if (first)
361 its.it_value = *first;
362 else
363 its.it_value = *interval;
364 /* repeating interval */
365 its.it_interval = *interval;
366
367 return timerfd_settime(ofd->fd, 0, &its, NULL);
368}
369
370/*! setup osmocom-wrapped timerfd
371 * \param[inout] ofd Osmocom-wrapped timerfd on which to operate
372 * \param[in] cb Call-back function called when timerfd becomes readable
373 * \param[in] data Opaque data to be passed on to call-back
374 * \returns 0 on success; negative on error
375 *
376 * We simply initialize the data structures here, but do not yet
377 * schedule the timer.
378 */
379int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data)
380{
381 ofd->cb = cb;
382 ofd->data = data;
Harald Welte16886992019-03-20 10:26:39 +0100383 ofd->when = OSMO_FD_READ;
Harald Welteea4d8932017-12-03 16:13:39 +0100384
385 if (ofd->fd < 0) {
Harald Welte37608f92018-10-21 12:32:55 +0200386 int rc;
387
Harald Welteea4d8932017-12-03 16:13:39 +0100388 ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
389 if (ofd->fd < 0)
390 return ofd->fd;
391
Harald Welte37608f92018-10-21 12:32:55 +0200392 rc = osmo_fd_register(ofd);
393 if (rc < 0) {
Harald Welte100e44e2020-04-18 21:16:43 +0200394 osmo_fd_unregister(ofd);
Harald Welte37608f92018-10-21 12:32:55 +0200395 close(ofd->fd);
396 ofd->fd = -1;
397 return rc;
398 }
Harald Welteea4d8932017-12-03 16:13:39 +0100399 }
400 return 0;
401}
402
403#endif /* HAVE_SYS_TIMERFD_H */
404
Harald Weltea70ac852020-04-17 19:20:01 +0200405#ifdef HAVE_SYS_SIGNALFD_H
406#include <sys/signalfd.h>
407
408static int signalfd_callback(struct osmo_fd *ofd, unsigned int what)
409{
410 struct osmo_signalfd *osfd = ofd->data;
411 struct signalfd_siginfo fdsi;
412 int rc;
413
414 rc = read(ofd->fd, &fdsi, sizeof(fdsi));
415 if (rc < 0) {
416 osmo_fd_unregister(ofd);
417 close(ofd->fd);
418 ofd->fd = -1;
419 return rc;
420 }
421
422 osfd->cb(osfd, &fdsi);
423
424 return 0;
425};
426
427/*! create a signalfd and register it with osmocom select loop.
428 * \param[in] ctx talloc context from which osmo_signalfd is to be allocated
429 * \param[in] set of signals to be accept via this file descriptor
430 * \param[in] cb call-back function to be called for each arriving signal
431 * \param[in] data opaque user-provided data to pass to callback
432 * \returns pointer to newly-allocated + registered osmo_signalfd; NULL on error */
433struct osmo_signalfd *
434osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data)
435{
436 struct osmo_signalfd *osfd = talloc_size(ctx, sizeof(*osfd));
437 int fd, rc;
438
439 if (!osfd)
440 return NULL;
441
442 osfd->data = data;
443 osfd->sigset = set;
444 osfd->cb = cb;
445
446 fd = signalfd(-1, &osfd->sigset, SFD_NONBLOCK);
447 if (fd < 0) {
448 talloc_free(osfd);
449 return NULL;
450 }
451
452 osmo_fd_setup(&osfd->ofd, fd, OSMO_FD_READ, signalfd_callback, osfd, 0);
453 rc = osmo_fd_register(&osfd->ofd);
454 if (rc < 0) {
455 close(fd);
456 talloc_free(osfd);
457 return NULL;
458 }
459
460 return osfd;
461}
462
463#endif /* HAVE_SYS_SIGNALFD_H */
Harald Welteea4d8932017-12-03 16:13:39 +0100464
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200465/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200466
Harald Welteec8b4502010-02-20 20:34:29 +0100467#endif /* _HAVE_SYS_SELECT_H */