blob: 1c62e011e4c1e6d2d1119bb16a2d7948e5ead0e7 [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>
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080033
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010034#include <osmocom/core/select.h>
35#include <osmocom/core/linuxlist.h>
36#include <osmocom/core/timer.h>
Harald Welteec8b4502010-02-20 20:34:29 +010037
Harald Welte54844802010-02-20 22:23:08 +010038#include "../config.h"
39
Harald Welteec8b4502010-02-20 20:34:29 +010040#ifdef HAVE_SYS_SELECT_H
Harald Welte1d604d82017-05-17 15:32:35 +010041#include <sys/select.h>
Harald Welteec8b4502010-02-20 20:34:29 +010042
Harald Welteba6988b2011-08-17 12:46:48 +020043/*! \addtogroup select
44 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020045 * select() loop abstraction
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020046 *
47 * \file select.c */
Harald Welteba6988b2011-08-17 12:46:48 +020048
Harald Welteec8b4502010-02-20 20:34:29 +010049static int maxfd = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020050static LLIST_HEAD(osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +010051static int unregistered_count;
52
Harald Welte6c0a0e62017-08-12 11:43:14 +020053/*! Set up an osmo-fd. Will not register it.
54 * \param[inout] ofd Osmo FD to be set-up
55 * \param[in] fd OS-level file descriptor number
56 * \param[in] when bit-mask of BSC_FD_{READ,WRITE,EXECEPT}
57 * \param[in] cb Call-back function to be called
58 * \param[in] data Private context pointer
59 * \param[in] priv_nr Private number
60 */
61void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
62 int (*cb)(struct osmo_fd *fd, unsigned int what),
63 void *data, unsigned int priv_nr)
64{
65 ofd->fd = fd;
66 ofd->when = when;
67 ofd->cb = cb;
68 ofd->data = data;
69 ofd->priv_nr = priv_nr;
70}
Philipp Maierb2888532016-12-09 14:07:18 +010071
Neels Hofmeyr87e45502017-06-20 00:17:59 +020072/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010073 * \param[in] fd osmocom file descriptor to be checked
74 * \returns true if registered; otherwise false
75 */
76bool osmo_fd_is_registered(struct osmo_fd *fd)
77{
78 struct osmo_fd *entry;
79 llist_for_each_entry(entry, &osmo_fds, list) {
80 if (entry == fd) {
81 return true;
82 }
83 }
84
85 return false;
86}
87
Neels Hofmeyr87e45502017-06-20 00:17:59 +020088/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020089 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020090 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020091 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020092int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010093{
94 int flags;
95
96 /* make FD nonblocking */
97 flags = fcntl(fd->fd, F_GETFL);
98 if (flags < 0)
99 return flags;
100 flags |= O_NONBLOCK;
101 flags = fcntl(fd->fd, F_SETFL, flags);
102 if (flags < 0)
103 return flags;
104
Harald Welte32e1f232011-06-26 13:07:18 +0200105 /* set close-on-exec flag */
106 flags = fcntl(fd->fd, F_GETFD);
107 if (flags < 0)
108 return flags;
109 flags |= FD_CLOEXEC;
110 flags = fcntl(fd->fd, F_SETFD, flags);
111 if (flags < 0)
112 return flags;
113
Harald Welteec8b4502010-02-20 20:34:29 +0100114 /* Register FD */
115 if (fd->fd > maxfd)
116 maxfd = fd->fd;
117
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800118#ifdef BSC_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +0100119 if (osmo_fd_is_registered(fd)) {
120 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
121 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800122 }
123#endif
124
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200125 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100126
127 return 0;
128}
129
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200130/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200131 * \param[in] fd osmocom file descriptor to be unregistered
132 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200133void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100134{
Philipp Maierb2888532016-12-09 14:07:18 +0100135 /* Note: when fd is inside the osmo_fds list (not registered before)
136 * this function will crash! If in doubt, check file descriptor with
137 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100138 unregistered_count++;
139 llist_del(&fd->list);
140}
141
Harald Welteea91a512017-07-13 14:28:30 +0200142/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
143 * \param[in] fd osmocom file descriptor to be unregistered + closed
144 *
145 * If \a fd is registered, we unregister it from the select() loop
146 * abstraction. We then close the fd and set it to -1, as well as
147 * unsetting any 'when' flags */
148void osmo_fd_close(struct osmo_fd *fd)
149{
150 if (osmo_fd_is_registered(fd))
151 osmo_fd_unregister(fd);
152 if (fd->fd != -1)
153 close(fd->fd);
154 fd->fd = -1;
155 fd->when = 0;
156}
157
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200158/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100159 * \param[in] _rset The readfds to populate
160 * \param[in] _wset The wrtiefds to populate
161 * \param[in] _eset The errorfds to populate
162 *
163 * \returns The highest file descriptor seen or 0 on an empty list
164 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100165inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100166{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100167 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
168 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100169 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100170
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200171 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100172 if (ufd->when & BSC_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100173 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100174
175 if (ufd->when & BSC_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100176 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100177
178 if (ufd->when & BSC_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100179 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100180
181 if (ufd->fd > highfd)
182 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100183 }
184
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100185 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100186}
Harald Welteec8b4502010-02-20 20:34:29 +0100187
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100188inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
189{
190 struct osmo_fd *ufd, *tmp;
191 int work = 0;
192 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100193
Harald Welteec8b4502010-02-20 20:34:29 +0100194restart:
195 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200196 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100197 int flags = 0;
198
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100199 if (FD_ISSET(ufd->fd, readset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100200 flags |= BSC_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100201 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100202 }
203
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100204 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100205 flags |= BSC_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100206 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100207 }
208
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100209 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100210 flags |= BSC_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100211 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100212 }
213
214 if (flags) {
215 work = 1;
216 ufd->cb(ufd, flags);
217 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200218 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100219 * unregistered, they might have been consecutive and
220 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200221 /* this seems to happen with the last element of the list as well */
222 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100223 goto restart;
224 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100225
Harald Welteec8b4502010-02-20 20:34:29 +0100226 return work;
227}
228
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200229/*! select main loop integration
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100230 * \param[in] polling should we pollonly (1) or block on select (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200231 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100232 */
233int osmo_select_main(int polling)
234{
235 fd_set readset, writeset, exceptset;
236 int rc;
237 struct timeval no_time = {0, 0};
238
239 FD_ZERO(&readset);
240 FD_ZERO(&writeset);
241 FD_ZERO(&exceptset);
242
243 /* prepare read and write fdsets */
244 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
245
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100246 if (!polling)
247 osmo_timers_prepare();
248 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
249 if (rc < 0)
250 return 0;
251
252 /* fire timers */
253 osmo_timers_update();
254
255 /* call registered callback functions */
256 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
257}
258
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200259/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200260 * \param[in] fd file descriptor to use as search key
261 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100262struct osmo_fd *osmo_fd_get_by_fd(int fd)
263{
264 struct osmo_fd *ofd;
265
266 llist_for_each_entry(ofd, &osmo_fds, list) {
267 if (ofd->fd == fd)
268 return ofd;
269 }
270 return NULL;
271}
272
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200273/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200274
Harald Welteec8b4502010-02-20 20:34:29 +0100275#endif /* _HAVE_SYS_SELECT_H */