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