blob: 496beea9f6f7d7bdb922c0433cb3fa972a01c4e3 [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
Neels Hofmeyr87e45502017-06-20 00:17:59 +020078/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010079 * \param[in] fd osmocom file descriptor to be checked
80 * \returns true if registered; otherwise false
81 */
82bool osmo_fd_is_registered(struct osmo_fd *fd)
83{
84 struct osmo_fd *entry;
85 llist_for_each_entry(entry, &osmo_fds, list) {
86 if (entry == fd) {
87 return true;
88 }
89 }
90
91 return false;
92}
93
Neels Hofmeyr87e45502017-06-20 00:17:59 +020094/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020095 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020096 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020097 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020098int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010099{
100 int flags;
101
102 /* make FD nonblocking */
103 flags = fcntl(fd->fd, F_GETFL);
104 if (flags < 0)
105 return flags;
106 flags |= O_NONBLOCK;
107 flags = fcntl(fd->fd, F_SETFL, flags);
108 if (flags < 0)
109 return flags;
110
Harald Welte32e1f232011-06-26 13:07:18 +0200111 /* set close-on-exec flag */
112 flags = fcntl(fd->fd, F_GETFD);
113 if (flags < 0)
114 return flags;
115 flags |= FD_CLOEXEC;
116 flags = fcntl(fd->fd, F_SETFD, flags);
117 if (flags < 0)
118 return flags;
119
Harald Welteec8b4502010-02-20 20:34:29 +0100120 /* Register FD */
121 if (fd->fd > maxfd)
122 maxfd = fd->fd;
123
Pau Espin Pedrold05def02020-05-09 19:24:21 +0200124#ifdef OSMO_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +0100125 if (osmo_fd_is_registered(fd)) {
126 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
127 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800128 }
129#endif
130
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200131 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100132
133 return 0;
134}
135
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200136/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200137 * \param[in] fd osmocom file descriptor to be unregistered
138 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200139void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100140{
Philipp Maierb2888532016-12-09 14:07:18 +0100141 /* Note: when fd is inside the osmo_fds list (not registered before)
142 * this function will crash! If in doubt, check file descriptor with
143 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100144 unregistered_count++;
145 llist_del(&fd->list);
146}
147
Harald Welteea91a512017-07-13 14:28:30 +0200148/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
149 * \param[in] fd osmocom file descriptor to be unregistered + closed
150 *
151 * If \a fd is registered, we unregister it from the select() loop
152 * abstraction. We then close the fd and set it to -1, as well as
153 * unsetting any 'when' flags */
154void osmo_fd_close(struct osmo_fd *fd)
155{
156 if (osmo_fd_is_registered(fd))
157 osmo_fd_unregister(fd);
158 if (fd->fd != -1)
159 close(fd->fd);
160 fd->fd = -1;
161 fd->when = 0;
162}
163
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200164/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100165 * \param[in] _rset The readfds to populate
166 * \param[in] _wset The wrtiefds to populate
167 * \param[in] _eset The errorfds to populate
168 *
169 * \returns The highest file descriptor seen or 0 on an empty list
170 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100171inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100172{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100173 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
174 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100175 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100176
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200177 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welte16886992019-03-20 10:26:39 +0100178 if (ufd->when & OSMO_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100179 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100180
Harald Welte16886992019-03-20 10:26:39 +0100181 if (ufd->when & OSMO_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100182 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100183
Harald Welte16886992019-03-20 10:26:39 +0100184 if (ufd->when & OSMO_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100185 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100186
187 if (ufd->fd > highfd)
188 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100189 }
190
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100191 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100192}
Harald Welteec8b4502010-02-20 20:34:29 +0100193
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100194inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
195{
196 struct osmo_fd *ufd, *tmp;
197 int work = 0;
198 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100199
Harald Welteec8b4502010-02-20 20:34:29 +0100200restart:
201 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200202 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100203 int flags = 0;
204
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100205 if (FD_ISSET(ufd->fd, readset)) {
Harald Welte16886992019-03-20 10:26:39 +0100206 flags |= OSMO_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100207 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100208 }
209
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100210 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welte16886992019-03-20 10:26:39 +0100211 flags |= OSMO_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100212 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100213 }
214
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100215 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welte16886992019-03-20 10:26:39 +0100216 flags |= OSMO_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100217 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100218 }
219
220 if (flags) {
221 work = 1;
Harald Welte52a83752019-02-21 16:37:10 +0100222 /* make sure to clear any log context before processing the next incoming message
223 * as part of some file descriptor callback. This effectively prevents "context
224 * leaking" from processing of one message into processing of the next message as part
225 * of one iteration through the list of file descriptors here. See OS#3813 */
226 log_reset_context();
Harald Welteec8b4502010-02-20 20:34:29 +0100227 ufd->cb(ufd, flags);
228 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200229 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100230 * unregistered, they might have been consecutive and
231 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200232 /* this seems to happen with the last element of the list as well */
233 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100234 goto restart;
235 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100236
Harald Welteec8b4502010-02-20 20:34:29 +0100237 return work;
238}
239
Harald Welte2d906112019-03-18 17:17:43 +0100240static int _osmo_select_main(int polling)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100241{
242 fd_set readset, writeset, exceptset;
243 int rc;
244 struct timeval no_time = {0, 0};
245
246 FD_ZERO(&readset);
247 FD_ZERO(&writeset);
248 FD_ZERO(&exceptset);
249
250 /* prepare read and write fdsets */
251 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
252
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100253 if (!polling)
254 osmo_timers_prepare();
255 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
256 if (rc < 0)
257 return 0;
258
259 /* fire timers */
260 osmo_timers_update();
261
Harald Welte2d906112019-03-18 17:17:43 +0100262 OSMO_ASSERT(osmo_ctx->select);
263
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100264 /* call registered callback functions */
265 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
266}
267
Harald Welte2d906112019-03-18 17:17:43 +0100268/*! select main loop integration
269 * \param[in] polling should we pollonly (1) or block on select (0)
270 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
271 */
272int osmo_select_main(int polling)
273{
274 int rc = _osmo_select_main(polling);
275#ifndef EMBEDDED
276 if (talloc_total_size(osmo_ctx->select) != 0) {
277 osmo_panic("You cannot use the 'select' volatile "
278 "context if you don't use osmo_select_main_ctx()!\n");
279 }
280#endif
281 return rc;
282}
283
284#ifndef EMBEDDED
285/*! select main loop integration with temporary select-dispatch talloc context
286 * \param[in] polling should we pollonly (1) or block on select (0)
287 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
288 */
289int osmo_select_main_ctx(int polling)
290{
291 int rc = _osmo_select_main(polling);
292 /* free all the children of the volatile 'select' scope context */
293 talloc_free_children(osmo_ctx->select);
294 return rc;
295}
296#endif
297
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200298/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200299 * \param[in] fd file descriptor to use as search key
300 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100301struct osmo_fd *osmo_fd_get_by_fd(int fd)
302{
303 struct osmo_fd *ofd;
304
305 llist_for_each_entry(ofd, &osmo_fds, list) {
306 if (ofd->fd == fd)
307 return ofd;
308 }
309 return NULL;
310}
311
Harald Welte7a010b12019-04-06 13:46:40 +0200312/*! initialize the osmocom select abstraction for the current thread */
313void osmo_select_init(void)
314{
315 INIT_LLIST_HEAD(&osmo_fds);
316}
317
318/* ensure main thread always has pre-initialized osmo_fds */
319static __attribute__((constructor)) void on_dso_load_select(void)
320{
321 osmo_select_init();
322}
323
Harald Welteea4d8932017-12-03 16:13:39 +0100324#ifdef HAVE_SYS_TIMERFD_H
325#include <sys/timerfd.h>
326
327/*! disable the osmocom-wrapped timerfd */
328int osmo_timerfd_disable(struct osmo_fd *ofd)
329{
330 const struct itimerspec its_null = {
331 .it_value = { 0, 0 },
332 .it_interval = { 0, 0 },
333 };
334 return timerfd_settime(ofd->fd, 0, &its_null, NULL);
335}
336
Alexander Chemerise8ec2142020-05-09 01:57:16 +0300337/*! schedule the osmocom-wrapped timerfd to occur first at \a first, then periodically at \a interval
Harald Welteea4d8932017-12-03 16:13:39 +0100338 * \param[in] ofd Osmocom wrapped timerfd
339 * \param[in] first Relative time at which the timer should first execute (NULL = \a interval)
340 * \param[in] interval Time interval at which subsequent timer shall fire
341 * \returns 0 on success; negative on error */
342int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
343 const struct timespec *interval)
344{
345 struct itimerspec its;
346
347 if (ofd->fd < 0)
348 return -EINVAL;
349
350 /* first expiration */
351 if (first)
352 its.it_value = *first;
353 else
354 its.it_value = *interval;
355 /* repeating interval */
356 its.it_interval = *interval;
357
358 return timerfd_settime(ofd->fd, 0, &its, NULL);
359}
360
361/*! setup osmocom-wrapped timerfd
362 * \param[inout] ofd Osmocom-wrapped timerfd on which to operate
363 * \param[in] cb Call-back function called when timerfd becomes readable
364 * \param[in] data Opaque data to be passed on to call-back
365 * \returns 0 on success; negative on error
366 *
367 * We simply initialize the data structures here, but do not yet
368 * schedule the timer.
369 */
370int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data)
371{
372 ofd->cb = cb;
373 ofd->data = data;
Harald Welte16886992019-03-20 10:26:39 +0100374 ofd->when = OSMO_FD_READ;
Harald Welteea4d8932017-12-03 16:13:39 +0100375
376 if (ofd->fd < 0) {
Harald Welte37608f92018-10-21 12:32:55 +0200377 int rc;
378
Harald Welteea4d8932017-12-03 16:13:39 +0100379 ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
380 if (ofd->fd < 0)
381 return ofd->fd;
382
Harald Welte37608f92018-10-21 12:32:55 +0200383 rc = osmo_fd_register(ofd);
384 if (rc < 0) {
Harald Welte100e44e2020-04-18 21:16:43 +0200385 osmo_fd_unregister(ofd);
Harald Welte37608f92018-10-21 12:32:55 +0200386 close(ofd->fd);
387 ofd->fd = -1;
388 return rc;
389 }
Harald Welteea4d8932017-12-03 16:13:39 +0100390 }
391 return 0;
392}
393
394#endif /* HAVE_SYS_TIMERFD_H */
395
Harald Weltea70ac852020-04-17 19:20:01 +0200396#ifdef HAVE_SYS_SIGNALFD_H
397#include <sys/signalfd.h>
398
399static int signalfd_callback(struct osmo_fd *ofd, unsigned int what)
400{
401 struct osmo_signalfd *osfd = ofd->data;
402 struct signalfd_siginfo fdsi;
403 int rc;
404
405 rc = read(ofd->fd, &fdsi, sizeof(fdsi));
406 if (rc < 0) {
407 osmo_fd_unregister(ofd);
408 close(ofd->fd);
409 ofd->fd = -1;
410 return rc;
411 }
412
413 osfd->cb(osfd, &fdsi);
414
415 return 0;
416};
417
418/*! create a signalfd and register it with osmocom select loop.
419 * \param[in] ctx talloc context from which osmo_signalfd is to be allocated
420 * \param[in] set of signals to be accept via this file descriptor
421 * \param[in] cb call-back function to be called for each arriving signal
422 * \param[in] data opaque user-provided data to pass to callback
423 * \returns pointer to newly-allocated + registered osmo_signalfd; NULL on error */
424struct osmo_signalfd *
425osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data)
426{
427 struct osmo_signalfd *osfd = talloc_size(ctx, sizeof(*osfd));
428 int fd, rc;
429
430 if (!osfd)
431 return NULL;
432
433 osfd->data = data;
434 osfd->sigset = set;
435 osfd->cb = cb;
436
437 fd = signalfd(-1, &osfd->sigset, SFD_NONBLOCK);
438 if (fd < 0) {
439 talloc_free(osfd);
440 return NULL;
441 }
442
443 osmo_fd_setup(&osfd->ofd, fd, OSMO_FD_READ, signalfd_callback, osfd, 0);
444 rc = osmo_fd_register(&osfd->ofd);
445 if (rc < 0) {
446 close(fd);
447 talloc_free(osfd);
448 return NULL;
449 }
450
451 return osfd;
452}
453
454#endif /* HAVE_SYS_SIGNALFD_H */
Harald Welteea4d8932017-12-03 16:13:39 +0100455
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200456/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200457
Harald Welteec8b4502010-02-20 20:34:29 +0100458#endif /* _HAVE_SYS_SELECT_H */