blob: b594ca558eacd99ae7b96474648e236a4098bf1b [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 Welteec8b4502010-02-20 20:34:29 +010038
Harald Welte54844802010-02-20 22:23:08 +010039#include "../config.h"
40
Harald Welteec8b4502010-02-20 20:34:29 +010041#ifdef HAVE_SYS_SELECT_H
Harald Welte1d604d82017-05-17 15:32:35 +010042#include <sys/select.h>
Harald Welteec8b4502010-02-20 20:34:29 +010043
Harald Welteba6988b2011-08-17 12:46:48 +020044/*! \addtogroup select
45 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020046 * select() loop abstraction
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020047 *
48 * \file select.c */
Harald Welteba6988b2011-08-17 12:46:48 +020049
Harald Welteec8b4502010-02-20 20:34:29 +010050static int maxfd = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020051static LLIST_HEAD(osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +010052static int unregistered_count;
53
Harald Welte6c0a0e62017-08-12 11:43:14 +020054/*! Set up an osmo-fd. Will not register it.
55 * \param[inout] ofd Osmo FD to be set-up
56 * \param[in] fd OS-level file descriptor number
57 * \param[in] when bit-mask of BSC_FD_{READ,WRITE,EXECEPT}
58 * \param[in] cb Call-back function to be called
59 * \param[in] data Private context pointer
60 * \param[in] priv_nr Private number
61 */
62void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
63 int (*cb)(struct osmo_fd *fd, unsigned int what),
64 void *data, unsigned int priv_nr)
65{
66 ofd->fd = fd;
67 ofd->when = when;
68 ofd->cb = cb;
69 ofd->data = data;
70 ofd->priv_nr = priv_nr;
71}
Philipp Maierb2888532016-12-09 14:07:18 +010072
Neels Hofmeyr87e45502017-06-20 00:17:59 +020073/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010074 * \param[in] fd osmocom file descriptor to be checked
75 * \returns true if registered; otherwise false
76 */
77bool osmo_fd_is_registered(struct osmo_fd *fd)
78{
79 struct osmo_fd *entry;
80 llist_for_each_entry(entry, &osmo_fds, list) {
81 if (entry == fd) {
82 return true;
83 }
84 }
85
86 return false;
87}
88
Neels Hofmeyr87e45502017-06-20 00:17:59 +020089/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020090 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020091 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020092 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020093int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010094{
95 int flags;
96
97 /* make FD nonblocking */
98 flags = fcntl(fd->fd, F_GETFL);
99 if (flags < 0)
100 return flags;
101 flags |= O_NONBLOCK;
102 flags = fcntl(fd->fd, F_SETFL, flags);
103 if (flags < 0)
104 return flags;
105
Harald Welte32e1f232011-06-26 13:07:18 +0200106 /* set close-on-exec flag */
107 flags = fcntl(fd->fd, F_GETFD);
108 if (flags < 0)
109 return flags;
110 flags |= FD_CLOEXEC;
111 flags = fcntl(fd->fd, F_SETFD, flags);
112 if (flags < 0)
113 return flags;
114
Harald Welteec8b4502010-02-20 20:34:29 +0100115 /* Register FD */
116 if (fd->fd > maxfd)
117 maxfd = fd->fd;
118
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800119#ifdef BSC_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +0100120 if (osmo_fd_is_registered(fd)) {
121 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
122 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800123 }
124#endif
125
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200126 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100127
128 return 0;
129}
130
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200131/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200132 * \param[in] fd osmocom file descriptor to be unregistered
133 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200134void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100135{
Philipp Maierb2888532016-12-09 14:07:18 +0100136 /* Note: when fd is inside the osmo_fds list (not registered before)
137 * this function will crash! If in doubt, check file descriptor with
138 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100139 unregistered_count++;
140 llist_del(&fd->list);
141}
142
Harald Welteea91a512017-07-13 14:28:30 +0200143/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
144 * \param[in] fd osmocom file descriptor to be unregistered + closed
145 *
146 * If \a fd is registered, we unregister it from the select() loop
147 * abstraction. We then close the fd and set it to -1, as well as
148 * unsetting any 'when' flags */
149void osmo_fd_close(struct osmo_fd *fd)
150{
151 if (osmo_fd_is_registered(fd))
152 osmo_fd_unregister(fd);
153 if (fd->fd != -1)
154 close(fd->fd);
155 fd->fd = -1;
156 fd->when = 0;
157}
158
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200159/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100160 * \param[in] _rset The readfds to populate
161 * \param[in] _wset The wrtiefds to populate
162 * \param[in] _eset The errorfds to populate
163 *
164 * \returns The highest file descriptor seen or 0 on an empty list
165 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100166inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100167{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100168 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
169 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100170 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100171
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200172 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100173 if (ufd->when & BSC_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100174 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100175
176 if (ufd->when & BSC_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100177 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100178
179 if (ufd->when & BSC_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100180 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100181
182 if (ufd->fd > highfd)
183 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100184 }
185
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100186 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100187}
Harald Welteec8b4502010-02-20 20:34:29 +0100188
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100189inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
190{
191 struct osmo_fd *ufd, *tmp;
192 int work = 0;
193 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100194
Harald Welteec8b4502010-02-20 20:34:29 +0100195restart:
196 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200197 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100198 int flags = 0;
199
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100200 if (FD_ISSET(ufd->fd, readset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100201 flags |= BSC_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100202 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100203 }
204
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100205 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100206 flags |= BSC_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100207 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100208 }
209
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100210 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100211 flags |= BSC_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100212 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100213 }
214
215 if (flags) {
216 work = 1;
217 ufd->cb(ufd, flags);
218 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200219 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100220 * unregistered, they might have been consecutive and
221 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200222 /* this seems to happen with the last element of the list as well */
223 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100224 goto restart;
225 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100226
Harald Welteec8b4502010-02-20 20:34:29 +0100227 return work;
228}
229
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200230/*! select main loop integration
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100231 * \param[in] polling should we pollonly (1) or block on select (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200232 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100233 */
234int osmo_select_main(int polling)
235{
236 fd_set readset, writeset, exceptset;
237 int rc;
238 struct timeval no_time = {0, 0};
239
240 FD_ZERO(&readset);
241 FD_ZERO(&writeset);
242 FD_ZERO(&exceptset);
243
244 /* prepare read and write fdsets */
245 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
246
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100247 if (!polling)
248 osmo_timers_prepare();
249 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
250 if (rc < 0)
251 return 0;
252
253 /* fire timers */
254 osmo_timers_update();
255
256 /* call registered callback functions */
257 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
258}
259
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200260/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200261 * \param[in] fd file descriptor to use as search key
262 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100263struct osmo_fd *osmo_fd_get_by_fd(int fd)
264{
265 struct osmo_fd *ofd;
266
267 llist_for_each_entry(ofd, &osmo_fds, list) {
268 if (ofd->fd == fd)
269 return ofd;
270 }
271 return NULL;
272}
273
Harald Welteea4d8932017-12-03 16:13:39 +0100274#ifdef HAVE_SYS_TIMERFD_H
275#include <sys/timerfd.h>
276
277/*! disable the osmocom-wrapped timerfd */
278int osmo_timerfd_disable(struct osmo_fd *ofd)
279{
280 const struct itimerspec its_null = {
281 .it_value = { 0, 0 },
282 .it_interval = { 0, 0 },
283 };
284 return timerfd_settime(ofd->fd, 0, &its_null, NULL);
285}
286
287/*! schedule the osmcoom-wrapped timerfd to occur first at \a first, then periodically at \a interval
288 * \param[in] ofd Osmocom wrapped timerfd
289 * \param[in] first Relative time at which the timer should first execute (NULL = \a interval)
290 * \param[in] interval Time interval at which subsequent timer shall fire
291 * \returns 0 on success; negative on error */
292int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
293 const struct timespec *interval)
294{
295 struct itimerspec its;
296
297 if (ofd->fd < 0)
298 return -EINVAL;
299
300 /* first expiration */
301 if (first)
302 its.it_value = *first;
303 else
304 its.it_value = *interval;
305 /* repeating interval */
306 its.it_interval = *interval;
307
308 return timerfd_settime(ofd->fd, 0, &its, NULL);
309}
310
311/*! setup osmocom-wrapped timerfd
312 * \param[inout] ofd Osmocom-wrapped timerfd on which to operate
313 * \param[in] cb Call-back function called when timerfd becomes readable
314 * \param[in] data Opaque data to be passed on to call-back
315 * \returns 0 on success; negative on error
316 *
317 * We simply initialize the data structures here, but do not yet
318 * schedule the timer.
319 */
320int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data)
321{
322 ofd->cb = cb;
323 ofd->data = data;
324 ofd->when = BSC_FD_READ;
325
326 if (ofd->fd < 0) {
Harald Welte37608f92018-10-21 12:32:55 +0200327 int rc;
328
Harald Welteea4d8932017-12-03 16:13:39 +0100329 ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
330 if (ofd->fd < 0)
331 return ofd->fd;
332
Harald Welte37608f92018-10-21 12:32:55 +0200333 rc = osmo_fd_register(ofd);
334 if (rc < 0) {
335 close(ofd->fd);
336 ofd->fd = -1;
337 return rc;
338 }
Harald Welteea4d8932017-12-03 16:13:39 +0100339 }
340 return 0;
341}
342
343#endif /* HAVE_SYS_TIMERFD_H */
344
345
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200346/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200347
Harald Welteec8b4502010-02-20 20:34:29 +0100348#endif /* _HAVE_SYS_SELECT_H */