2149b8978b0ce6ea28873c190e7b6a55cef995be
[ircu2.10.12-pk.git] / ircd / engine_select.c
1 /*
2  * IRC - Internet Relay Chat, ircd/engine_select.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  * $Id$
20  */
21 #include "config.h"
22
23 #include "ircd_events.h"
24
25 #include "ircd.h"
26 #include "ircd_log.h"
27 #include "s_debug.h"
28
29 /* On BSD, define FD_SETSIZE to what we want before including sys/types.h */
30 #if  defined(__FreeBSD__) || defined(__NetBSD__) || defined(__bsdi__)
31 # if !defined(FD_SETSIZE)
32 #  define FD_SETSIZE    MAXCONNECTIONS
33 # endif
34 #endif
35
36 #include <assert.h>
37 #include <errno.h>
38 #include <sys/socket.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <time.h>
42 #include <unistd.h>
43
44 #define SELECT_ERROR_THRESHOLD  20      /* after 20 select errors, restart */
45 #define ERROR_EXPIRE_TIME       3600    /* expire errors after an hour */
46
47 static struct Socket* sockList[FD_SETSIZE];
48 static int highest_fd;
49 static fd_set global_read_set;
50 static fd_set global_write_set;
51
52 static int errors = 0;
53 static struct Timer clear_error;
54
55 /* decrements the error count once per hour */
56 static void
57 error_clear(struct Event* ev)
58 {
59   if (!--errors) /* remove timer when error count reaches 0 */
60     timer_del(ev_timer(ev));
61 }
62
63 /* initialize the select engine */
64 static int
65 engine_init(int max_sockets)
66 {
67   int i;
68
69   if (max_sockets > FD_SETSIZE) { /* too many sockets */
70     log_write(LS_SYSTEM, L_WARNING, 0,
71               "select() engine cannot handle %d sockets (> %d)",
72               max_sockets, FD_SETSIZE);
73     return 0;
74   }
75
76   FD_ZERO(&global_read_set); /* zero the global fd sets */
77   FD_ZERO(&global_write_set);
78
79   for (i = 0; i < FD_SETSIZE; i++) /* zero the sockList */
80     sockList[i] = 0;
81
82   highest_fd = -1; /* No fds in set */
83
84   return 1; /* initialization successful */
85 }
86
87 /* Figure out what events go with a given state */
88 static unsigned int
89 state_to_events(enum SocketState state, unsigned int events)
90 {
91   switch (state) {
92   case SS_CONNECTING: /* connecting socket */
93     return SOCK_EVENT_WRITABLE;
94     break;
95
96   case SS_LISTENING: /* listening socket */
97   case SS_NOTSOCK: /* our signal socket */
98     return SOCK_EVENT_READABLE;
99     break;
100
101   case SS_CONNECTED: case SS_DATAGRAM: case SS_CONNECTDG:
102     return events; /* ordinary socket */
103     break;
104   }
105
106   /*NOTREACHED*/
107   return 0;
108 }
109
110 /* Toggle bits in the global fd sets appropriately */
111 static void
112 set_or_clear(int fd, unsigned int clear, unsigned int set)
113 {
114   if ((clear ^ set) & SOCK_EVENT_READABLE) { /* readable has changed */
115     if (set & SOCK_EVENT_READABLE) /* it's set */
116       FD_SET(fd, &global_read_set);
117     else /* clear it */
118       FD_CLR(fd, &global_read_set);
119   }
120
121   if ((clear ^ set) & SOCK_EVENT_WRITABLE) { /* writable has changed */
122     if (set & SOCK_EVENT_WRITABLE) /* it's set */
123       FD_SET(fd, &global_write_set);
124     else /* clear it */
125       FD_CLR(fd, &global_write_set);
126   }
127 }
128
129 /* add a socket to be listened on */
130 static int
131 engine_add(struct Socket* sock)
132 {
133   assert(0 != sock);
134   assert(0 == sockList[s_fd(sock)]);
135
136   /* bounds-check... */
137   if (s_fd(sock) >= FD_SETSIZE) {
138     log_write(LS_SYSTEM, L_ERROR, 0,
139               "Attempt to add socket %d (> %d) to event engine", s_fd(sock),
140               FD_SETSIZE);
141     return 0;
142   }
143
144   sockList[s_fd(sock)] = sock; /* add to list */
145
146   if (s_fd(sock) >= highest_fd) /* update highest_fd */
147     highest_fd = s_fd(sock);
148
149   Debug((DEBUG_ENGINE, "select: Adding socket %d to engine [%p], state %s",
150          s_fd(sock), sock, state_to_name(s_state(sock))));
151
152   /* set the fd set bits */
153   set_or_clear(s_fd(sock), 0, state_to_events(s_state(sock), s_events(sock)));
154
155   return 1; /* success */
156 }
157
158 /* socket switching to new state */
159 static void
160 engine_state(struct Socket* sock, enum SocketState new_state)
161 {
162   assert(0 != sock);
163   assert(sock == sockList[s_fd(sock)]);
164
165   Debug((DEBUG_ENGINE, "select: Changing state for socket %p to %s", sock,
166          state_to_name(new_state)));
167
168   /* set the correct events */
169   set_or_clear(s_fd(sock),
170                state_to_events(s_state(sock), s_events(sock)), /* old state */
171                state_to_events(new_state, s_events(sock))); /* new state */
172 }
173
174 /* socket events changing */
175 static void
176 engine_events(struct Socket* sock, unsigned int new_events)
177 {
178   assert(0 != sock);
179   assert(sock == sockList[s_fd(sock)]);
180
181   Debug((DEBUG_ENGINE, "select: Changing event mask for socket %p to [%s]",
182          sock, sock_flags(new_events)));
183
184   /* set the correct events */
185   set_or_clear(s_fd(sock),
186                state_to_events(s_state(sock), s_events(sock)), /* old events */
187                state_to_events(s_state(sock), new_events)); /* new events */
188 }
189
190 /* socket going away */
191 static void
192 engine_delete(struct Socket* sock)
193 {
194   assert(0 != sock);
195   assert(sock == sockList[s_fd(sock)]);
196
197   Debug((DEBUG_ENGINE, "select: Deleting socket %d [%p], state %s", s_fd(sock),
198          sock, state_to_name(s_state(sock))));
199
200   FD_CLR(s_fd(sock), &global_read_set); /* clear event set bits */
201   FD_CLR(s_fd(sock), &global_write_set);
202
203   sockList[s_fd(sock)] = 0; /* zero the socket list entry */
204
205   while (highest_fd > -1 && sockList[highest_fd] == 0) /* update highest_fd */
206     highest_fd--;
207 }
208
209 /* engine event loop */
210 static void
211 engine_loop(struct Generators* gen)
212 {
213   struct timeval wait;
214   fd_set read_set;
215   fd_set write_set;
216   int nfds;
217   int i;
218   int errcode;
219   size_t codesize;
220   struct Socket *sock;
221
222   while (running) {
223     read_set = global_read_set; /* all hail structure copy!! */
224     write_set = global_write_set;
225
226     /* set up the sleep time */
227     wait.tv_sec = timer_next(gen) ? (timer_next(gen) - CurrentTime) : -1;
228     wait.tv_usec = 0;
229
230     Debug((DEBUG_INFO, "select: delay: %Tu (%Tu) %Tu", timer_next(gen),
231            CurrentTime, wait.tv_sec));
232
233     /* check for active files */
234     nfds = select(highest_fd + 1, &read_set, &write_set, 0,
235                   wait.tv_sec < 0 ? 0 : &wait);
236
237     CurrentTime = time(0); /* set current time... */
238
239     if (nfds < 0) {
240       if (errno != EINTR) { /* ignore select interrupts */
241         /* Log the select error */
242         log_write(LS_SOCKET, L_ERROR, 0, "select() error: %m");
243         if (!errors++)
244           timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC,
245                     ERROR_EXPIRE_TIME);
246         else if (errors > SELECT_ERROR_THRESHOLD) /* too many errors... */
247           server_restart("too many select errors");
248       }
249       /* old code did a sleep(1) here; with usage these days,
250        * that may be too expensive
251        */
252       continue;
253     }
254
255     for (i = 0; nfds && i <= highest_fd; i++) {
256       if (!(sock = sockList[i])) /* skip empty socket elements */
257         continue;
258
259       assert(s_fd(sock) == i);
260
261       gen_ref_inc(sock); /* can't have it going away on us */
262
263       Debug((DEBUG_ENGINE, "select: Checking socket %p (fd %d) state %s, "
264              "events %s", sock, i, state_to_name(s_state(sock)),
265              sock_flags(s_events(sock))));
266
267       if (s_state(sock) != SS_NOTSOCK) {
268         errcode = 0; /* check for errors on socket */
269         codesize = sizeof(errcode);
270         if (getsockopt(i, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
271           errcode = errno; /* work around Solaris implementation */
272
273         if (errcode) { /* an error occurred; generate an event */
274           Debug((DEBUG_ENGINE, "select: Error %d on fd %d, socket %p", errcode,
275                  i, sock));
276           event_generate(ET_ERROR, sock, errcode);
277           gen_ref_dec(sock); /* careful not to leak reference counts */
278           continue;
279         }
280       }
281
282       switch (s_state(sock)) {
283       case SS_CONNECTING:
284         if (FD_ISSET(i, &write_set)) { /* connection completed */
285           Debug((DEBUG_ENGINE, "select: Connection completed"));
286           event_generate(ET_CONNECT, sock, 0);
287           nfds--;
288           continue;
289         }
290         break;
291
292       case SS_LISTENING:
293         if (FD_ISSET(i, &read_set)) { /* connection to be accepted */
294           Debug((DEBUG_ENGINE, "select: Ready for accept"));
295           event_generate(ET_ACCEPT, sock, 0);
296           nfds--;
297         }
298         break;
299
300       case SS_NOTSOCK:
301         if (FD_ISSET(i, &read_set)) { /* data on socket */
302           /* can't peek; it's not a socket */
303           Debug((DEBUG_ENGINE, "select: non-socket readable"));
304           event_generate(ET_READ, sock, 0);
305           nfds--;
306         }
307         break;
308
309       case SS_CONNECTED:
310         if (FD_ISSET(i, &read_set)) { /* data to be read from socket */
311           char c;
312
313           switch (recv(i, &c, 1, MSG_PEEK)) { /* check for EOF */
314           case -1: /* error occurred?!? */
315             if (errno == EAGAIN) {
316               Debug((DEBUG_ENGINE, "select: Resource temporarily "
317                      "unavailable?"));
318               continue;
319             }
320             Debug((DEBUG_ENGINE, "select: Uncaught error!"));
321             event_generate(ET_ERROR, sock, errno);
322             break;
323
324           case 0: /* EOF from client */
325             Debug((DEBUG_ENGINE, "select: EOF from client"));
326             event_generate(ET_EOF, sock, 0);
327             break;
328
329           default: /* some data can be read */
330             Debug((DEBUG_ENGINE, "select: Data to be read"));
331             event_generate(ET_READ, sock, 0);
332             break;
333           }
334         }
335         if (FD_ISSET(i, &write_set)) { /* data can be written to socket */
336           Debug((DEBUG_ENGINE, "select: Data can be written"));
337           event_generate(ET_WRITE, sock, 0);
338         }
339         if (FD_ISSET(i, &read_set) || FD_ISSET(i, &write_set))
340           nfds--;
341         break;
342
343       case SS_DATAGRAM: case SS_CONNECTDG:
344         if (FD_ISSET(i, &read_set)) { /* data to be read from socket */
345           Debug((DEBUG_ENGINE, "select: Datagram to be read"));
346           event_generate(ET_READ, sock, 0);
347         }
348         if (FD_ISSET(i, &write_set)) { /* data can be written to socket */
349           Debug((DEBUG_ENGINE, "select: Datagram can be written"));
350           event_generate(ET_WRITE, sock, 0);
351         }
352         if (FD_ISSET(i, &read_set) || FD_ISSET(i, &write_set))
353           nfds--;
354         break;
355       }
356
357       assert(s_fd(sock) == i);
358
359       gen_ref_dec(sock); /* we're done with it */
360     }
361
362     timer_run(); /* execute any pending timers */
363   }
364 }
365
366 struct Engine engine_select = {
367   "select()",           /* Engine name */
368   engine_init,          /* Engine initialization function */
369   0,                    /* Engine signal registration function (none) */
370   engine_add,           /* Engine socket registration function */
371   engine_state,         /* Engine socket state change function */
372   engine_events,        /* Engine socket events mask function */
373   engine_delete,        /* Engine socket deletion function */
374   engine_loop           /* Core engine event loop */
375 };