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