blob: 4e7be3546c1103a9d0111d62b219f0c50e0a4c3e [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 Welteec8b4502010-02-20 20:34:29 +010039
Harald Welte54844802010-02-20 22:23:08 +010040#include "../config.h"
41
Harald Welteec8b4502010-02-20 20:34:29 +010042#ifdef HAVE_SYS_SELECT_H
Harald Welte1d604d82017-05-17 15:32:35 +010043#include <sys/select.h>
Harald Welteec8b4502010-02-20 20:34:29 +010044
Harald Welteba6988b2011-08-17 12:46:48 +020045/*! \addtogroup select
46 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020047 * select() loop abstraction
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020048 *
49 * \file select.c */
Harald Welteba6988b2011-08-17 12:46:48 +020050
Harald Welteec8b4502010-02-20 20:34:29 +010051static int maxfd = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020052static LLIST_HEAD(osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +010053static int unregistered_count;
54
Harald Welte6c0a0e62017-08-12 11:43:14 +020055/*! Set up an osmo-fd. Will not register it.
56 * \param[inout] ofd Osmo FD to be set-up
57 * \param[in] fd OS-level file descriptor number
58 * \param[in] when bit-mask of BSC_FD_{READ,WRITE,EXECEPT}
59 * \param[in] cb Call-back function to be called
60 * \param[in] data Private context pointer
61 * \param[in] priv_nr Private number
62 */
63void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
64 int (*cb)(struct osmo_fd *fd, unsigned int what),
65 void *data, unsigned int priv_nr)
66{
67 ofd->fd = fd;
68 ofd->when = when;
69 ofd->cb = cb;
70 ofd->data = data;
71 ofd->priv_nr = priv_nr;
72}
Philipp Maierb2888532016-12-09 14:07:18 +010073
Neels Hofmeyr87e45502017-06-20 00:17:59 +020074/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010075 * \param[in] fd osmocom file descriptor to be checked
76 * \returns true if registered; otherwise false
77 */
78bool osmo_fd_is_registered(struct osmo_fd *fd)
79{
80 struct osmo_fd *entry;
81 llist_for_each_entry(entry, &osmo_fds, list) {
82 if (entry == fd) {
83 return true;
84 }
85 }
86
87 return false;
88}
89
Neels Hofmeyr87e45502017-06-20 00:17:59 +020090/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020091 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020092 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020093 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020094int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010095{
96 int flags;
97
98 /* make FD nonblocking */
99 flags = fcntl(fd->fd, F_GETFL);
100 if (flags < 0)
101 return flags;
102 flags |= O_NONBLOCK;
103 flags = fcntl(fd->fd, F_SETFL, flags);
104 if (flags < 0)
105 return flags;
106
Harald Welte32e1f232011-06-26 13:07:18 +0200107 /* set close-on-exec flag */
108 flags = fcntl(fd->fd, F_GETFD);
109 if (flags < 0)
110 return flags;
111 flags |= FD_CLOEXEC;
112 flags = fcntl(fd->fd, F_SETFD, flags);
113 if (flags < 0)
114 return flags;
115
Harald Welteec8b4502010-02-20 20:34:29 +0100116 /* Register FD */
117 if (fd->fd > maxfd)
118 maxfd = fd->fd;
119
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800120#ifdef BSC_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +0100121 if (osmo_fd_is_registered(fd)) {
122 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
123 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800124 }
125#endif
126
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200127 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100128
129 return 0;
130}
131
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200132/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200133 * \param[in] fd osmocom file descriptor to be unregistered
134 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200135void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100136{
Philipp Maierb2888532016-12-09 14:07:18 +0100137 /* Note: when fd is inside the osmo_fds list (not registered before)
138 * this function will crash! If in doubt, check file descriptor with
139 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100140 unregistered_count++;
141 llist_del(&fd->list);
142}
143
Harald Welteea91a512017-07-13 14:28:30 +0200144/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
145 * \param[in] fd osmocom file descriptor to be unregistered + closed
146 *
147 * If \a fd is registered, we unregister it from the select() loop
148 * abstraction. We then close the fd and set it to -1, as well as
149 * unsetting any 'when' flags */
150void osmo_fd_close(struct osmo_fd *fd)
151{
152 if (osmo_fd_is_registered(fd))
153 osmo_fd_unregister(fd);
154 if (fd->fd != -1)
155 close(fd->fd);
156 fd->fd = -1;
157 fd->when = 0;
158}
159
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200160/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100161 * \param[in] _rset The readfds to populate
162 * \param[in] _wset The wrtiefds to populate
163 * \param[in] _eset The errorfds to populate
164 *
165 * \returns The highest file descriptor seen or 0 on an empty list
166 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100167inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100168{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100169 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
170 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100171 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100172
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200173 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100174 if (ufd->when & BSC_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100175 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100176
177 if (ufd->when & BSC_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100178 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100179
180 if (ufd->when & BSC_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100181 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100182
183 if (ufd->fd > highfd)
184 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100185 }
186
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100187 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100188}
Harald Welteec8b4502010-02-20 20:34:29 +0100189
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100190inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
191{
192 struct osmo_fd *ufd, *tmp;
193 int work = 0;
194 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100195
Harald Welteec8b4502010-02-20 20:34:29 +0100196restart:
197 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200198 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100199 int flags = 0;
200
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100201 if (FD_ISSET(ufd->fd, readset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100202 flags |= BSC_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100203 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100204 }
205
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100206 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100207 flags |= BSC_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100208 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100209 }
210
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100211 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100212 flags |= BSC_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100213 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100214 }
215
216 if (flags) {
217 work = 1;
Harald Welte52a83752019-02-21 16:37:10 +0100218 /* make sure to clear any log context before processing the next incoming message
219 * as part of some file descriptor callback. This effectively prevents "context
220 * leaking" from processing of one message into processing of the next message as part
221 * of one iteration through the list of file descriptors here. See OS#3813 */
222 log_reset_context();
Harald Welteec8b4502010-02-20 20:34:29 +0100223 ufd->cb(ufd, flags);
224 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200225 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100226 * unregistered, they might have been consecutive and
227 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200228 /* this seems to happen with the last element of the list as well */
229 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100230 goto restart;
231 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100232
Harald Welteec8b4502010-02-20 20:34:29 +0100233 return work;
234}
235
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200236/*! select main loop integration
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100237 * \param[in] polling should we pollonly (1) or block on select (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200238 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100239 */
240int osmo_select_main(int polling)
241{
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
262 /* call registered callback functions */
263 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
264}
265
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200266/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200267 * \param[in] fd file descriptor to use as search key
268 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100269struct osmo_fd *osmo_fd_get_by_fd(int fd)
270{
271 struct osmo_fd *ofd;
272
273 llist_for_each_entry(ofd, &osmo_fds, list) {
274 if (ofd->fd == fd)
275 return ofd;
276 }
277 return NULL;
278}
279
Harald Welteea4d8932017-12-03 16:13:39 +0100280#ifdef HAVE_SYS_TIMERFD_H
281#include <sys/timerfd.h>
282
283/*! disable the osmocom-wrapped timerfd */
284int osmo_timerfd_disable(struct osmo_fd *ofd)
285{
286 const struct itimerspec its_null = {
287 .it_value = { 0, 0 },
288 .it_interval = { 0, 0 },
289 };
290 return timerfd_settime(ofd->fd, 0, &its_null, NULL);
291}
292
293/*! schedule the osmcoom-wrapped timerfd to occur first at \a first, then periodically at \a interval
294 * \param[in] ofd Osmocom wrapped timerfd
295 * \param[in] first Relative time at which the timer should first execute (NULL = \a interval)
296 * \param[in] interval Time interval at which subsequent timer shall fire
297 * \returns 0 on success; negative on error */
298int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
299 const struct timespec *interval)
300{
301 struct itimerspec its;
302
303 if (ofd->fd < 0)
304 return -EINVAL;
305
306 /* first expiration */
307 if (first)
308 its.it_value = *first;
309 else
310 its.it_value = *interval;
311 /* repeating interval */
312 its.it_interval = *interval;
313
314 return timerfd_settime(ofd->fd, 0, &its, NULL);
315}
316
317/*! setup osmocom-wrapped timerfd
318 * \param[inout] ofd Osmocom-wrapped timerfd on which to operate
319 * \param[in] cb Call-back function called when timerfd becomes readable
320 * \param[in] data Opaque data to be passed on to call-back
321 * \returns 0 on success; negative on error
322 *
323 * We simply initialize the data structures here, but do not yet
324 * schedule the timer.
325 */
326int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data)
327{
328 ofd->cb = cb;
329 ofd->data = data;
330 ofd->when = BSC_FD_READ;
331
332 if (ofd->fd < 0) {
Harald Welte37608f92018-10-21 12:32:55 +0200333 int rc;
334
Harald Welteea4d8932017-12-03 16:13:39 +0100335 ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
336 if (ofd->fd < 0)
337 return ofd->fd;
338
Harald Welte37608f92018-10-21 12:32:55 +0200339 rc = osmo_fd_register(ofd);
340 if (rc < 0) {
341 close(ofd->fd);
342 ofd->fd = -1;
343 return rc;
344 }
Harald Welteea4d8932017-12-03 16:13:39 +0100345 }
346 return 0;
347}
348
349#endif /* HAVE_SYS_TIMERFD_H */
350
351
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200352/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200353
Harald Welteec8b4502010-02-20 20:34:29 +0100354#endif /* _HAVE_SYS_SELECT_H */