blob: ab0734eaff3f0e6d8dfc4676e9468a44111f1909 [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>
Harald Welte9e166e82014-03-10 17:28:33 +010027#include <sys/select.h>
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080028
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010029#include <osmocom/core/select.h>
30#include <osmocom/core/linuxlist.h>
31#include <osmocom/core/timer.h>
Harald Welteec8b4502010-02-20 20:34:29 +010032
Harald Welte54844802010-02-20 22:23:08 +010033#include "../config.h"
34
Harald Welteec8b4502010-02-20 20:34:29 +010035#ifdef HAVE_SYS_SELECT_H
36
Harald Welteba6988b2011-08-17 12:46:48 +020037/*! \addtogroup select
38 * @{
39 */
40
41/*! \file select.c
42 * \brief select loop abstraction
43 */
44
Harald Welteec8b4502010-02-20 20:34:29 +010045static int maxfd = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020046static LLIST_HEAD(osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +010047static int unregistered_count;
48
Philipp Maierb2888532016-12-09 14:07:18 +010049
50/*! \brief Check if a file descriptor is already registered
51 * \param[in] fd osmocom file descriptor to be checked
52 * \returns true if registered; otherwise false
53 */
54bool osmo_fd_is_registered(struct osmo_fd *fd)
55{
56 struct osmo_fd *entry;
57 llist_for_each_entry(entry, &osmo_fds, list) {
58 if (entry == fd) {
59 return true;
60 }
61 }
62
63 return false;
64}
65
Harald Welteba6988b2011-08-17 12:46:48 +020066/*! \brief Register a new file descriptor with select loop abstraction
67 * \param[in] fd osmocom file descriptor to be registered
Harald Welte2d2e2cc2016-04-25 12:11:20 +020068 * \returns 0 on success; negative in case of error
Harald Welteba6988b2011-08-17 12:46:48 +020069 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020070int osmo_fd_register(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +010071{
72 int flags;
73
74 /* make FD nonblocking */
75 flags = fcntl(fd->fd, F_GETFL);
76 if (flags < 0)
77 return flags;
78 flags |= O_NONBLOCK;
79 flags = fcntl(fd->fd, F_SETFL, flags);
80 if (flags < 0)
81 return flags;
82
Harald Welte32e1f232011-06-26 13:07:18 +020083 /* set close-on-exec flag */
84 flags = fcntl(fd->fd, F_GETFD);
85 if (flags < 0)
86 return flags;
87 flags |= FD_CLOEXEC;
88 flags = fcntl(fd->fd, F_SETFD, flags);
89 if (flags < 0)
90 return flags;
91
Harald Welteec8b4502010-02-20 20:34:29 +010092 /* Register FD */
93 if (fd->fd > maxfd)
94 maxfd = fd->fd;
95
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +080096#ifdef BSC_FD_CHECK
Philipp Maierb2888532016-12-09 14:07:18 +010097 if (osmo_fd_is_registered(fd)) {
98 fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
99 return 0;
Holger Hans Peter Freyther43558312010-08-06 06:48:43 +0800100 }
101#endif
102
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200103 llist_add_tail(&fd->list, &osmo_fds);
Harald Welteec8b4502010-02-20 20:34:29 +0100104
105 return 0;
106}
107
Harald Welteba6988b2011-08-17 12:46:48 +0200108/*! \brief Unregister a file descriptor from select loop abstraction
109 * \param[in] fd osmocom file descriptor to be unregistered
110 */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200111void osmo_fd_unregister(struct osmo_fd *fd)
Harald Welteec8b4502010-02-20 20:34:29 +0100112{
Philipp Maierb2888532016-12-09 14:07:18 +0100113 /* Note: when fd is inside the osmo_fds list (not registered before)
114 * this function will crash! If in doubt, check file descriptor with
115 * osmo_fd_is_registered() */
Harald Welteec8b4502010-02-20 20:34:29 +0100116 unregistered_count++;
117 llist_del(&fd->list);
118}
119
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100120inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
Harald Welteec8b4502010-02-20 20:34:29 +0100121{
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100122 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
123 struct osmo_fd *ufd;
Harald Welteec8b4502010-02-20 20:34:29 +0100124
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200125 llist_for_each_entry(ufd, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100126 if (ufd->when & BSC_FD_READ)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100127 FD_SET(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100128
129 if (ufd->when & BSC_FD_WRITE)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100130 FD_SET(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100131
132 if (ufd->when & BSC_FD_EXCEPT)
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100133 FD_SET(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100134 }
135
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100136 return maxfd;
137}
Harald Welteec8b4502010-02-20 20:34:29 +0100138
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100139inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
140{
141 struct osmo_fd *ufd, *tmp;
142 int work = 0;
143 fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
Harald Welteec8b4502010-02-20 20:34:29 +0100144
Harald Welteec8b4502010-02-20 20:34:29 +0100145restart:
146 unregistered_count = 0;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200147 llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
Harald Welteec8b4502010-02-20 20:34:29 +0100148 int flags = 0;
149
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100150 if (FD_ISSET(ufd->fd, readset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100151 flags |= BSC_FD_READ;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100152 FD_CLR(ufd->fd, readset);
Harald Welteec8b4502010-02-20 20:34:29 +0100153 }
154
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100155 if (FD_ISSET(ufd->fd, writeset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100156 flags |= BSC_FD_WRITE;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100157 FD_CLR(ufd->fd, writeset);
Harald Welteec8b4502010-02-20 20:34:29 +0100158 }
159
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100160 if (FD_ISSET(ufd->fd, exceptset)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100161 flags |= BSC_FD_EXCEPT;
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100162 FD_CLR(ufd->fd, exceptset);
Harald Welteec8b4502010-02-20 20:34:29 +0100163 }
164
165 if (flags) {
166 work = 1;
167 ufd->cb(ufd, flags);
168 }
Holger Hans Peter Freyther92e1e702014-04-09 17:24:00 +0200169 /* ugly, ugly hack. If more than one filedescriptor was
Harald Welteec8b4502010-02-20 20:34:29 +0100170 * unregistered, they might have been consecutive and
171 * llist_for_each_entry_safe() is no longer safe */
Holger Hans Peter Freyther23ba4742010-04-11 17:33:19 +0200172 /* this seems to happen with the last element of the list as well */
173 if (unregistered_count >= 1)
Harald Welteec8b4502010-02-20 20:34:29 +0100174 goto restart;
175 }
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100176
Harald Welteec8b4502010-02-20 20:34:29 +0100177 return work;
178}
179
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100180/*! \brief select main loop integration
181 * \param[in] polling should we pollonly (1) or block on select (0)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200182 * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100183 */
184int osmo_select_main(int polling)
185{
186 fd_set readset, writeset, exceptset;
187 int rc;
188 struct timeval no_time = {0, 0};
189
190 FD_ZERO(&readset);
191 FD_ZERO(&writeset);
192 FD_ZERO(&exceptset);
193
194 /* prepare read and write fdsets */
195 osmo_fd_fill_fds(&readset, &writeset, &exceptset);
196
Holger Hans Peter Freyther61f28882016-03-21 09:55:05 +0100197 if (!polling)
198 osmo_timers_prepare();
199 rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
200 if (rc < 0)
201 return 0;
202
203 /* fire timers */
204 osmo_timers_update();
205
206 /* call registered callback functions */
207 return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
208}
209
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200210/*! \brief find an osmo_fd based on the integer fd
211 * \param[in] fd file descriptor to use as search key
212 * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
Harald Welte6c33ae22016-03-19 21:17:58 +0100213struct osmo_fd *osmo_fd_get_by_fd(int fd)
214{
215 struct osmo_fd *ofd;
216
217 llist_for_each_entry(ofd, &osmo_fds, list) {
218 if (ofd->fd == fd)
219 return ofd;
220 }
221 return NULL;
222}
223
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200224/*! @} */
Harald Welteba6988b2011-08-17 12:46:48 +0200225
Harald Welteec8b4502010-02-20 20:34:29 +0100226#endif /* _HAVE_SYS_SELECT_H */