tevent: Add an internal function tevent_epoll_set_panic_fallback().
[metze/samba/wip.git] / lib / tevent / tevent_epoll.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    main select loop and event handling - epoll implementation
5
6    Copyright (C) Andrew Tridgell        2003-2005
7    Copyright (C) Stefan Metzmacher      2005-2009
8
9      ** NOTE! The following LGPL license applies to the tevent
10      ** library. This does NOT imply that all of Samba is released
11      ** under the LGPL
12
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 3 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "replace.h"
28 #include "system/filesys.h"
29 #include "system/select.h"
30 #include "tevent.h"
31 #include "tevent_internal.h"
32 #include "tevent_util.h"
33
34 struct epoll_event_context {
35         /* a pointer back to the generic event_context */
36         struct tevent_context *ev;
37
38         /* when using epoll this is the handle from epoll_create */
39         int epoll_fd;
40
41         pid_t pid;
42
43         bool (*panic_fallback)(struct tevent_context *ev, bool replay);
44 };
45
46 /*
47   called to set the panic fallback function.
48 */
49 _PRIVATE_ bool tevent_epoll_set_panic_fallback(struct tevent_context *ev,
50                                 bool (*panic_fallback)(struct tevent_context *ev,
51                                                        bool replay))
52 {
53         struct epoll_event_context *epoll_ev;
54
55         if (ev->additional_data == NULL) {
56                 return false;
57         }
58
59         epoll_ev = talloc_get_type(ev->additional_data,
60                                 struct epoll_event_context);
61         if (epoll_ev == NULL) {
62                 return false;
63         }
64         epoll_ev->panic_fallback = panic_fallback;
65         return true;
66 }
67
68 /*
69   called when a epoll call fails
70 */
71 static void epoll_panic(struct epoll_event_context *epoll_ev,
72                         const char *reason, bool replay)
73 {
74         tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
75                  "%s (%s) - calling abort()\n", reason, strerror(errno));
76         abort();
77 }
78
79 /*
80   map from TEVENT_FD_* to EPOLLIN/EPOLLOUT
81 */
82 static uint32_t epoll_map_flags(uint16_t flags)
83 {
84         uint32_t ret = 0;
85         if (flags & TEVENT_FD_READ) ret |= (EPOLLIN | EPOLLERR | EPOLLHUP);
86         if (flags & TEVENT_FD_WRITE) ret |= (EPOLLOUT | EPOLLERR | EPOLLHUP);
87         return ret;
88 }
89
90 /*
91  free the epoll fd
92 */
93 static int epoll_ctx_destructor(struct epoll_event_context *epoll_ev)
94 {
95         close(epoll_ev->epoll_fd);
96         epoll_ev->epoll_fd = -1;
97         return 0;
98 }
99
100 /*
101  init the epoll fd
102 */
103 static int epoll_init_ctx(struct epoll_event_context *epoll_ev)
104 {
105         epoll_ev->epoll_fd = epoll_create(64);
106         if (epoll_ev->epoll_fd == -1) {
107                 tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
108                              "Failed to create epoll handle.\n");
109                 return -1;
110         }
111
112         if (!ev_set_close_on_exec(epoll_ev->epoll_fd)) {
113                 tevent_debug(epoll_ev->ev, TEVENT_DEBUG_WARNING,
114                              "Failed to set close-on-exec, file descriptor may be leaked to children.\n");
115         }
116
117         epoll_ev->pid = getpid();
118         talloc_set_destructor(epoll_ev, epoll_ctx_destructor);
119
120         return 0;
121 }
122
123 static void epoll_add_event(struct epoll_event_context *epoll_ev, struct tevent_fd *fde);
124
125 /*
126   reopen the epoll handle when our pid changes
127   see http://junkcode.samba.org/ftp/unpacked/junkcode/epoll_fork.c for an 
128   demonstration of why this is needed
129  */
130 static void epoll_check_reopen(struct epoll_event_context *epoll_ev)
131 {
132         struct tevent_fd *fde;
133
134         if (epoll_ev->pid == getpid()) {
135                 return;
136         }
137
138         close(epoll_ev->epoll_fd);
139         epoll_ev->epoll_fd = epoll_create(64);
140         if (epoll_ev->epoll_fd == -1) {
141                 tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
142                              "Failed to recreate epoll handle after fork\n");
143                 return;
144         }
145
146         if (!ev_set_close_on_exec(epoll_ev->epoll_fd)) {
147                 tevent_debug(epoll_ev->ev, TEVENT_DEBUG_WARNING,
148                              "Failed to set close-on-exec, file descriptor may be leaked to children.\n");
149         }
150
151         epoll_ev->pid = getpid();
152         for (fde=epoll_ev->ev->fd_events;fde;fde=fde->next) {
153                 epoll_add_event(epoll_ev, fde);
154         }
155 }
156
157 #define EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT      (1<<0)
158 #define EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR   (1<<1)
159 #define EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR      (1<<2)
160
161 /*
162  add the epoll event to the given fd_event
163 */
164 static void epoll_add_event(struct epoll_event_context *epoll_ev, struct tevent_fd *fde)
165 {
166         struct epoll_event event;
167
168         if (epoll_ev->epoll_fd == -1) return;
169
170         fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
171
172         /* if we don't want events yet, don't add an epoll_event */
173         if (fde->flags == 0) return;
174
175         ZERO_STRUCT(event);
176         event.events = epoll_map_flags(fde->flags);
177         event.data.ptr = fde;
178         if (epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_ADD, fde->fd, &event) != 0) {
179                 epoll_panic(epoll_ev, "EPOLL_CTL_ADD failed", false);
180                 return;
181         }
182         fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT;
183
184         /* only if we want to read we want to tell the event handler about errors */
185         if (fde->flags & TEVENT_FD_READ) {
186                 fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
187         }
188 }
189
190 /*
191  delete the epoll event for given fd_event
192 */
193 static void epoll_del_event(struct epoll_event_context *epoll_ev, struct tevent_fd *fde)
194 {
195         struct epoll_event event;
196
197         if (epoll_ev->epoll_fd == -1) return;
198
199         fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
200
201         /* if there's no epoll_event, we don't need to delete it */
202         if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT)) return;
203
204         ZERO_STRUCT(event);
205         event.events = epoll_map_flags(fde->flags);
206         event.data.ptr = fde;
207         if (epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_DEL, fde->fd, &event) != 0) {
208                 tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
209                              "epoll_del_event failed! probable early close bug (%s)\n",
210                              strerror(errno));
211         }
212         fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT;
213 }
214
215 /*
216  change the epoll event to the given fd_event
217 */
218 static void epoll_mod_event(struct epoll_event_context *epoll_ev, struct tevent_fd *fde)
219 {
220         struct epoll_event event;
221         if (epoll_ev->epoll_fd == -1) return;
222
223         fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
224
225         ZERO_STRUCT(event);
226         event.events = epoll_map_flags(fde->flags);
227         event.data.ptr = fde;
228         if (epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) {
229                 epoll_panic(epoll_ev, "EPOLL_CTL_MOD failed", false);
230                 return;
231         }
232
233         /* only if we want to read we want to tell the event handler about errors */
234         if (fde->flags & TEVENT_FD_READ) {
235                 fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
236         }
237 }
238
239 static void epoll_change_event(struct epoll_event_context *epoll_ev, struct tevent_fd *fde)
240 {
241         bool got_error = (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR);
242         bool want_read = (fde->flags & TEVENT_FD_READ);
243         bool want_write= (fde->flags & TEVENT_FD_WRITE);
244
245         if (epoll_ev->epoll_fd == -1) return;
246
247         fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR;
248
249         /* there's already an event */
250         if (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT) {
251                 if (want_read || (want_write && !got_error)) {
252                         epoll_mod_event(epoll_ev, fde);
253                         return;
254                 }
255                 /* 
256                  * if we want to match the select behavior, we need to remove the epoll_event
257                  * when the caller isn't interested in events.
258                  *
259                  * this is because epoll reports EPOLLERR and EPOLLHUP, even without asking for them
260                  */
261                 epoll_del_event(epoll_ev, fde);
262                 return;
263         }
264
265         /* there's no epoll_event attached to the fde */
266         if (want_read || (want_write && !got_error)) {
267                 epoll_add_event(epoll_ev, fde);
268                 return;
269         }
270 }
271
272 /*
273   event loop handling using epoll
274 */
275 static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval *tvalp)
276 {
277         int ret, i;
278 #define MAXEVENTS 1
279         struct epoll_event events[MAXEVENTS];
280         int timeout = -1;
281
282         if (epoll_ev->epoll_fd == -1) return -1;
283
284         if (tvalp) {
285                 /* it's better to trigger timed events a bit later than to early */
286                 timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000);
287         }
288
289         if (epoll_ev->ev->signal_events &&
290             tevent_common_check_signal(epoll_ev->ev)) {
291                 return 0;
292         }
293
294         tevent_trace_point_callback(epoll_ev->ev, TEVENT_TRACE_BEFORE_WAIT);
295         ret = epoll_wait(epoll_ev->epoll_fd, events, MAXEVENTS, timeout);
296         tevent_trace_point_callback(epoll_ev->ev, TEVENT_TRACE_AFTER_WAIT);
297
298         if (ret == -1 && errno == EINTR && epoll_ev->ev->signal_events) {
299                 if (tevent_common_check_signal(epoll_ev->ev)) {
300                         return 0;
301                 }
302         }
303
304         if (ret == -1 && errno != EINTR) {
305                 epoll_panic(epoll_ev, "epoll_wait() failed", true);
306                 return -1;
307         }
308
309         if (ret == 0 && tvalp) {
310                 /* we don't care about a possible delay here */
311                 tevent_common_loop_timer_delay(epoll_ev->ev);
312                 return 0;
313         }
314
315         for (i=0;i<ret;i++) {
316                 struct tevent_fd *fde = talloc_get_type(events[i].data.ptr, 
317                                                        struct tevent_fd);
318                 uint16_t flags = 0;
319
320                 if (fde == NULL) {
321                         epoll_panic(epoll_ev, "epoll_wait() gave bad data", true);
322                         return -1;
323                 }
324                 if (events[i].events & (EPOLLHUP|EPOLLERR)) {
325                         fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR;
326                         /*
327                          * if we only wait for TEVENT_FD_WRITE, we should not tell the
328                          * event handler about it, and remove the epoll_event,
329                          * as we only report errors when waiting for read events,
330                          * to match the select() behavior
331                          */
332                         if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR)) {
333                                 epoll_del_event(epoll_ev, fde);
334                                 continue;
335                         }
336                         flags |= TEVENT_FD_READ;
337                 }
338                 if (events[i].events & EPOLLIN) flags |= TEVENT_FD_READ;
339                 if (events[i].events & EPOLLOUT) flags |= TEVENT_FD_WRITE;
340                 if (flags) {
341                         fde->handler(epoll_ev->ev, fde, flags, fde->private_data);
342                         break;
343                 }
344         }
345
346         return 0;
347 }
348
349 /*
350   create a epoll_event_context structure.
351 */
352 static int epoll_event_context_init(struct tevent_context *ev)
353 {
354         int ret;
355         struct epoll_event_context *epoll_ev;
356
357         epoll_ev = talloc_zero(ev, struct epoll_event_context);
358         if (!epoll_ev) return -1;
359         epoll_ev->ev = ev;
360         epoll_ev->epoll_fd = -1;
361
362         ret = epoll_init_ctx(epoll_ev);
363         if (ret != 0) {
364                 talloc_free(epoll_ev);
365                 return ret;
366         }
367
368         ev->additional_data = epoll_ev;
369         return 0;
370 }
371
372 /*
373   destroy an fd_event
374 */
375 static int epoll_event_fd_destructor(struct tevent_fd *fde)
376 {
377         struct tevent_context *ev = fde->event_ctx;
378         struct epoll_event_context *epoll_ev = NULL;
379
380         if (ev) {
381                 epoll_ev = talloc_get_type(ev->additional_data,
382                                            struct epoll_event_context);
383
384                 epoll_check_reopen(epoll_ev);
385
386                 epoll_del_event(epoll_ev, fde);
387         }
388
389         return tevent_common_fd_destructor(fde);
390 }
391
392 /*
393   add a fd based event
394   return NULL on failure (memory allocation error)
395 */
396 static struct tevent_fd *epoll_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
397                                             int fd, uint16_t flags,
398                                             tevent_fd_handler_t handler,
399                                             void *private_data,
400                                             const char *handler_name,
401                                             const char *location)
402 {
403         struct epoll_event_context *epoll_ev = talloc_get_type(ev->additional_data,
404                                                            struct epoll_event_context);
405         struct tevent_fd *fde;
406
407         epoll_check_reopen(epoll_ev);
408
409         fde = tevent_common_add_fd(ev, mem_ctx, fd, flags,
410                                    handler, private_data,
411                                    handler_name, location);
412         if (!fde) return NULL;
413
414         talloc_set_destructor(fde, epoll_event_fd_destructor);
415
416         epoll_add_event(epoll_ev, fde);
417
418         return fde;
419 }
420
421 /*
422   set the fd event flags
423 */
424 static void epoll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
425 {
426         struct tevent_context *ev;
427         struct epoll_event_context *epoll_ev;
428
429         if (fde->flags == flags) return;
430
431         ev = fde->event_ctx;
432         epoll_ev = talloc_get_type(ev->additional_data, struct epoll_event_context);
433
434         fde->flags = flags;
435
436         epoll_check_reopen(epoll_ev);
437
438         epoll_change_event(epoll_ev, fde);
439 }
440
441 /*
442   do a single event loop using the events defined in ev 
443 */
444 static int epoll_event_loop_once(struct tevent_context *ev, const char *location)
445 {
446         struct epoll_event_context *epoll_ev = talloc_get_type(ev->additional_data,
447                                                            struct epoll_event_context);
448         struct timeval tval;
449
450         if (ev->signal_events &&
451             tevent_common_check_signal(ev)) {
452                 return 0;
453         }
454
455         if (ev->immediate_events &&
456             tevent_common_loop_immediate(ev)) {
457                 return 0;
458         }
459
460         tval = tevent_common_loop_timer_delay(ev);
461         if (tevent_timeval_is_zero(&tval)) {
462                 return 0;
463         }
464
465         epoll_check_reopen(epoll_ev);
466
467         return epoll_event_loop(epoll_ev, &tval);
468 }
469
470 static const struct tevent_ops epoll_event_ops = {
471         .context_init           = epoll_event_context_init,
472         .add_fd                 = epoll_event_add_fd,
473         .set_fd_close_fn        = tevent_common_fd_set_close_fn,
474         .get_fd_flags           = tevent_common_fd_get_flags,
475         .set_fd_flags           = epoll_event_set_fd_flags,
476         .add_timer              = tevent_common_add_timer,
477         .schedule_immediate     = tevent_common_schedule_immediate,
478         .add_signal             = tevent_common_add_signal,
479         .loop_once              = epoll_event_loop_once,
480         .loop_wait              = tevent_common_loop_wait,
481 };
482
483 _PRIVATE_ bool tevent_epoll_init(void)
484 {
485         return tevent_register_backend("epoll", &epoll_event_ops);
486 }