1 |
/* |
2 |
* ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). |
3 |
* |
4 |
* Copyright (C) 2001 Kevin L. Mitchell <klmitch@mit.edu> |
5 |
* Copyright (C) 2013 by the Hybrid Development Team. |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* 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. |
11 |
* |
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 |
19 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
20 |
* USA |
21 |
*/ |
22 |
|
23 |
/*! \file ioengine_poll.c |
24 |
* \brief POSIX poll() event engine. |
25 |
* \version $Id$ |
26 |
*/ |
27 |
|
28 |
#include "stdinc.h" |
29 |
#include "ioengine.h" |
30 |
#include "ircd.h" |
31 |
#include "memory.h" |
32 |
#include "log.h" |
33 |
#include "restart.h" |
34 |
|
35 |
#include <errno.h> |
36 |
#include <sys/poll.h> |
37 |
#include <sys/socket.h> |
38 |
#include <sys/types.h> |
39 |
#include <time.h> |
40 |
#include <unistd.h> |
41 |
|
42 |
#define POLL_ERROR_THRESHOLD 20 /**< after 20 poll errors, restart */ |
43 |
#define ERROR_EXPIRE_TIME 3600 /**< expire errors after an hour */ |
44 |
|
45 |
/* Figure out what bits to set for read */ |
46 |
#if defined(POLLMSG) && defined(POLLIN) && defined(POLLRDNORM) |
47 |
# define POLLREADFLAGS (POLLMSG|POLLIN|POLLRDNORM) |
48 |
#elif defined(POLLIN) && defined(POLLRDNORM) |
49 |
# define POLLREADFLAGS (POLLIN|POLLRDNORM) |
50 |
#elif defined(POLLIN) |
51 |
# define POLLREADFLAGS POLLIN |
52 |
#elif defined(POLLRDNORM) |
53 |
# define POLLREADFLAGS POLLRDNORM |
54 |
#endif |
55 |
|
56 |
/* Figure out what bits to set for write */ |
57 |
#if defined(POLLOUT) && defined(POLLWRNORM) |
58 |
# define POLLWRITEFLAGS (POLLOUT|POLLWRNORM) |
59 |
#elif defined(POLLOUT) |
60 |
# define POLLWRITEFLAGS POLLOUT |
61 |
#elif defined(POLLWRNORM) |
62 |
# define POLLWRITEFLAGS POLLWRNORM |
63 |
#endif |
64 |
|
65 |
/** Array of active Socket structures, indexed by file descriptor. */ |
66 |
static struct Socket** sockList; |
67 |
/** Array of poll() active elements. */ |
68 |
static struct pollfd* pollfdList; |
69 |
/** Number of pollfd elements currently used. */ |
70 |
static unsigned int poll_count; |
71 |
/** Maximum file descriptor supported, plus one. */ |
72 |
static unsigned int poll_max; |
73 |
|
74 |
/** Number of recent errors from poll(). */ |
75 |
static int errors; |
76 |
/** Periodic timer to forget errors. */ |
77 |
static struct Timer clear_error; |
78 |
|
79 |
/** Decrement the error count (once per hour). |
80 |
* @param[in] ev Expired timer event (ignored). |
81 |
*/ |
82 |
static void |
83 |
error_clear(struct Event* ev) |
84 |
{ |
85 |
if (!--errors) /* remove timer when error count reaches 0 */ |
86 |
timer_del(ev_timer(ev)); |
87 |
} |
88 |
|
89 |
/** Initialize the poll() engine. |
90 |
* @param[in] max_sockets Maximum number of file descriptors to support. |
91 |
* @return Non-zero on success, or zero on failure. |
92 |
*/ |
93 |
static int |
94 |
engine_init(int max_sockets) |
95 |
{ |
96 |
int i; |
97 |
|
98 |
/* allocate necessary memory */ |
99 |
sockList = (struct Socket **)MyMalloc(sizeof(struct Socket*) * max_sockets); |
100 |
pollfdList = (struct pollfd *)MyMalloc(sizeof(struct pollfd) * max_sockets); |
101 |
|
102 |
/* initialize the data */ |
103 |
for (i = 0; i < max_sockets; i++) |
104 |
{ |
105 |
sockList[i] = 0; |
106 |
pollfdList[i].fd = -1; |
107 |
pollfdList[i].events = 0; |
108 |
pollfdList[i].revents = 0; |
109 |
} |
110 |
|
111 |
poll_count = 0; /* nothing in set */ |
112 |
poll_max = max_sockets; /* number of sockets allocated */ |
113 |
|
114 |
return 1; |
115 |
} |
116 |
|
117 |
/** Figure out what events go with a given state. |
118 |
* @param[in] state %Socket state to consider. |
119 |
* @param[in] events User-specified preferred event set. |
120 |
* @return Actual set of preferred events. |
121 |
*/ |
122 |
static unsigned int |
123 |
state_to_events(enum SocketState state, unsigned int events) |
124 |
{ |
125 |
switch (state) |
126 |
{ |
127 |
case SS_CONNECTING: /* connecting socket */ |
128 |
return SOCK_EVENT_WRITABLE; |
129 |
break; |
130 |
|
131 |
case SS_LISTENING: /* listening socket */ |
132 |
case SS_NOTSOCK: /* our signal socket */ |
133 |
return SOCK_EVENT_READABLE; |
134 |
break; |
135 |
|
136 |
case SS_CONNECTED: |
137 |
case SS_DATAGRAM: |
138 |
case SS_CONNECTDG: |
139 |
return events; /* ordinary socket */ |
140 |
break; |
141 |
} |
142 |
|
143 |
/*NOTREACHED*/ |
144 |
return 0; |
145 |
} |
146 |
|
147 |
/** Set interest events in a pollfd as appropriate. |
148 |
* @param[in] idx Index of pollfd to operate on. |
149 |
* @param[in] clear Set of interest events to clear from socket. |
150 |
* @param[in] set Set of interest events to set on socket. |
151 |
*/ |
152 |
static void |
153 |
set_or_clear(unsigned int idx, unsigned int clear, unsigned int set) |
154 |
{ |
155 |
if ((clear ^ set) & SOCK_EVENT_READABLE) |
156 |
{ |
157 |
/* readable has changed */ |
158 |
if (set & SOCK_EVENT_READABLE) /* it's set */ |
159 |
pollfdList[idx].events |= POLLREADFLAGS; |
160 |
else /* clear it */ |
161 |
pollfdList[idx].events &= ~POLLREADFLAGS; |
162 |
} |
163 |
|
164 |
if ((clear ^ set) & SOCK_EVENT_WRITABLE) |
165 |
{ |
166 |
/* writable has changed */ |
167 |
if (set & SOCK_EVENT_WRITABLE) /* it's set */ |
168 |
pollfdList[idx].events |= POLLWRITEFLAGS; |
169 |
else /* clear it */ |
170 |
pollfdList[idx].events &= ~POLLWRITEFLAGS; |
171 |
} |
172 |
} |
173 |
|
174 |
/** Add a socket to the event engine. |
175 |
* @param[in] sock Socket to add to engine. |
176 |
* @return Non-zero on success, or zero on error. |
177 |
*/ |
178 |
static int |
179 |
engine_add(struct Socket *sock) |
180 |
{ |
181 |
unsigned int i; |
182 |
|
183 |
assert(sock); |
184 |
|
185 |
for (i = 0; sockList[i] && i < poll_count; ++i) /* Find an empty slot */ |
186 |
; |
187 |
|
188 |
ilog(LOG_TYPE_DEBUG, "poll: Looking at slot %d, contents %p", |
189 |
i, sockList[i]); |
190 |
|
191 |
if (i >= poll_count) |
192 |
{ |
193 |
/* ok, need to allocate another off the list */ |
194 |
if (poll_count >= poll_max) |
195 |
{ |
196 |
/* bounds-check... */ |
197 |
ilog(LOG_TYPE_DEBUG, |
198 |
"Attempt to add socket %d (> %d) to event engine", |
199 |
sock->s_fd, poll_max); |
200 |
return 0; |
201 |
} |
202 |
|
203 |
i = poll_count++; |
204 |
ilog(LOG_TYPE_DEBUG, "poll: Allocating a new slot: %d", i); |
205 |
} |
206 |
|
207 |
s_ed_int(sock) = i; /* set engine data */ |
208 |
sockList[i] = sock; /* enter socket into data structures */ |
209 |
pollfdList[i].fd = s_fd(sock); |
210 |
|
211 |
ilog(LOG_TYPE_DEBUG, "poll: Adding socket %d to engine on %d [%p], state %s", |
212 |
s_fd(sock), s_ed_int(sock), sock, state_to_name(s_state(sock))); |
213 |
|
214 |
/* set the appropriate bits */ |
215 |
set_or_clear(i, 0, state_to_events(s_state(sock), s_events(sock))); |
216 |
|
217 |
return 1; /* success */ |
218 |
} |
219 |
|
220 |
/** Handle state transition for a socket. |
221 |
* @param[in] sock Socket changing state. |
222 |
* @param[in] new_state New state for socket. |
223 |
*/ |
224 |
static void |
225 |
engine_state(struct Socket *sock, enum SocketState new_state) |
226 |
{ |
227 |
assert(0 != sock); |
228 |
assert(sock == sockList[s_ed_int(sock)]); |
229 |
assert(s_fd(sock) == pollfdList[s_ed_int(sock)].fd); |
230 |
|
231 |
ilog(LOG_TYPE_DEBUG, "poll: Changing state for socket %p to %s", |
232 |
sock, state_to_name(new_state)); |
233 |
|
234 |
/* set the correct events */ |
235 |
set_or_clear(s_ed_int(sock), |
236 |
state_to_events(s_state(sock), s_events(sock)), /* old state */ |
237 |
state_to_events(new_state, s_events(sock))); /* new state */ |
238 |
} |
239 |
|
240 |
/** Handle change to preferred socket events. |
241 |
* @param[in] sock Socket getting new interest list. |
242 |
* @param[in] new_events New set of interesting events for socket. |
243 |
*/ |
244 |
static void |
245 |
engine_events(struct Socket *sock, unsigned int new_events) |
246 |
{ |
247 |
assert(0 != sock); |
248 |
assert(sock == sockList[s_ed_int(sock)]); |
249 |
assert(s_fd(sock) == pollfdList[s_ed_int(sock)].fd); |
250 |
|
251 |
ilog(LOG_TYPE_DEBUG, "poll: Changing event mask for socket %p to [%s]", |
252 |
sock, sock_flags(new_events)); |
253 |
|
254 |
/* set the correct events */ |
255 |
set_or_clear(s_ed_int(sock), |
256 |
state_to_events(s_state(sock), s_events(sock)), /* old events */ |
257 |
state_to_events(s_state(sock), new_events)); /* new events */ |
258 |
} |
259 |
|
260 |
/** Remove a socket from the event engine. |
261 |
* @param[in] sock Socket being destroyed. |
262 |
*/ |
263 |
static void |
264 |
engine_delete(struct Socket *sock) |
265 |
{ |
266 |
assert(sock); |
267 |
assert(sock == sockList[s_ed_int(sock)]); |
268 |
assert(s_fd(sock) == pollfdList[s_ed_int(sock)].fd); |
269 |
|
270 |
ilog(LOG_TYPE_DEBUG, "poll: Deleting socket %d (%d) [%p], state %s", |
271 |
s_fd(sock), s_ed_int(sock), sock, state_to_name(s_state(sock))); |
272 |
|
273 |
/* clear the events */ |
274 |
pollfdList[s_ed_int(sock)].fd = -1; |
275 |
pollfdList[s_ed_int(sock)].events = 0; |
276 |
|
277 |
/* zero the socket list entry */ |
278 |
sockList[s_ed_int(sock)] = 0; |
279 |
|
280 |
/* update poll_count */ |
281 |
while (poll_count > 0 && sockList[poll_count - 1] == 0) |
282 |
--poll_count; |
283 |
} |
284 |
|
285 |
/** Run engine event loop. |
286 |
* @param[in] gen Lists of generators of various types. |
287 |
*/ |
288 |
static void |
289 |
engine_loop(struct Generators *gen) |
290 |
{ |
291 |
int iwait; |
292 |
int nfds; |
293 |
unsigned int i; |
294 |
int errcode; |
295 |
socklen_t codesize; |
296 |
struct Socket *sock; |
297 |
|
298 |
while (running) |
299 |
{ |
300 |
iwait = timer_next(gen) ? (timer_next(gen) - CurrentTime) * 1000 : -1; |
301 |
|
302 |
ilog(LOG_TYPE_DEBUG, "poll: delay: %Tu (%Tu) %d", |
303 |
timer_next(gen), CurrentTime, iwait); |
304 |
|
305 |
/* check for active files */ |
306 |
nfds = poll(pollfdList, poll_count, iwait); |
307 |
|
308 |
set_time(); /* set current time... */ |
309 |
|
310 |
if (nfds < 0) |
311 |
{ |
312 |
if (errno != EINTR) |
313 |
{ |
314 |
/* ignore poll interrupts */ |
315 |
/* Log the poll error */ |
316 |
ilog(LOG_TYPE_DEBUG, "poll() error: %m"); |
317 |
if (!errors++) |
318 |
timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC, ERROR_EXPIRE_TIME); |
319 |
else if (errors > POLL_ERROR_THRESHOLD) /* too many errors... */ |
320 |
restart("too many poll errors"); |
321 |
} |
322 |
|
323 |
/* |
324 |
* Old code did a sleep(1) here; with usage these days, |
325 |
* that may be too expensive |
326 |
*/ |
327 |
continue; |
328 |
} |
329 |
|
330 |
for (i = 0; nfds && i < poll_count; i++) |
331 |
{ |
332 |
if (!(sock = sockList[i])) /* skip empty socket elements */ |
333 |
continue; |
334 |
|
335 |
assert(s_fd(sock) == pollfdList[i].fd); |
336 |
|
337 |
gen_ref_inc(sock); /* can't have it going away on us */ |
338 |
|
339 |
ilog(LOG_TYPE_DEBUG, "poll: Checking socket %p (fd %d, index %d, " |
340 |
"state %s, events %s", sock, s_fd(sock), i, |
341 |
state_to_name(s_state(sock)), sock_flags(s_events(sock))); |
342 |
|
343 |
if (s_state(sock) != SS_NOTSOCK) { |
344 |
errcode = 0; /* check for errors on socket */ |
345 |
codesize = sizeof(errcode); |
346 |
if (getsockopt(s_fd(sock), SOL_SOCKET, SO_ERROR, &errcode, |
347 |
&codesize) < 0) |
348 |
errcode = errno; /* work around Solaris implementation */ |
349 |
|
350 |
if (errcode) |
351 |
{ |
352 |
/* an error occurred; generate an event */ |
353 |
ilog(LOG_TYPE_DEBUG, "poll: Error %d on fd %d (index %d), socket %p", |
354 |
errcode, s_fd(sock), i, sock); |
355 |
event_generate(ET_ERROR, sock, errcode); |
356 |
gen_ref_dec(sock); /* careful not to leak ref counts */ |
357 |
nfds--; |
358 |
continue; |
359 |
} |
360 |
} |
361 |
|
362 |
#ifdef POLLHUP |
363 |
if (pollfdList[i].revents & POLLHUP) |
364 |
{ |
365 |
/* hang-up on socket */ |
366 |
ilog(LOG_TYPE_DEBUG, "poll: EOF from client (POLLHUP)"); |
367 |
event_generate(ET_EOF, sock, 0); |
368 |
nfds--; |
369 |
continue; |
370 |
} |
371 |
#endif /* POLLHUP */ |
372 |
|
373 |
switch (s_state(sock)) |
374 |
{ |
375 |
case SS_CONNECTING: |
376 |
if (pollfdList[i].revents & POLLWRITEFLAGS) |
377 |
{ |
378 |
/* connect completed */ |
379 |
ilog(LOG_TYPE_DEBUG, "poll: Connection completed"); |
380 |
event_generate(ET_CONNECT, sock, 0); |
381 |
nfds--; |
382 |
} |
383 |
break; |
384 |
|
385 |
case SS_LISTENING: |
386 |
if (pollfdList[i].revents & POLLREADFLAGS) |
387 |
{ |
388 |
/* ready for accept */ |
389 |
ilog(LOG_TYPE_DEBUG, "poll: Ready for accept"); |
390 |
event_generate(ET_ACCEPT, sock, 0); |
391 |
nfds--; |
392 |
} |
393 |
break; |
394 |
|
395 |
case SS_NOTSOCK: |
396 |
if (pollfdList[i].revents & POLLREADFLAGS) |
397 |
{ |
398 |
/* data on socket */ |
399 |
/* can't peek; it's not a socket */ |
400 |
ilog(LOG_TYPE_DEBUG, "poll: non-socket readable"); |
401 |
event_generate(ET_READ, sock, 0); |
402 |
nfds--; |
403 |
} |
404 |
break; |
405 |
|
406 |
case SS_CONNECTED: |
407 |
if (pollfdList[i].revents & POLLREADFLAGS) |
408 |
{ |
409 |
/* data on socket */ |
410 |
char c; |
411 |
|
412 |
switch (recv(s_fd(sock), &c, 1, MSG_PEEK)) |
413 |
{ |
414 |
/* check EOF */ |
415 |
case -1: /* error occurred?!? */ |
416 |
if (errno == EAGAIN) |
417 |
{ |
418 |
ilog(LOG_TYPE_DEBUG, "poll: Resource temporarily unavailable?"); |
419 |
continue; |
420 |
} |
421 |
ilog(LOG_TYPE_DEBUG, "poll: Uncaught error!"); |
422 |
event_generate(ET_ERROR, sock, errno); |
423 |
break; |
424 |
|
425 |
case 0: /* EOF from client */ |
426 |
ilog(LOG_TYPE_DEBUG, "poll: EOF from client"); |
427 |
event_generate(ET_EOF, sock, 0); |
428 |
break; |
429 |
|
430 |
default: /* some data can be read */ |
431 |
ilog(LOG_TYPE_DEBUG, "poll: Data to be read"); |
432 |
event_generate(ET_READ, sock, 0); |
433 |
break; |
434 |
} |
435 |
} |
436 |
if (pollfdList[i].revents & POLLWRITEFLAGS) { /* socket writable */ |
437 |
ilog(LOG_TYPE_DEBUG, "poll: Data can be written"); |
438 |
event_generate(ET_WRITE, sock, 0); |
439 |
} |
440 |
if (pollfdList[i].revents & (POLLREADFLAGS | POLLWRITEFLAGS)) |
441 |
nfds--; |
442 |
break; |
443 |
|
444 |
case SS_DATAGRAM: |
445 |
case SS_CONNECTDG: |
446 |
if (pollfdList[i].revents & POLLREADFLAGS) |
447 |
{ |
448 |
/* socket readable */ |
449 |
ilog(LOG_TYPE_DEBUG, "poll: Datagram to be read"); |
450 |
event_generate(ET_READ, sock, 0); |
451 |
} |
452 |
|
453 |
if (pollfdList[i].revents & POLLWRITEFLAGS) |
454 |
{ |
455 |
/* socket writable */ |
456 |
ilog(LOG_TYPE_DEBUG, "poll: Datagram can be written"); |
457 |
event_generate(ET_WRITE, sock, 0); |
458 |
} |
459 |
|
460 |
if (pollfdList[i].revents & (POLLREADFLAGS | POLLWRITEFLAGS)) |
461 |
nfds--; |
462 |
break; |
463 |
} |
464 |
|
465 |
gen_ref_dec(sock); /* we're done with it */ |
466 |
} |
467 |
|
468 |
timer_run(); /* execute any pending timers */ |
469 |
} |
470 |
} |
471 |
|
472 |
/** Descriptor for poll() event engine. */ |
473 |
struct Engine engine_poll = |
474 |
{ |
475 |
"poll()", /* Engine name */ |
476 |
engine_init, /* Engine initialization function */ |
477 |
0, /* Engine signal registration function */ |
478 |
engine_add, /* Engine socket registration function */ |
479 |
engine_state, /* Engine socket state change function */ |
480 |
engine_events, /* Engine socket events mask function */ |
481 |
engine_delete, /* Engine socket deletion function */ |
482 |
engine_loop /* Core engine event loop */ |
483 |
}; |