1 /* This file is part of GNU Radius.
2 Copyright (C) 2003,2004,2006,2007 Free Software Foundation, Inc.
3
4 Written by Sergey Poznyakoff
5
6 GNU Radius is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 GNU Radius is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Radius; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 #include <sys/types.h>
24 #include <errno.h>
25
26 #include <radiusd.h>
27
28 struct input_system {
29 fd_set fdset;
30 int fd_max;
31 grad_list_t *methods; /* List of METHOD structures */
32 grad_list_t *channels; /* List of CHANNEL structures */
33 grad_iterator_t *citr;
34 };
35
36 typedef struct input_method METHOD;
37 struct input_method {
38 const char *name;
39 int prio;
40 int (*handler)(int, void *);
41 int (*close)(int, void *);
42 int (*cmp)(const void *, const void *);
43 };
44
45 typedef struct input_channel CHANNEL;
46 struct input_channel {
47 int fd;
48 void *data;
49 METHOD *method;
50 };
51
52 INPUT *
input_create()53 input_create()
54 {
55 INPUT *p = grad_emalloc(sizeof(*p));
56 p->methods = grad_list_create();
57 p->channels = grad_list_create();
58 FD_ZERO(&p->fdset);
59 p->fd_max = -2;
60 return p;
61 }
62
63 static int
def_cmp(const void * a,const void * b)64 def_cmp(const void *a, const void *b)
65 {
66 return a != b;
67 }
68
69 void
input_register_method(INPUT * input,const char * name,int prio,int (* handler)(int,void *),int (* close)(int,void *),int (* cmp)(const void *,const void *))70 input_register_method(INPUT *input,
71 const char *name,
72 int prio,
73 int (*handler)(int, void *),
74 int (*close)(int, void *),
75 int (*cmp)(const void *, const void *))
76 {
77 METHOD *m = grad_emalloc(sizeof(*m));
78 m->name = name;
79 m->prio = prio;
80 m->handler = handler;
81 m->close = close;
82 m->cmp = cmp ? cmp : def_cmp;
83 grad_list_append(input->methods, m);
84 }
85
86 static int
_method_comp(const void * a,const void * b)87 _method_comp(const void *a, const void *b)
88 {
89 const METHOD *ma = a;
90 const char *name = b;
91 return strcmp(ma->name, name);
92 }
93
94 int
_channel_prio_comp(const void * a,const void * b)95 _channel_prio_comp(const void *a, const void *b)
96 {
97 const CHANNEL *ca = a;
98 const CHANNEL *cb = b;
99
100 return ca->method->prio - cb->method->prio;
101 }
102
103 int
input_register_channel(INPUT * input,char * name,int fd,void * data)104 input_register_channel(INPUT *input, char *name, int fd, void *data)
105 {
106 CHANNEL *c;
107 METHOD *m = grad_list_locate(input->methods, name, _method_comp);
108
109 if (!m)
110 return -1;
111
112 c = grad_emalloc(sizeof(*c));
113 c->fd = fd;
114 c->data = data;
115 c->method = m;
116 FD_SET(fd, &input->fdset);
117 if (fd > input->fd_max)
118 input->fd_max = fd;
119 grad_list_insert_sorted(input->channels, c, _channel_prio_comp);
120 return 0;
121 }
122
123 static void
channel_close(INPUT * input,CHANNEL * chan)124 channel_close(INPUT *input, CHANNEL *chan)
125 {
126 if (chan->method->close)
127 chan->method->close(chan->fd, chan->data);
128 FD_CLR(chan->fd, &input->fdset);
129 input->fd_max = -2;
130 grad_free(chan);
131 }
132
133 static int
channel_handle(CHANNEL * chan)134 channel_handle(CHANNEL *chan)
135 {
136 GRAD_DEBUG1(1, "handling method %s", chan->method->name);
137 return chan->method->handler(chan->fd, chan->data);
138 }
139
140 struct _channel_cmp_closure {
141 char *name;
142 void *data;
143 };
144
145 static int
_channel_cmp(const void * a,const void * b)146 _channel_cmp(const void *a, const void *b)
147 {
148 const CHANNEL *ca = a;
149 const struct _channel_cmp_closure *clos = b;
150
151 return strcmp(clos->name, ca->method->name)
152 || ca->method->cmp(ca->data, clos->data);
153 }
154
155 static int
_channel_cmp_fd(const void * a,const void * b)156 _channel_cmp_fd(const void *a, const void *b)
157 {
158 const CHANNEL *ca = a;
159 const int *fd = b;
160 return ca->fd != *fd;
161 }
162
163 void
input_close_channels(INPUT * input)164 input_close_channels(INPUT *input)
165 {
166 CHANNEL *p;
167
168 if (!input->citr)
169 input->citr = grad_iterator_create(input->channels);
170
171 for (p = grad_iterator_first(input->citr); p;
172 p = grad_iterator_next(input->citr)) {
173 grad_list_remove(input->channels, p, NULL);
174 channel_close(input, p);
175 }
176 grad_iterator_destroy(&input->citr);
177 }
178
179 void
input_close_channel_fd(INPUT * input,int fd)180 input_close_channel_fd(INPUT *input, int fd)
181 {
182 CHANNEL *p = grad_list_locate(input->channels, &fd, _channel_cmp_fd);
183
184 if (p) {
185 grad_list_remove(input->channels, p, NULL);
186 channel_close(input, p);
187 }
188 }
189
190 void *
input_find_channel(INPUT * input,char * name,void * data)191 input_find_channel(INPUT *input, char *name, void *data)
192 {
193 struct _channel_cmp_closure clos;
194 CHANNEL *p;
195
196 clos.name = name;
197 clos.data = data;
198 p = grad_list_locate(input->channels, &clos, _channel_cmp);
199 return p ? p->data : NULL;
200 }
201
202 void
input_close_channel_data(INPUT * input,char * name,void * data)203 input_close_channel_data(INPUT *input, char *name, void *data)
204 {
205 struct _channel_cmp_closure clos;
206 CHANNEL *p;
207
208 clos.name = name;
209 clos.data = data;
210 p = grad_list_locate(input->channels, &clos, _channel_cmp);
211 if (p) {
212 grad_list_remove(input->channels, p, NULL);
213 channel_close(input, p);
214 }
215 }
216
217 int
input_select(INPUT * input,struct timeval * tv)218 input_select(INPUT *input, struct timeval *tv)
219 {
220 CHANNEL *p;
221 int status;
222 fd_set readfds;
223
224 GRAD_DEBUG(100,"enter");
225 if (!input->citr)
226 input->citr = grad_iterator_create(input->channels);
227
228 if (input->fd_max == -2) {
229 for (p = grad_iterator_first(input->citr); p;
230 p = grad_iterator_next(input->citr)) {
231 if (p->fd > input->fd_max)
232 input->fd_max = p->fd;
233 }
234 if (input->fd_max == -2)
235 input->fd_max = -1;
236 }
237
238 if (input->fd_max < 0) {
239 pause();
240 return errno;
241 }
242
243 readfds = input->fdset;
244
245 status = select(input->fd_max + 1, &readfds, NULL, NULL, tv);
246
247 if (status == -1) {
248 if (errno == EINTR)
249 return 0;
250 } else if (status > 0) {
251 GRAD_DEBUG1(1, "select returned %d", status);
252
253 for (p = grad_iterator_first(input->citr); p;
254 p = grad_iterator_next(input->citr))
255 if (FD_ISSET(p->fd, &readfds))
256 channel_handle(p);
257 }
258 GRAD_DEBUG(100,"exit");
259 return status;
260 }
261
262 int
input_select_channel(INPUT * input,char * name,struct timeval * tv)263 input_select_channel(INPUT *input, char *name, struct timeval *tv)
264 {
265 CHANNEL *p;
266 int status;
267 fd_set readfds;
268 int fd_max = -1;
269 METHOD *m = grad_list_locate(input->methods, name, _method_comp);
270
271 GRAD_DEBUG(100,"enter");
272
273 if (!m)
274 return -1;
275 if (!input->citr)
276 input->citr = grad_iterator_create(input->channels);
277
278 FD_ZERO(&readfds);
279 for (p = grad_iterator_first(input->citr); p;
280 p = grad_iterator_next(input->citr)) {
281 if (p->method == m) {
282 if (p->fd > fd_max)
283 fd_max = p->fd;
284 FD_SET(p->fd, &readfds);
285 }
286 }
287
288 if (fd_max == -1)
289 return -1;
290
291 status = select(fd_max + 1, &readfds, NULL, NULL, tv);
292
293 if (status == -1) {
294 if (errno == EINTR)
295 return 0;
296 } else if (status > 0) {
297 GRAD_DEBUG1(1, "select returned %d", status);
298
299 for (p = grad_iterator_first(input->citr); p;
300 p = grad_iterator_next(input->citr))
301 if (FD_ISSET(p->fd, &readfds))
302 channel_handle(p);
303 }
304 GRAD_DEBUG(100,"exit");
305 return status;
306 }
307
308 struct iterate_closure {
309 char *name;
310 list_iterator_t fun;
311 void *data;
312 };
313
314 static int
_chan_itr(void * item,void * data)315 _chan_itr(void *item, void *data)
316 {
317 CHANNEL *chan = item;
318 struct iterate_closure *cp = data;
319 int rc = 0;
320
321 if (cp->name == NULL || strcmp(chan->method->name, cp->name) == 0)
322 rc = cp->fun(chan->data, cp->data);
323 return rc;
324 }
325
326 void
input_iterate_channels(INPUT * input,char * name,list_iterator_t fun,void * data)327 input_iterate_channels(INPUT *input, char *name,
328 list_iterator_t fun, void *data)
329 {
330 struct iterate_closure clos;
331
332 clos.name = name;
333 clos.fun = fun;
334 clos.data = data;
335 grad_list_iterate(input->channels, _chan_itr, &clos);
336 }
337