blob: 0ba8bc60a96ee3b29e3ec0f7ee4c9c0690e871ae [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
Philipp Maierb2888532016-12-09 14:07:18 +010050
Neels Hofmeyr87e45502017-06-20 00:17:59 +020051/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010052 * \param[in] fd osmocom file descriptor to be checked
53 * \returns true if registered; otherwise false
54 */
55bool osmo_fd_is_registered(struct osmo_fd *fd)
56{
57 struct osmo_fd *entry;
58 llist_for_each_entry(entry, &osmo_fds, list) {
59 if (entry == fd) {
60 return true;
61 }
62 }
63
64 return false;
65}
66
Neels Hofmeyr87e45502017-06-20 00:17:59 +020067/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020068 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020069 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020070 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020071int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010072{
73 int flags;
74
75 /* make FD nonblocking */
76 flags = fcntl(fd->fd, F_GETFL);
77 if (flags < 0)
78 return flags;
79 flags |= O_NONBLOCK;
80 flags = fcntl(fd->fd, F_SETFL, flags);
81 if (flags < 0)
82 return flags;
83
Harald Welte32e1f232011-06-26 13:07:18 +020084 /* set close-on-exec flag */
85 flags = fcntl(fd->fd, F_GETFD);
86 if (flags < 0)
87 return flags;
88 flags |= FD_CLOEXEC;
89 flags = fcntl(fd->fd, F_SETFD, flags);
90 if (flags < 0)
91 return flags;
92
Harald Welteec8b4502010-02-20 20:34:29 +010093 /* Register FD */
94 if (fd->fd > maxfd)
95 maxfd = fd->fd;
96
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080097#ifdef BSC_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +010098 if (osmo_fd_is_registered(fd)) {
99 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
100 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800101 }
102#endif
103
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200104 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100105
106 return 0;
107}
108
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200109/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200110 * \param[in] fd osmocom file descriptor to be unregistered
111 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200112void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100113{
Philipp Maierb2888532016-12-09 14:07:18 +0100114 /* Note: when fd is inside the osmo_fds list (not registered before)
115 * this function will crash! If in doubt, check file descriptor with
116 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100117 unregistered_count++;
118 llist_del(&fd->list);
119}
120
Harald Welteea91a512017-07-13 14:28:30 +0200121/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
122 * \param[in] fd osmocom file descriptor to be unregistered + closed
123 *
124 * If \a fd is registered, we unregister it from the select() loop
125 * abstraction. We then close the fd and set it to -1, as well as
126 * unsetting any 'when' flags */
127void osmo_fd_close(struct osmo_fd *fd)
128{
129 if (osmo_fd_is_registered(fd))
130 osmo_fd_unregister(fd);
131 if (fd->fd != -1)
132 close(fd->fd);
133 fd->fd = -1;
134 fd->when = 0;
135}
136
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200137/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100138 * \param[in] _rset The readfds to populate
139 * \param[in] _wset The wrtiefds to populate
140 * \param[in] _eset The errorfds to populate
141 *
142 * \returns The highest file descriptor seen or 0 on an empty list
143 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100144inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100145{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100146 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
147 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100148 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100149
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200150 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100151 if (ufd->when & BSC_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100152 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100153
154 if (ufd->when & BSC_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100155 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100156
157 if (ufd->when & BSC_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100158 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100159
160 if (ufd->fd > highfd)
161 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100162 }
163
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100164 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100165}
Harald Welteec8b4502010-02-20 20:34:29 +0100166
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100167inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
168{
169 struct osmo_fd *ufd, *tmp;
170 int work = 0;
171 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100172
Harald Welteec8b4502010-02-20 20:34:29 +0100173restart:
174 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200175 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100176 int flags = 0;
177
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100178 if (FD_ISSET(ufd->fd, readset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100179 flags |= BSC_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100180 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100181 }
182
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100183 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100184 flags |= BSC_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100185 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100186 }
187
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100188 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100189 flags |= BSC_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100190 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100191 }
192
193 if (flags) {
194 work = 1;
195 ufd->cb(ufd, flags);
196 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200197 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100198 * unregistered, they might have been consecutive and
199 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200200 /* this seems to happen with the last element of the list as well */
201 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100202 goto restart;
203 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100204
Harald Welteec8b4502010-02-20 20:34:29 +0100205 return work;
206}
207
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200208/*! select main loop integration
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100209 * \param[in] polling should we pollonly (1) or block on select (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200210 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100211 */
212int osmo_select_main(int polling)
213{
214 fd_set readset, writeset, exceptset;
215 int rc;
216 struct timeval no_time = {0, 0};
217
218 FD_ZERO(&readset);
219 FD_ZERO(&writeset);
220 FD_ZERO(&exceptset);
221
222 /* prepare read and write fdsets */
223 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
224
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100225 if (!polling)
226 osmo_timers_prepare();
227 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
228 if (rc < 0)
229 return 0;
230
231 /* fire timers */
232 osmo_timers_update();
233
234 /* call registered callback functions */
235 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
236}
237
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200238/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200239 * \param[in] fd file descriptor to use as search key
240 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100241struct osmo_fd *osmo_fd_get_by_fd(int fd)
242{
243 struct osmo_fd *ofd;
244
245 llist_for_each_entry(ofd, &osmo_fds, list) {
246 if (ofd->fd == fd)
247 return ofd;
248 }
249 return NULL;
250}
251
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200252/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200253
Harald Welteec8b4502010-02-20 20:34:29 +0100254#endif /* _HAVE_SYS_SELECT_H */