blob: 192cae2f3221823aff359d253ab5a54de84ef982 [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>
26#include <sys/select.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
35
Harald Welteba6988b2011-08-17 12:46:48 +020036/*! \addtogroup select
37 * @{
38 */
39
40/*! \file select.c
41 * \brief select loop abstraction
42 */
43
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
Harald Welteba6988b2011-08-17 12:46:48 +020048/*! \brief Register a new file descriptor with select loop abstraction
49 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020050 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020051 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020052int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010053{
54 int flags;
55
56 /* make FD nonblocking */
57 flags = fcntl(fd->fd, F_GETFL);
58 if (flags < 0)
59 return flags;
60 flags |= O_NONBLOCK;
61 flags = fcntl(fd->fd, F_SETFL, flags);
62 if (flags < 0)
63 return flags;
64
Harald Welte32e1f232011-06-26 13:07:18 +020065 /* set close-on-exec flag */
66 flags = fcntl(fd->fd, F_GETFD);
67 if (flags < 0)
68 return flags;
69 flags |= FD_CLOEXEC;
70 flags = fcntl(fd->fd, F_SETFD, flags);
71 if (flags < 0)
72 return flags;
73
Harald Welteec8b4502010-02-20 20:34:29 +010074 /* Register FD */
75 if (fd->fd > maxfd)
76 maxfd = fd->fd;
77
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080078#ifdef BSC_FD_CHECK
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020079 struct osmo_fd *entry;
80 llist_for_each_entry(entry, &osmo_fds, list) {
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080081 if (entry == fd) {
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020082 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080083 return 0;
84 }
85 }
86#endif
87
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020088 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +010089
90 return 0;
91}
92
Harald Welteba6988b2011-08-17 12:46:48 +020093/*! \brief Unregister a file descriptor from select loop abstraction
94 * \param[in] fd osmocom file descriptor to be unregistered
95 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020096void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010097{
98 unregistered_count++;
99 llist_del(&fd->list);
100}
101
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100102inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100103{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100104 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
105 struct osmo_fd *ufd;
Harald Welteec8b4502010-02-20 20:34:29 +0100106
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200107 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100108 if (ufd->when & BSC_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100109 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100110
111 if (ufd->when & BSC_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100112 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100113
114 if (ufd->when & BSC_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100115 FD_SET(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100116 }
117
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100118 return maxfd;
119}
Harald Welteec8b4502010-02-20 20:34:29 +0100120
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100121inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
122{
123 struct osmo_fd *ufd, *tmp;
124 int work = 0;
125 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100126
Harald Welteec8b4502010-02-20 20:34:29 +0100127restart:
128 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200129 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100130 int flags = 0;
131
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100132 if (FD_ISSET(ufd->fd, readset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100133 flags |= BSC_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100134 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100135 }
136
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100137 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100138 flags |= BSC_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100139 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100140 }
141
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100142 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100143 flags |= BSC_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100144 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100145 }
146
147 if (flags) {
148 work = 1;
149 ufd->cb(ufd, flags);
150 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200151 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100152 * unregistered, they might have been consecutive and
153 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200154 /* this seems to happen with the last element of the list as well */
155 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100156 goto restart;
157 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100158
Harald Welteec8b4502010-02-20 20:34:29 +0100159 return work;
160}
161
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100162/*! \brief select main loop integration
163 * \param[in] polling should we pollonly (1) or block on select (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200164 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100165 */
166int osmo_select_main(int polling)
167{
168 fd_set readset, writeset, exceptset;
169 int rc;
170 struct timeval no_time = {0, 0};
171
172 FD_ZERO(&readset);
173 FD_ZERO(&writeset);
174 FD_ZERO(&exceptset);
175
176 /* prepare read and write fdsets */
177 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
178
179 osmo_timers_check();
180
181 if (!polling)
182 osmo_timers_prepare();
183 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
184 if (rc < 0)
185 return 0;
186
187 /* fire timers */
188 osmo_timers_update();
189
190 /* call registered callback functions */
191 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
192}
193
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200194/*! \brief find an osmo_fd based on the integer fd
195 * \param[in] fd file descriptor to use as search key
196 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100197struct osmo_fd *osmo_fd_get_by_fd(int fd)
198{
199 struct osmo_fd *ofd;
200
201 llist_for_each_entry(ofd, &osmo_fds, list) {
202 if (ofd->fd == fd)
203 return ofd;
204 }
205 return NULL;
206}
207
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200208/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200209
Harald Welteec8b4502010-02-20 20:34:29 +0100210#endif /* _HAVE_SYS_SELECT_H */