blob: 575fd777188ff3c44320b1dfe2ccfb5cb5e5a388 [file] [log] [blame]
Harald Welteec8b4502010-02-20 20:34:29 +01001/* select filedescriptor handling, taken from:
2 * userspace logging daemon for the iptables ULOG target
3 * of the linux 2.4 netfilter subsystem.
4 *
5 * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
Harald Welte9d92f0e2010-10-31 13:56:45 +01008 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
Harald Welteec8b4502010-02-20 20:34:29 +010011 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
Harald Welteec8b4502010-02-20 20:34:29 +010021 */
22
23#include <fcntl.h>
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080024#include <stdio.h>
Harald Welte9e166e82014-03-10 17:28:33 +010025#include <string.h>
Philipp Maierb2888532016-12-09 14:07:18 +010026#include <stdbool.h>
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080027
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010028#include <osmocom/core/select.h>
29#include <osmocom/core/linuxlist.h>
30#include <osmocom/core/timer.h>
Harald Welteec8b4502010-02-20 20:34:29 +010031
Harald Welte54844802010-02-20 22:23:08 +010032#include "../config.h"
33
Harald Welteec8b4502010-02-20 20:34:29 +010034#ifdef HAVE_SYS_SELECT_H
Harald Welte1d604d82017-05-17 15:32:35 +010035#include <sys/select.h>
Harald Welteec8b4502010-02-20 20:34:29 +010036
Harald Welteba6988b2011-08-17 12:46:48 +020037/*! \addtogroup select
38 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020039 * select() loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020040 */
41
Harald Welte96e2a002017-06-12 21:44:18 +020042/*! \file select.c */
Harald Welteba6988b2011-08-17 12:46:48 +020043
Harald Welteec8b4502010-02-20 20:34:29 +010044static int maxfd = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020045static LLIST_HEAD(osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +010046static int unregistered_count;
47
Philipp Maierb2888532016-12-09 14:07:18 +010048
Neels Hofmeyr87e45502017-06-20 00:17:59 +020049/*! Check if a file descriptor is already registered
Philipp Maierb2888532016-12-09 14:07:18 +010050 * \param[in] fd osmocom file descriptor to be checked
51 * \returns true if registered; otherwise false
52 */
53bool osmo_fd_is_registered(struct osmo_fd *fd)
54{
55 struct osmo_fd *entry;
56 llist_for_each_entry(entry, &osmo_fds, list) {
57 if (entry == fd) {
58 return true;
59 }
60 }
61
62 return false;
63}
64
Neels Hofmeyr87e45502017-06-20 00:17:59 +020065/*! Register a new file descriptor with select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +020066 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020067 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020068 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020069int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010070{
71 int flags;
72
73 /* make FD nonblocking */
74 flags = fcntl(fd->fd, F_GETFL);
75 if (flags < 0)
76 return flags;
77 flags |= O_NONBLOCK;
78 flags = fcntl(fd->fd, F_SETFL, flags);
79 if (flags < 0)
80 return flags;
81
Harald Welte32e1f232011-06-26 13:07:18 +020082 /* set close-on-exec flag */
83 flags = fcntl(fd->fd, F_GETFD);
84 if (flags < 0)
85 return flags;
86 flags |= FD_CLOEXEC;
87 flags = fcntl(fd->fd, F_SETFD, flags);
88 if (flags < 0)
89 return flags;
90
Harald Welteec8b4502010-02-20 20:34:29 +010091 /* Register FD */
92 if (fd->fd > maxfd)
93 maxfd = fd->fd;
94
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080095#ifdef BSC_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +010096 if (osmo_fd_is_registered(fd)) {
97 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
98 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080099 }
100#endif
101
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200102 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100103
104 return 0;
105}
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107/*! Unregister a file descriptor from select loop abstraction
Harald Welteba6988b2011-08-17 12:46:48 +0200108 * \param[in] fd osmocom file descriptor to be unregistered
109 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200110void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100111{
Philipp Maierb2888532016-12-09 14:07:18 +0100112 /* Note: when fd is inside the osmo_fds list (not registered before)
113 * this function will crash! If in doubt, check file descriptor with
114 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100115 unregistered_count++;
116 llist_del(&fd->list);
117}
118
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200119/*! Populate the fd_sets and return the highest fd number
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100120 * \param[in] _rset The readfds to populate
121 * \param[in] _wset The wrtiefds to populate
122 * \param[in] _eset The errorfds to populate
123 *
124 * \returns The highest file descriptor seen or 0 on an empty list
125 */
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100126inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100127{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100128 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
129 struct osmo_fd *ufd;
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100130 int highfd = 0;
Harald Welteec8b4502010-02-20 20:34:29 +0100131
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200132 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100133 if (ufd->when & BSC_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100134 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100135
136 if (ufd->when & BSC_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100137 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100138
139 if (ufd->when & BSC_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100140 FD_SET(ufd->fd, exceptset);
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100141
142 if (ufd->fd > highfd)
143 highfd = ufd->fd;
Harald Welteec8b4502010-02-20 20:34:29 +0100144 }
145
Holger Hans Peter Freytherff206412017-03-07 14:28:38 +0100146 return highfd;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100147}
Harald Welteec8b4502010-02-20 20:34:29 +0100148
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100149inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
150{
151 struct osmo_fd *ufd, *tmp;
152 int work = 0;
153 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100154
Harald Welteec8b4502010-02-20 20:34:29 +0100155restart:
156 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200157 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100158 int flags = 0;
159
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100160 if (FD_ISSET(ufd->fd, readset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100161 flags |= BSC_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100162 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100163 }
164
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100165 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100166 flags |= BSC_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100167 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100168 }
169
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100170 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100171 flags |= BSC_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100172 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100173 }
174
175 if (flags) {
176 work = 1;
177 ufd->cb(ufd, flags);
178 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200179 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100180 * unregistered, they might have been consecutive and
181 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200182 /* this seems to happen with the last element of the list as well */
183 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100184 goto restart;
185 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100186
Harald Welteec8b4502010-02-20 20:34:29 +0100187 return work;
188}
189
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200190/*! select main loop integration
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100191 * \param[in] polling should we pollonly (1) or block on select (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200192 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100193 */
194int osmo_select_main(int polling)
195{
196 fd_set readset, writeset, exceptset;
197 int rc;
198 struct timeval no_time = {0, 0};
199
200 FD_ZERO(&readset);
201 FD_ZERO(&writeset);
202 FD_ZERO(&exceptset);
203
204 /* prepare read and write fdsets */
205 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
206
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100207 if (!polling)
208 osmo_timers_prepare();
209 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
210 if (rc < 0)
211 return 0;
212
213 /* fire timers */
214 osmo_timers_update();
215
216 /* call registered callback functions */
217 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
218}
219
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200220/*! find an osmo_fd based on the integer fd
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200221 * \param[in] fd file descriptor to use as search key
222 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100223struct osmo_fd *osmo_fd_get_by_fd(int fd)
224{
225 struct osmo_fd *ofd;
226
227 llist_for_each_entry(ofd, &osmo_fds, list) {
228 if (ofd->fd == fd)
229 return ofd;
230 }
231 return NULL;
232}
233
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200234/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200235
Harald Welteec8b4502010-02-20 20:34:29 +0100236#endif /* _HAVE_SYS_SELECT_H */