blob: 394a59ddc2cf151af1e3f4b24b3a3d27fc86d61d [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 Welteec8b4502010-02-20 20:34:29 +010053static int maxfd = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020054static LLIST_HEAD(osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +010055static int unregistered_count;
56
Harald Welte6c0a0e62017-08-12 11:43:14 +020057/*! Set up an osmo-fd. Will not register it.
58 * \param[inout] ofd Osmo FD to be set-up
59 * \param[in] fd OS-level file descriptor number
Harald Welte16886992019-03-20 10:26:39 +010060 * \param[in] when bit-mask of OSMO_FD_{READ,WRITE,EXECEPT}
Harald Welte6c0a0e62017-08-12 11:43:14 +020061 * \param[in] cb Call-back function to be called
62 * \param[in] data Private context pointer
63 * \param[in] priv_nr Private number
64 */
65void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
66 int (*cb)(struct osmo_fd *fd, unsigned int what),
67 void *data, unsigned int priv_nr)
68{
69 ofd->fd = fd;
70 ofd->when = when;
71 ofd->cb = cb;
72 ofd->data = data;
73 ofd->priv_nr = priv_nr;
74}
Philipp Maierb2888532016-12-09 14:07:18 +010075
Neels Hofmeyr87e45502017-06-20 00:17:59 +020076/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010077 * \param[in] fd osmocom file descriptor to be checked
78 * \returns true if registered; otherwise false
79 */
80bool osmo_fd_is_registered(struct osmo_fd *fd)
81{
82 struct osmo_fd *entry;
83 llist_for_each_entry(entry, &osmo_fds, list) {
84 if (entry == fd) {
85 return true;
86 }
87 }
88
89 return false;
90}
91
Neels Hofmeyr87e45502017-06-20 00:17:59 +020092/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020093 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020094 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020095 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020096int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010097{
98 int flags;
99
100 /* make FD nonblocking */
101 flags = fcntl(fd->fd, F_GETFL);
102 if (flags < 0)
103 return flags;
104 flags |= O_NONBLOCK;
105 flags = fcntl(fd->fd, F_SETFL, flags);
106 if (flags < 0)
107 return flags;
108
Harald Welte32e1f232011-06-26 13:07:18 +0200109 /* set close-on-exec flag */
110 flags = fcntl(fd->fd, F_GETFD);
111 if (flags < 0)
112 return flags;
113 flags |= FD_CLOEXEC;
114 flags = fcntl(fd->fd, F_SETFD, flags);
115 if (flags < 0)
116 return flags;
117
Harald Welteec8b4502010-02-20 20:34:29 +0100118 /* Register FD */
119 if (fd->fd > maxfd)
120 maxfd = fd->fd;
121
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800122#ifdef BSC_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +0100123 if (osmo_fd_is_registered(fd)) {
124 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
125 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800126 }
127#endif
128
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200129 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100130
131 return 0;
132}
133
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200134/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200135 * \param[in] fd osmocom file descriptor to be unregistered
136 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200137void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100138{
Philipp Maierb2888532016-12-09 14:07:18 +0100139 /* Note: when fd is inside the osmo_fds list (not registered before)
140 * this function will crash! If in doubt, check file descriptor with
141 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100142 unregistered_count++;
143 llist_del(&fd->list);
144}
145
Harald Welteea91a512017-07-13 14:28:30 +0200146/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
147 * \param[in] fd osmocom file descriptor to be unregistered + closed
148 *
149 * If \a fd is registered, we unregister it from the select() loop
150 * abstraction. We then close the fd and set it to -1, as well as
151 * unsetting any 'when' flags */
152void osmo_fd_close(struct osmo_fd *fd)
153{
154 if (osmo_fd_is_registered(fd))
155 osmo_fd_unregister(fd);
156 if (fd->fd != -1)
157 close(fd->fd);
158 fd->fd = -1;
159 fd->when = 0;
160}
161
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200162/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100163 * \param[in] _rset The readfds to populate
164 * \param[in] _wset The wrtiefds to populate
165 * \param[in] _eset The errorfds to populate
166 *
167 * \returns The highest file descriptor seen or 0 on an empty list
168 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100169inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100170{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100171 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
172 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100173 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100174
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200175 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welte16886992019-03-20 10:26:39 +0100176 if (ufd->when & OSMO_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100177 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100178
Harald Welte16886992019-03-20 10:26:39 +0100179 if (ufd->when & OSMO_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100180 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100181
Harald Welte16886992019-03-20 10:26:39 +0100182 if (ufd->when & OSMO_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100183 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100184
185 if (ufd->fd > highfd)
186 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100187 }
188
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100189 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100190}
Harald Welteec8b4502010-02-20 20:34:29 +0100191
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100192inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
193{
194 struct osmo_fd *ufd, *tmp;
195 int work = 0;
196 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100197
Harald Welteec8b4502010-02-20 20:34:29 +0100198restart:
199 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200200 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100201 int flags = 0;
202
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100203 if (FD_ISSET(ufd->fd, readset)) {
Harald Welte16886992019-03-20 10:26:39 +0100204 flags |= OSMO_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100205 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100206 }
207
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100208 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welte16886992019-03-20 10:26:39 +0100209 flags |= OSMO_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100210 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100211 }
212
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100213 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welte16886992019-03-20 10:26:39 +0100214 flags |= OSMO_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100215 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100216 }
217
218 if (flags) {
219 work = 1;
Harald Welte52a83752019-02-21 16:37:10 +0100220 /* make sure to clear any log context before processing the next incoming message
221 * as part of some file descriptor callback. This effectively prevents "context
222 * leaking" from processing of one message into processing of the next message as part
223 * of one iteration through the list of file descriptors here. See OS#3813 */
224 log_reset_context();
Harald Welteec8b4502010-02-20 20:34:29 +0100225 ufd->cb(ufd, flags);
226 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200227 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100228 * unregistered, they might have been consecutive and
229 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200230 /* this seems to happen with the last element of the list as well */
231 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100232 goto restart;
233 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100234
Harald Welteec8b4502010-02-20 20:34:29 +0100235 return work;
236}
237
Harald Welte2d906112019-03-18 17:17:43 +0100238static int _osmo_select_main(int polling)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100239{
240 fd_set readset, writeset, exceptset;
241 int rc;
242 struct timeval no_time = {0, 0};
243
244 FD_ZERO(&readset);
245 FD_ZERO(&writeset);
246 FD_ZERO(&exceptset);
247
248 /* prepare read and write fdsets */
249 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
250
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100251 if (!polling)
252 osmo_timers_prepare();
253 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
254 if (rc < 0)
255 return 0;
256
257 /* fire timers */
258 osmo_timers_update();
259
Harald Welte2d906112019-03-18 17:17:43 +0100260 OSMO_ASSERT(osmo_ctx->select);
261
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100262 /* call registered callback functions */
263 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
264}
265
Harald Welte2d906112019-03-18 17:17:43 +0100266/*! select main loop integration
267 * \param[in] polling should we pollonly (1) or block on select (0)
268 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
269 */
270int osmo_select_main(int polling)
271{
272 int rc = _osmo_select_main(polling);
273#ifndef EMBEDDED
274 if (talloc_total_size(osmo_ctx->select) != 0) {
275 osmo_panic("You cannot use the 'select' volatile "
276 "context if you don't use osmo_select_main_ctx()!\n");
277 }
278#endif
279 return rc;
280}
281
282#ifndef EMBEDDED
283/*! select main loop integration with temporary select-dispatch talloc context
284 * \param[in] polling should we pollonly (1) or block on select (0)
285 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
286 */
287int osmo_select_main_ctx(int polling)
288{
289 int rc = _osmo_select_main(polling);
290 /* free all the children of the volatile 'select' scope context */
291 talloc_free_children(osmo_ctx->select);
292 return rc;
293}
294#endif
295
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200296/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200297 * \param[in] fd file descriptor to use as search key
298 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100299struct osmo_fd *osmo_fd_get_by_fd(int fd)
300{
301 struct osmo_fd *ofd;
302
303 llist_for_each_entry(ofd, &osmo_fds, list) {
304 if (ofd->fd == fd)
305 return ofd;
306 }
307 return NULL;
308}
309
Harald Welteea4d8932017-12-03 16:13:39 +0100310#ifdef HAVE_SYS_TIMERFD_H
311#include <sys/timerfd.h>
312
313/*! disable the osmocom-wrapped timerfd */
314int osmo_timerfd_disable(struct osmo_fd *ofd)
315{
316 const struct itimerspec its_null = {
317 .it_value = { 0, 0 },
318 .it_interval = { 0, 0 },
319 };
320 return timerfd_settime(ofd->fd, 0, &its_null, NULL);
321}
322
323/*! schedule the osmcoom-wrapped timerfd to occur first at \a first, then periodically at \a interval
324 * \param[in] ofd Osmocom wrapped timerfd
325 * \param[in] first Relative time at which the timer should first execute (NULL = \a interval)
326 * \param[in] interval Time interval at which subsequent timer shall fire
327 * \returns 0 on success; negative on error */
328int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
329 const struct timespec *interval)
330{
331 struct itimerspec its;
332
333 if (ofd->fd < 0)
334 return -EINVAL;
335
336 /* first expiration */
337 if (first)
338 its.it_value = *first;
339 else
340 its.it_value = *interval;
341 /* repeating interval */
342 its.it_interval = *interval;
343
344 return timerfd_settime(ofd->fd, 0, &its, NULL);
345}
346
347/*! setup osmocom-wrapped timerfd
348 * \param[inout] ofd Osmocom-wrapped timerfd on which to operate
349 * \param[in] cb Call-back function called when timerfd becomes readable
350 * \param[in] data Opaque data to be passed on to call-back
351 * \returns 0 on success; negative on error
352 *
353 * We simply initialize the data structures here, but do not yet
354 * schedule the timer.
355 */
356int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data)
357{
358 ofd->cb = cb;
359 ofd->data = data;
Harald Welte16886992019-03-20 10:26:39 +0100360 ofd->when = OSMO_FD_READ;
Harald Welteea4d8932017-12-03 16:13:39 +0100361
362 if (ofd->fd < 0) {
Harald Welte37608f92018-10-21 12:32:55 +0200363 int rc;
364
Harald Welteea4d8932017-12-03 16:13:39 +0100365 ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
366 if (ofd->fd < 0)
367 return ofd->fd;
368
Harald Welte37608f92018-10-21 12:32:55 +0200369 rc = osmo_fd_register(ofd);
370 if (rc < 0) {
371 close(ofd->fd);
372 ofd->fd = -1;
373 return rc;
374 }
Harald Welteea4d8932017-12-03 16:13:39 +0100375 }
376 return 0;
377}
378
379#endif /* HAVE_SYS_TIMERFD_H */
380
381
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200382/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200383
Harald Welteec8b4502010-02-20 20:34:29 +0100384#endif /* _HAVE_SYS_SELECT_H */