blob: 4b98b6230573bc1e9b787909b31622484f1985b3 [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>
8 *
9 * This program is free software; you can redistribute it and/or modify
Harald Welte9d92f0e2010-10-31 13:56:45 +010010 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
Harald Welteec8b4502010-02-20 20:34:29 +010013 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
Harald Welteec8b4502010-02-20 20:34:29 +010023 */
24
25#include <fcntl.h>
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080026#include <stdio.h>
Harald Welteea91a512017-07-13 14:28:30 +020027#include <unistd.h>
Harald Welte9e166e82014-03-10 17:28:33 +010028#include <string.h>
Philipp Maierb2888532016-12-09 14:07:18 +010029#include <stdbool.h>
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080030
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010031#include <osmocom/core/select.h>
32#include <osmocom/core/linuxlist.h>
33#include <osmocom/core/timer.h>
Harald Welteec8b4502010-02-20 20:34:29 +010034
Harald Welte54844802010-02-20 22:23:08 +010035#include "../config.h"
36
Harald Welteec8b4502010-02-20 20:34:29 +010037#ifdef HAVE_SYS_SELECT_H
Harald Welte1d604d82017-05-17 15:32:35 +010038#include <sys/select.h>
Harald Welteec8b4502010-02-20 20:34:29 +010039
Harald Welteba6988b2011-08-17 12:46:48 +020040/*! \addtogroup select
41 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020042 * select() loop abstraction
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020043 *
44 * \file select.c */
Harald Welteba6988b2011-08-17 12:46:48 +020045
Harald Welteec8b4502010-02-20 20:34:29 +010046static int maxfd = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020047static LLIST_HEAD(osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +010048static int unregistered_count;
49
Harald Welte6c0a0e62017-08-12 11:43:14 +020050/*! Set up an osmo-fd. Will not register it.
51 * \param[inout] ofd Osmo FD to be set-up
52 * \param[in] fd OS-level file descriptor number
53 * \param[in] when bit-mask of BSC_FD_{READ,WRITE,EXECEPT}
54 * \param[in] cb Call-back function to be called
55 * \param[in] data Private context pointer
56 * \param[in] priv_nr Private number
57 */
58void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
59 int (*cb)(struct osmo_fd *fd, unsigned int what),
60 void *data, unsigned int priv_nr)
61{
62 ofd->fd = fd;
63 ofd->when = when;
64 ofd->cb = cb;
65 ofd->data = data;
66 ofd->priv_nr = priv_nr;
67}
Philipp Maierb2888532016-12-09 14:07:18 +010068
Neels Hofmeyr87e45502017-06-20 00:17:59 +020069/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010070 * \param[in] fd osmocom file descriptor to be checked
71 * \returns true if registered; otherwise false
72 */
73bool osmo_fd_is_registered(struct osmo_fd *fd)
74{
75 struct osmo_fd *entry;
76 llist_for_each_entry(entry, &osmo_fds, list) {
77 if (entry == fd) {
78 return true;
79 }
80 }
81
82 return false;
83}
84
Neels Hofmeyr87e45502017-06-20 00:17:59 +020085/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020086 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020087 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020088 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020089int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010090{
91 int flags;
92
93 /* make FD nonblocking */
94 flags = fcntl(fd->fd, F_GETFL);
95 if (flags < 0)
96 return flags;
97 flags |= O_NONBLOCK;
98 flags = fcntl(fd->fd, F_SETFL, flags);
99 if (flags < 0)
100 return flags;
101
Harald Welte32e1f232011-06-26 13:07:18 +0200102 /* set close-on-exec flag */
103 flags = fcntl(fd->fd, F_GETFD);
104 if (flags < 0)
105 return flags;
106 flags |= FD_CLOEXEC;
107 flags = fcntl(fd->fd, F_SETFD, flags);
108 if (flags < 0)
109 return flags;
110
Harald Welteec8b4502010-02-20 20:34:29 +0100111 /* Register FD */
112 if (fd->fd > maxfd)
113 maxfd = fd->fd;
114
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800115#ifdef BSC_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +0100116 if (osmo_fd_is_registered(fd)) {
117 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
118 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800119 }
120#endif
121
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200122 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100123
124 return 0;
125}
126
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200127/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200128 * \param[in] fd osmocom file descriptor to be unregistered
129 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200130void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100131{
Philipp Maierb2888532016-12-09 14:07:18 +0100132 /* Note: when fd is inside the osmo_fds list (not registered before)
133 * this function will crash! If in doubt, check file descriptor with
134 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100135 unregistered_count++;
136 llist_del(&fd->list);
137}
138
Harald Welteea91a512017-07-13 14:28:30 +0200139/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
140 * \param[in] fd osmocom file descriptor to be unregistered + closed
141 *
142 * If \a fd is registered, we unregister it from the select() loop
143 * abstraction. We then close the fd and set it to -1, as well as
144 * unsetting any 'when' flags */
145void osmo_fd_close(struct osmo_fd *fd)
146{
147 if (osmo_fd_is_registered(fd))
148 osmo_fd_unregister(fd);
149 if (fd->fd != -1)
150 close(fd->fd);
151 fd->fd = -1;
152 fd->when = 0;
153}
154
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200155/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100156 * \param[in] _rset The readfds to populate
157 * \param[in] _wset The wrtiefds to populate
158 * \param[in] _eset The errorfds to populate
159 *
160 * \returns The highest file descriptor seen or 0 on an empty list
161 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100162inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100163{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100164 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
165 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100166 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100167
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200168 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100169 if (ufd->when & BSC_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100170 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100171
172 if (ufd->when & BSC_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100173 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100174
175 if (ufd->when & BSC_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100176 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100177
178 if (ufd->fd > highfd)
179 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100180 }
181
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100182 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100183}
Harald Welteec8b4502010-02-20 20:34:29 +0100184
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100185inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
186{
187 struct osmo_fd *ufd, *tmp;
188 int work = 0;
189 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100190
Harald Welteec8b4502010-02-20 20:34:29 +0100191restart:
192 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200193 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100194 int flags = 0;
195
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100196 if (FD_ISSET(ufd->fd, readset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100197 flags |= BSC_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100198 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100199 }
200
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100201 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100202 flags |= BSC_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100203 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100204 }
205
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100206 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100207 flags |= BSC_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100208 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100209 }
210
211 if (flags) {
212 work = 1;
213 ufd->cb(ufd, flags);
214 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200215 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100216 * unregistered, they might have been consecutive and
217 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200218 /* this seems to happen with the last element of the list as well */
219 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100220 goto restart;
221 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100222
Harald Welteec8b4502010-02-20 20:34:29 +0100223 return work;
224}
225
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200226/*! select main loop integration
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100227 * \param[in] polling should we pollonly (1) or block on select (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200228 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100229 */
230int osmo_select_main(int polling)
231{
232 fd_set readset, writeset, exceptset;
233 int rc;
234 struct timeval no_time = {0, 0};
235
236 FD_ZERO(&readset);
237 FD_ZERO(&writeset);
238 FD_ZERO(&exceptset);
239
240 /* prepare read and write fdsets */
241 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
242
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100243 if (!polling)
244 osmo_timers_prepare();
245 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
246 if (rc < 0)
247 return 0;
248
249 /* fire timers */
250 osmo_timers_update();
251
252 /* call registered callback functions */
253 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
254}
255
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200256/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200257 * \param[in] fd file descriptor to use as search key
258 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100259struct osmo_fd *osmo_fd_get_by_fd(int fd)
260{
261 struct osmo_fd *ofd;
262
263 llist_for_each_entry(ofd, &osmo_fds, list) {
264 if (ofd->fd == fd)
265 return ofd;
266 }
267 return NULL;
268}
269
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200270/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200271
Harald Welteec8b4502010-02-20 20:34:29 +0100272#endif /* _HAVE_SYS_SELECT_H */