]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | /* |
2 | * util/winsock_event.c - implementation of the unbound winsock event handler. | |
3 | * | |
4 | * Copyright (c) 2008, NLnet Labs. All rights reserved. | |
5 | * | |
6 | * This software is open source. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * Redistributions of source code must retain the above copyright notice, | |
13 | * this list of conditions and the following disclaimer. | |
14 | * | |
15 | * Redistributions in binary form must reproduce the above copyright notice, | |
16 | * this list of conditions and the following disclaimer in the documentation | |
17 | * and/or other materials provided with the distribution. | |
18 | * | |
19 | * Neither the name of the NLNET LABS nor the names of its contributors may | |
20 | * be used to endorse or promote products derived from this software without | |
21 | * specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
27 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |
29 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
30 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
34 | */ | |
35 | /** | |
36 | * \file | |
37 | * Implementation of the unbound WinSock2 API event notification handler | |
38 | * for the Windows port. | |
39 | */ | |
40 | ||
41 | #include "config.h" | |
42 | #ifdef USE_WINSOCK | |
43 | #include <signal.h> | |
44 | #ifdef HAVE_TIME_H | |
45 | #include <time.h> | |
46 | #endif | |
47 | #include <sys/time.h> | |
48 | #include "util/winsock_event.h" | |
49 | #include "util/fptr_wlist.h" | |
50 | ||
51 | int mini_ev_cmp(const void* a, const void* b) | |
52 | { | |
53 | const struct event *e = (const struct event*)a; | |
54 | const struct event *f = (const struct event*)b; | |
55 | if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec) | |
56 | return -1; | |
57 | if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec) | |
58 | return 1; | |
59 | if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec) | |
60 | return -1; | |
61 | if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec) | |
62 | return 1; | |
63 | if(e < f) | |
64 | return -1; | |
65 | if(e > f) | |
66 | return 1; | |
67 | return 0; | |
68 | } | |
69 | ||
70 | /** set time */ | |
71 | static int | |
72 | settime(struct event_base* base) | |
73 | { | |
74 | if(gettimeofday(base->time_tv, NULL) < 0) { | |
75 | return -1; | |
76 | } | |
77 | #ifndef S_SPLINT_S | |
78 | *base->time_secs = (time_t)base->time_tv->tv_sec; | |
79 | #endif | |
80 | return 0; | |
81 | } | |
82 | ||
83 | #ifdef UNBOUND_DEBUG | |
84 | /** | |
85 | * Find a fd in the list of items. | |
86 | * Note that not all items have a fd associated (those are -1). | |
87 | * Signals are stored separately, and not searched. | |
88 | * @param base: event base to look in. | |
89 | * @param fd: what socket to look for. | |
90 | * @return the index in the array, or -1 on failure. | |
91 | */ | |
92 | static int | |
93 | find_fd(struct event_base* base, int fd) | |
94 | { | |
95 | int i; | |
96 | for(i=0; i<base->max; i++) { | |
97 | if(base->items[i]->ev_fd == fd) | |
98 | return i; | |
99 | } | |
100 | return -1; | |
101 | } | |
102 | #endif | |
103 | ||
104 | /** Find ptr in base array */ | |
105 | static void | |
106 | zero_waitfor(WSAEVENT waitfor[], WSAEVENT x) | |
107 | { | |
108 | int i; | |
109 | for(i=0; i<WSK_MAX_ITEMS; i++) { | |
110 | if(waitfor[i] == x) | |
111 | waitfor[i] = 0; | |
112 | } | |
113 | } | |
114 | ||
115 | void *event_init(time_t* time_secs, struct timeval* time_tv) | |
116 | { | |
117 | struct event_base* base = (struct event_base*)malloc( | |
118 | sizeof(struct event_base)); | |
119 | if(!base) | |
120 | return NULL; | |
121 | memset(base, 0, sizeof(*base)); | |
122 | base->time_secs = time_secs; | |
123 | base->time_tv = time_tv; | |
124 | if(settime(base) < 0) { | |
125 | event_base_free(base); | |
126 | return NULL; | |
127 | } | |
128 | base->items = (struct event**)calloc(WSK_MAX_ITEMS, | |
129 | sizeof(struct event*)); | |
130 | if(!base->items) { | |
131 | event_base_free(base); | |
132 | return NULL; | |
133 | } | |
134 | base->cap = WSK_MAX_ITEMS; | |
135 | base->max = 0; | |
136 | base->times = rbtree_create(mini_ev_cmp); | |
137 | if(!base->times) { | |
138 | event_base_free(base); | |
139 | return NULL; | |
140 | } | |
141 | base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*)); | |
142 | if(!base->signals) { | |
143 | event_base_free(base); | |
144 | return NULL; | |
145 | } | |
146 | base->tcp_stickies = 0; | |
147 | base->tcp_reinvigorated = 0; | |
148 | verbose(VERB_CLIENT, "winsock_event inited"); | |
149 | return base; | |
150 | } | |
151 | ||
152 | const char *event_get_version(void) | |
153 | { | |
154 | return "winsock-event-"PACKAGE_VERSION; | |
155 | } | |
156 | ||
157 | const char *event_get_method(void) | |
158 | { | |
159 | return "WSAWaitForMultipleEvents"; | |
160 | } | |
161 | ||
162 | /** call timeouts handlers, and return how long to wait for next one or -1 */ | |
163 | static void handle_timeouts(struct event_base* base, struct timeval* now, | |
164 | struct timeval* wait) | |
165 | { | |
166 | struct event* p; | |
167 | #ifndef S_SPLINT_S | |
168 | wait->tv_sec = (time_t)-1; | |
169 | #endif | |
170 | verbose(VERB_CLIENT, "winsock_event handle_timeouts"); | |
171 | ||
172 | while((rbnode_t*)(p = (struct event*)rbtree_first(base->times)) | |
173 | !=RBTREE_NULL) { | |
174 | #ifndef S_SPLINT_S | |
175 | if(p->ev_timeout.tv_sec > now->tv_sec || | |
176 | (p->ev_timeout.tv_sec==now->tv_sec && | |
177 | p->ev_timeout.tv_usec > now->tv_usec)) { | |
178 | /* there is a next larger timeout. wait for it */ | |
179 | wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec; | |
180 | if(now->tv_usec > p->ev_timeout.tv_usec) { | |
181 | wait->tv_sec--; | |
182 | wait->tv_usec = 1000000 - (now->tv_usec - | |
183 | p->ev_timeout.tv_usec); | |
184 | } else { | |
185 | wait->tv_usec = p->ev_timeout.tv_usec | |
186 | - now->tv_usec; | |
187 | } | |
188 | verbose(VERB_CLIENT, "winsock_event wait=" ARG_LL "d.%6.6d", | |
189 | (long long)wait->tv_sec, (int)wait->tv_usec); | |
190 | return; | |
191 | } | |
192 | #endif | |
193 | /* event times out, remove it */ | |
194 | (void)rbtree_delete(base->times, p); | |
195 | p->ev_events &= ~EV_TIMEOUT; | |
196 | fptr_ok(fptr_whitelist_event(p->ev_callback)); | |
197 | (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg); | |
198 | } | |
199 | verbose(VERB_CLIENT, "winsock_event wait=(-1)"); | |
200 | } | |
201 | ||
202 | /** handle is_signal events and see if signalled */ | |
203 | static void handle_signal(struct event* ev) | |
204 | { | |
205 | DWORD ret; | |
206 | log_assert(ev->is_signal && ev->hEvent); | |
207 | /* see if the event is signalled */ | |
208 | ret = WSAWaitForMultipleEvents(1, &ev->hEvent, 0 /* any object */, | |
209 | 0 /* return immediately */, 0 /* not alertable for IOcomple*/); | |
210 | if(ret == WSA_WAIT_IO_COMPLETION || ret == WSA_WAIT_FAILED) { | |
211 | log_err("WSAWaitForMultipleEvents(signal) failed: %s", | |
212 | wsa_strerror(WSAGetLastError())); | |
213 | return; | |
214 | } | |
215 | if(ret == WSA_WAIT_TIMEOUT) { | |
216 | /* not signalled */ | |
217 | return; | |
218 | } | |
219 | ||
220 | /* reset the signal */ | |
221 | if(!WSAResetEvent(ev->hEvent)) | |
222 | log_err("WSAResetEvent failed: %s", | |
223 | wsa_strerror(WSAGetLastError())); | |
224 | /* do the callback (which may set the signal again) */ | |
225 | fptr_ok(fptr_whitelist_event(ev->ev_callback)); | |
226 | (*ev->ev_callback)(ev->ev_fd, ev->ev_events, ev->ev_arg); | |
227 | } | |
228 | ||
229 | /** call select and callbacks for that */ | |
230 | static int handle_select(struct event_base* base, struct timeval* wait) | |
231 | { | |
232 | DWORD timeout = 0; /* in milliseconds */ | |
233 | DWORD ret; | |
234 | struct event* eventlist[WSK_MAX_ITEMS]; | |
235 | WSANETWORKEVENTS netev; | |
236 | int i, numwait = 0, startidx = 0, was_timeout = 0; | |
237 | int newstickies = 0; | |
238 | struct timeval nultm; | |
239 | ||
240 | verbose(VERB_CLIENT, "winsock_event handle_select"); | |
241 | ||
242 | #ifndef S_SPLINT_S | |
243 | if(wait->tv_sec==(time_t)-1) | |
244 | wait = NULL; | |
245 | if(wait) | |
246 | timeout = wait->tv_sec*1000 + wait->tv_usec/1000; | |
247 | if(base->tcp_stickies) { | |
248 | wait = &nultm; | |
249 | nultm.tv_sec = 0; | |
250 | nultm.tv_usec = 0; | |
251 | timeout = 0; /* no waiting, we have sticky events */ | |
252 | } | |
253 | #endif | |
254 | ||
255 | /* prepare event array */ | |
256 | for(i=0; i<base->max; i++) { | |
257 | if(base->items[i]->ev_fd == -1 && !base->items[i]->is_signal) | |
258 | continue; /* skip timer only events */ | |
259 | eventlist[numwait] = base->items[i]; | |
260 | base->waitfor[numwait++] = base->items[i]->hEvent; | |
261 | if(numwait == WSK_MAX_ITEMS) | |
262 | break; /* sanity check */ | |
263 | } | |
264 | log_assert(numwait <= WSA_MAXIMUM_WAIT_EVENTS); | |
265 | verbose(VERB_CLIENT, "winsock_event bmax=%d numwait=%d wait=%x " | |
266 | "timeout=%d", base->max, numwait, (int)wait, (int)timeout); | |
267 | ||
268 | /* do the wait */ | |
269 | if(numwait == 0) { | |
270 | /* WSAWaitFor.. doesn't like 0 event objects */ | |
271 | if(wait) { | |
272 | Sleep(timeout); | |
273 | } | |
274 | was_timeout = 1; | |
275 | } else { | |
276 | ret = WSAWaitForMultipleEvents(numwait, base->waitfor, | |
277 | 0 /* do not wait for all, just one will do */, | |
278 | wait?timeout:WSA_INFINITE, | |
279 | 0); /* we are not alertable (IO completion events) */ | |
280 | if(ret == WSA_WAIT_IO_COMPLETION) { | |
281 | log_err("WSAWaitForMultipleEvents failed: WSA_WAIT_IO_COMPLETION"); | |
282 | return -1; | |
283 | } else if(ret == WSA_WAIT_FAILED) { | |
284 | log_err("WSAWaitForMultipleEvents failed: %s", | |
285 | wsa_strerror(WSAGetLastError())); | |
286 | return -1; | |
287 | } else if(ret == WSA_WAIT_TIMEOUT) { | |
288 | was_timeout = 1; | |
289 | } else | |
290 | startidx = ret - WSA_WAIT_EVENT_0; | |
291 | } | |
292 | verbose(VERB_CLIENT, "winsock_event wake was_timeout=%d startidx=%d", | |
293 | was_timeout, startidx); | |
294 | ||
295 | /* get new time after wait */ | |
296 | if(settime(base) < 0) | |
297 | return -1; | |
298 | ||
299 | /* callbacks */ | |
300 | if(base->tcp_stickies) | |
301 | startidx = 0; /* process all events, some are sticky */ | |
302 | for(i=startidx; i<numwait; i++) | |
303 | eventlist[i]->just_checked = 1; | |
304 | ||
305 | verbose(VERB_CLIENT, "winsock_event signals"); | |
306 | for(i=startidx; i<numwait; i++) { | |
307 | if(!base->waitfor[i]) | |
308 | continue; /* was deleted */ | |
309 | if(eventlist[i]->is_signal) { | |
310 | eventlist[i]->just_checked = 0; | |
311 | handle_signal(eventlist[i]); | |
312 | } | |
313 | } | |
314 | /* early exit - do not process network, exit quickly */ | |
315 | if(base->need_to_exit) | |
316 | return 0; | |
317 | ||
318 | verbose(VERB_CLIENT, "winsock_event net"); | |
319 | for(i=startidx; i<numwait; i++) { | |
320 | short bits = 0; | |
321 | /* eventlist[i] fired */ | |
322 | /* see if eventlist[i] is still valid and just checked from | |
323 | * WSAWaitForEvents */ | |
324 | if(!base->waitfor[i]) | |
325 | continue; /* was deleted */ | |
326 | if(!eventlist[i]->just_checked) | |
327 | continue; /* added by other callback */ | |
328 | if(eventlist[i]->is_signal) | |
329 | continue; /* not a network event at all */ | |
330 | eventlist[i]->just_checked = 0; | |
331 | ||
332 | if(WSAEnumNetworkEvents(eventlist[i]->ev_fd, | |
333 | base->waitfor[i], /* reset the event handle */ | |
334 | /*NULL,*/ /* do not reset the event handle */ | |
335 | &netev) != 0) { | |
336 | log_err("WSAEnumNetworkEvents failed: %s", | |
337 | wsa_strerror(WSAGetLastError())); | |
338 | return -1; | |
339 | } | |
340 | if((netev.lNetworkEvents & FD_READ)) { | |
341 | if(netev.iErrorCode[FD_READ_BIT] != 0) | |
342 | verbose(VERB_ALGO, "FD_READ_BIT error: %s", | |
343 | wsa_strerror(netev.iErrorCode[FD_READ_BIT])); | |
344 | bits |= EV_READ; | |
345 | } | |
346 | if((netev.lNetworkEvents & FD_WRITE)) { | |
347 | if(netev.iErrorCode[FD_WRITE_BIT] != 0) | |
348 | verbose(VERB_ALGO, "FD_WRITE_BIT error: %s", | |
349 | wsa_strerror(netev.iErrorCode[FD_WRITE_BIT])); | |
350 | bits |= EV_WRITE; | |
351 | } | |
352 | if((netev.lNetworkEvents & FD_CONNECT)) { | |
353 | if(netev.iErrorCode[FD_CONNECT_BIT] != 0) | |
354 | verbose(VERB_ALGO, "FD_CONNECT_BIT error: %s", | |
355 | wsa_strerror(netev.iErrorCode[FD_CONNECT_BIT])); | |
356 | bits |= EV_READ; | |
357 | bits |= EV_WRITE; | |
358 | } | |
359 | if((netev.lNetworkEvents & FD_ACCEPT)) { | |
360 | if(netev.iErrorCode[FD_ACCEPT_BIT] != 0) | |
361 | verbose(VERB_ALGO, "FD_ACCEPT_BIT error: %s", | |
362 | wsa_strerror(netev.iErrorCode[FD_ACCEPT_BIT])); | |
363 | bits |= EV_READ; | |
364 | } | |
365 | if((netev.lNetworkEvents & FD_CLOSE)) { | |
366 | if(netev.iErrorCode[FD_CLOSE_BIT] != 0) | |
367 | verbose(VERB_ALGO, "FD_CLOSE_BIT error: %s", | |
368 | wsa_strerror(netev.iErrorCode[FD_CLOSE_BIT])); | |
369 | bits |= EV_READ; | |
370 | bits |= EV_WRITE; | |
371 | } | |
372 | if(eventlist[i]->is_tcp && eventlist[i]->stick_events) { | |
373 | verbose(VERB_ALGO, "winsock %d pass sticky %s%s", | |
374 | eventlist[i]->ev_fd, | |
375 | (eventlist[i]->old_events&EV_READ)?"EV_READ":"", | |
376 | (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":""); | |
377 | bits |= eventlist[i]->old_events; | |
378 | } | |
379 | if(eventlist[i]->is_tcp && bits) { | |
380 | eventlist[i]->old_events = bits; | |
381 | eventlist[i]->stick_events = 1; | |
382 | if((eventlist[i]->ev_events & bits)) { | |
383 | newstickies = 1; | |
384 | } | |
385 | verbose(VERB_ALGO, "winsock %d store sticky %s%s", | |
386 | eventlist[i]->ev_fd, | |
387 | (eventlist[i]->old_events&EV_READ)?"EV_READ":"", | |
388 | (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":""); | |
389 | } | |
390 | if((bits & eventlist[i]->ev_events)) { | |
391 | verbose(VERB_ALGO, "winsock event callback %p fd=%d " | |
392 | "%s%s%s%s%s ; %s%s%s", | |
393 | eventlist[i], eventlist[i]->ev_fd, | |
394 | (netev.lNetworkEvents&FD_READ)?" FD_READ":"", | |
395 | (netev.lNetworkEvents&FD_WRITE)?" FD_WRITE":"", | |
396 | (netev.lNetworkEvents&FD_CONNECT)? | |
397 | " FD_CONNECT":"", | |
398 | (netev.lNetworkEvents&FD_ACCEPT)? | |
399 | " FD_ACCEPT":"", | |
400 | (netev.lNetworkEvents&FD_CLOSE)?" FD_CLOSE":"", | |
401 | (bits&EV_READ)?" EV_READ":"", | |
402 | (bits&EV_WRITE)?" EV_WRITE":"", | |
403 | (bits&EV_TIMEOUT)?" EV_TIMEOUT":""); | |
404 | ||
405 | fptr_ok(fptr_whitelist_event( | |
406 | eventlist[i]->ev_callback)); | |
407 | (*eventlist[i]->ev_callback)(eventlist[i]->ev_fd, | |
408 | bits & eventlist[i]->ev_events, | |
409 | eventlist[i]->ev_arg); | |
410 | } | |
411 | if(eventlist[i]->is_tcp && bits) | |
412 | verbose(VERB_ALGO, "winsock %d got sticky %s%s", | |
413 | eventlist[i]->ev_fd, | |
414 | (eventlist[i]->old_events&EV_READ)?"EV_READ":"", | |
415 | (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":""); | |
416 | } | |
417 | verbose(VERB_CLIENT, "winsock_event net"); | |
418 | if(base->tcp_reinvigorated) { | |
419 | verbose(VERB_CLIENT, "winsock_event reinvigorated"); | |
420 | base->tcp_reinvigorated = 0; | |
421 | newstickies = 1; | |
422 | } | |
423 | base->tcp_stickies = newstickies; | |
424 | verbose(VERB_CLIENT, "winsock_event handle_select end"); | |
425 | return 0; | |
426 | } | |
427 | ||
428 | int event_base_dispatch(struct event_base *base) | |
429 | { | |
430 | struct timeval wait; | |
431 | if(settime(base) < 0) | |
432 | return -1; | |
433 | while(!base->need_to_exit) | |
434 | { | |
435 | /* see if timeouts need handling */ | |
436 | handle_timeouts(base, base->time_tv, &wait); | |
437 | if(base->need_to_exit) | |
438 | return 0; | |
439 | /* do select */ | |
440 | if(handle_select(base, &wait) < 0) { | |
441 | if(base->need_to_exit) | |
442 | return 0; | |
443 | return -1; | |
444 | } | |
445 | } | |
446 | return 0; | |
447 | } | |
448 | ||
449 | int event_base_loopexit(struct event_base *base, | |
450 | struct timeval * ATTR_UNUSED(tv)) | |
451 | { | |
452 | verbose(VERB_CLIENT, "winsock_event loopexit"); | |
453 | base->need_to_exit = 1; | |
454 | return 0; | |
455 | } | |
456 | ||
457 | void event_base_free(struct event_base *base) | |
458 | { | |
459 | verbose(VERB_CLIENT, "winsock_event event_base_free"); | |
460 | if(!base) | |
461 | return; | |
462 | if(base->items) | |
463 | free(base->items); | |
464 | if(base->times) | |
465 | free(base->times); | |
466 | if(base->signals) | |
467 | free(base->signals); | |
468 | free(base); | |
469 | } | |
470 | ||
471 | void event_set(struct event *ev, int fd, short bits, | |
472 | void (*cb)(int, short, void *), void *arg) | |
473 | { | |
474 | ev->node.key = ev; | |
475 | ev->ev_fd = fd; | |
476 | ev->ev_events = bits; | |
477 | ev->ev_callback = cb; | |
478 | fptr_ok(fptr_whitelist_event(ev->ev_callback)); | |
479 | ev->ev_arg = arg; | |
480 | ev->just_checked = 0; | |
481 | ev->added = 0; | |
482 | } | |
483 | ||
484 | int event_base_set(struct event_base *base, struct event *ev) | |
485 | { | |
486 | ev->ev_base = base; | |
487 | ev->old_events = 0; | |
488 | ev->stick_events = 0; | |
489 | ev->added = 0; | |
490 | return 0; | |
491 | } | |
492 | ||
493 | int event_add(struct event *ev, struct timeval *tv) | |
494 | { | |
495 | verbose(VERB_ALGO, "event_add %p added=%d fd=%d tv=" ARG_LL "d %s%s%s", | |
496 | ev, ev->added, ev->ev_fd, | |
497 | (tv?(long long)tv->tv_sec*1000+(long long)tv->tv_usec/1000:-1), | |
498 | (ev->ev_events&EV_READ)?" EV_READ":"", | |
499 | (ev->ev_events&EV_WRITE)?" EV_WRITE":"", | |
500 | (ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":""); | |
501 | if(ev->added) | |
502 | event_del(ev); | |
503 | log_assert(ev->ev_fd==-1 || find_fd(ev->ev_base, ev->ev_fd) == -1); | |
504 | ev->is_tcp = 0; | |
505 | ev->is_signal = 0; | |
506 | ev->just_checked = 0; | |
507 | ||
508 | if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) { | |
509 | BOOL b=0; | |
510 | int t, l; | |
511 | long events = 0; | |
512 | ||
513 | if(ev->ev_base->max == ev->ev_base->cap) | |
514 | return -1; | |
515 | ev->idx = ev->ev_base->max++; | |
516 | ev->ev_base->items[ev->idx] = ev; | |
517 | ||
518 | if( (ev->ev_events&EV_READ) ) | |
519 | events |= FD_READ; | |
520 | if( (ev->ev_events&EV_WRITE) ) | |
521 | events |= FD_WRITE; | |
522 | l = sizeof(t); | |
523 | if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_TYPE, | |
524 | (void*)&t, &l) != 0) | |
525 | log_err("getsockopt(SO_TYPE) failed: %s", | |
526 | wsa_strerror(WSAGetLastError())); | |
527 | if(t == SOCK_STREAM) { | |
528 | /* TCP socket */ | |
529 | ev->is_tcp = 1; | |
530 | events |= FD_CLOSE; | |
531 | if( (ev->ev_events&EV_WRITE) ) | |
532 | events |= FD_CONNECT; | |
533 | l = sizeof(b); | |
534 | if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_ACCEPTCONN, | |
535 | (void*)&b, &l) != 0) | |
536 | log_err("getsockopt(SO_ACCEPTCONN) failed: %s", | |
537 | wsa_strerror(WSAGetLastError())); | |
538 | if(b) /* TCP accept socket */ | |
539 | events |= FD_ACCEPT; | |
540 | } | |
541 | ev->hEvent = WSACreateEvent(); | |
542 | if(ev->hEvent == WSA_INVALID_EVENT) | |
543 | log_err("WSACreateEvent failed: %s", | |
544 | wsa_strerror(WSAGetLastError())); | |
545 | /* automatically sets fd to nonblocking mode. | |
546 | * nonblocking cannot be disabled, until wsaES(fd, NULL, 0) */ | |
547 | if(WSAEventSelect(ev->ev_fd, ev->hEvent, events) != 0) { | |
548 | log_err("WSAEventSelect failed: %s", | |
549 | wsa_strerror(WSAGetLastError())); | |
550 | } | |
551 | if(ev->is_tcp && ev->stick_events && | |
552 | (ev->ev_events & ev->old_events)) { | |
553 | /* go to processing the sticky event right away */ | |
554 | ev->ev_base->tcp_reinvigorated = 1; | |
555 | } | |
556 | } | |
557 | ||
558 | if(tv && (ev->ev_events&EV_TIMEOUT)) { | |
559 | #ifndef S_SPLINT_S | |
560 | struct timeval *now = ev->ev_base->time_tv; | |
561 | ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec; | |
562 | ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec; | |
563 | while(ev->ev_timeout.tv_usec > 1000000) { | |
564 | ev->ev_timeout.tv_usec -= 1000000; | |
565 | ev->ev_timeout.tv_sec++; | |
566 | } | |
567 | #endif | |
568 | (void)rbtree_insert(ev->ev_base->times, &ev->node); | |
569 | } | |
570 | ev->added = 1; | |
571 | return 0; | |
572 | } | |
573 | ||
574 | int event_del(struct event *ev) | |
575 | { | |
576 | verbose(VERB_ALGO, "event_del %p added=%d fd=%d tv=" ARG_LL "d %s%s%s", | |
577 | ev, ev->added, ev->ev_fd, | |
578 | (ev->ev_events&EV_TIMEOUT)?(long long)ev->ev_timeout.tv_sec*1000+ | |
579 | (long long)ev->ev_timeout.tv_usec/1000:-1, | |
580 | (ev->ev_events&EV_READ)?" EV_READ":"", | |
581 | (ev->ev_events&EV_WRITE)?" EV_WRITE":"", | |
582 | (ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":""); | |
583 | if(!ev->added) | |
584 | return 0; | |
585 | log_assert(ev->added); | |
586 | if((ev->ev_events&EV_TIMEOUT)) | |
587 | (void)rbtree_delete(ev->ev_base->times, &ev->node); | |
588 | if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) { | |
589 | log_assert(ev->ev_base->max > 0); | |
590 | /* remove item and compact the list */ | |
591 | ev->ev_base->items[ev->idx] = | |
592 | ev->ev_base->items[ev->ev_base->max-1]; | |
593 | ev->ev_base->items[ev->ev_base->max-1] = NULL; | |
594 | ev->ev_base->max--; | |
595 | if(ev->idx < ev->ev_base->max) | |
596 | ev->ev_base->items[ev->idx]->idx = ev->idx; | |
597 | zero_waitfor(ev->ev_base->waitfor, ev->hEvent); | |
598 | ||
599 | if(WSAEventSelect(ev->ev_fd, ev->hEvent, 0) != 0) | |
600 | log_err("WSAEventSelect(disable) failed: %s", | |
601 | wsa_strerror(WSAGetLastError())); | |
602 | if(!WSACloseEvent(ev->hEvent)) | |
603 | log_err("WSACloseEvent failed: %s", | |
604 | wsa_strerror(WSAGetLastError())); | |
605 | } | |
606 | ev->just_checked = 0; | |
607 | ev->added = 0; | |
608 | return 0; | |
609 | } | |
610 | ||
611 | /** which base gets to handle signals */ | |
612 | static struct event_base* signal_base = NULL; | |
613 | /** signal handler */ | |
614 | static RETSIGTYPE sigh(int sig) | |
615 | { | |
616 | struct event* ev; | |
617 | if(!signal_base || sig < 0 || sig >= MAX_SIG) | |
618 | return; | |
619 | ev = signal_base->signals[sig]; | |
620 | if(!ev) | |
621 | return; | |
622 | fptr_ok(fptr_whitelist_event(ev->ev_callback)); | |
623 | (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg); | |
624 | } | |
625 | ||
626 | int signal_add(struct event *ev, struct timeval * ATTR_UNUSED(tv)) | |
627 | { | |
628 | if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG) | |
629 | return -1; | |
630 | signal_base = ev->ev_base; | |
631 | ev->ev_base->signals[ev->ev_fd] = ev; | |
632 | ev->added = 1; | |
633 | if(signal(ev->ev_fd, sigh) == SIG_ERR) { | |
634 | return -1; | |
635 | } | |
636 | return 0; | |
637 | } | |
638 | ||
639 | int signal_del(struct event *ev) | |
640 | { | |
641 | if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG) | |
642 | return -1; | |
643 | ev->ev_base->signals[ev->ev_fd] = NULL; | |
644 | ev->added = 0; | |
645 | return 0; | |
646 | } | |
647 | ||
648 | void winsock_tcp_wouldblock(struct event* ev, int eventbits) | |
649 | { | |
650 | verbose(VERB_ALGO, "winsock: tcp wouldblock %s", | |
651 | eventbits==EV_READ?"EV_READ":"EV_WRITE"); | |
652 | ev->old_events &= (~eventbits); | |
653 | if(ev->old_events == 0) | |
654 | ev->stick_events = 0; | |
655 | /* in case this is the last sticky event, we could | |
656 | * possibly run an empty handler loop to reset the base | |
657 | * tcp_stickies variable | |
658 | */ | |
659 | } | |
660 | ||
661 | int winsock_register_wsaevent(struct event_base* base, struct event* ev, | |
662 | WSAEVENT wsaevent, void (*cb)(int, short, void*), void* arg) | |
663 | { | |
664 | if(base->max == base->cap) | |
665 | return 0; | |
666 | memset(ev, 0, sizeof(*ev)); | |
667 | ev->ev_fd = -1; | |
668 | ev->ev_events = EV_READ; | |
669 | ev->ev_callback = cb; | |
670 | ev->ev_arg = arg; | |
671 | ev->is_signal = 1; | |
672 | ev->hEvent = wsaevent; | |
673 | ev->added = 1; | |
674 | ev->ev_base = base; | |
675 | ev->idx = ev->ev_base->max++; | |
676 | ev->ev_base->items[ev->idx] = ev; | |
677 | return 1; | |
678 | } | |
679 | ||
680 | void winsock_unregister_wsaevent(struct event* ev) | |
681 | { | |
682 | if(!ev || !ev->added) return; | |
683 | log_assert(ev->added && ev->ev_base->max > 0) | |
684 | /* remove item and compact the list */ | |
685 | ev->ev_base->items[ev->idx] = ev->ev_base->items[ev->ev_base->max-1]; | |
686 | ev->ev_base->items[ev->ev_base->max-1] = NULL; | |
687 | ev->ev_base->max--; | |
688 | if(ev->idx < ev->ev_base->max) | |
689 | ev->ev_base->items[ev->idx]->idx = ev->idx; | |
690 | ev->added = 0; | |
691 | } | |
692 | ||
693 | #else /* USE_WINSOCK */ | |
694 | /** symbol so this codefile defines symbols. pleasing ranlib on OSX 10.5 */ | |
695 | int winsock_unused_symbol = 1; | |
696 | #endif /* USE_WINSOCK */ |