s3-events: make the old timed events compatible with tevent
[ira/wip.git] / source3 / lib / events.c
1 /*
2    Unix SMB/CIFS implementation.
3    Timed event library.
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Volker Lendecke 2005
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 3 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, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include <tevent_internal.h>
23
24 void event_fd_set_writeable(struct tevent_fd *fde)
25 {
26         TEVENT_FD_WRITEABLE(fde);
27 }
28
29 void event_fd_set_not_writeable(struct tevent_fd *fde)
30 {
31         TEVENT_FD_NOT_WRITEABLE(fde);
32 }
33
34 void event_fd_set_readable(struct tevent_fd *fde)
35 {
36         TEVENT_FD_READABLE(fde);
37 }
38
39 void event_fd_set_not_readable(struct tevent_fd *fde)
40 {
41         TEVENT_FD_NOT_READABLE(fde);
42 }
43
44 /*
45  * Return if there's something in the queue
46  */
47
48 bool event_add_to_select_args(struct tevent_context *ev,
49                               const struct timeval *now,
50                               fd_set *read_fds, fd_set *write_fds,
51                               struct timeval *timeout, int *maxfd)
52 {
53         struct tevent_fd *fde;
54         struct timeval diff;
55         bool ret = false;
56
57         for (fde = ev->fd_events; fde; fde = fde->next) {
58                 if (fde->flags & EVENT_FD_READ) {
59                         FD_SET(fde->fd, read_fds);
60                         ret = true;
61                 }
62                 if (fde->flags & EVENT_FD_WRITE) {
63                         FD_SET(fde->fd, write_fds);
64                         ret = true;
65                 }
66
67                 if ((fde->flags & (EVENT_FD_READ|EVENT_FD_WRITE))
68                     && (fde->fd > *maxfd)) {
69                         *maxfd = fde->fd;
70                 }
71         }
72
73         if (ev->immediate_events != NULL) {
74                 *timeout = timeval_zero();
75                 return true;
76         }
77
78         if (ev->timer_events == NULL) {
79                 return ret;
80         }
81
82         diff = timeval_until(now, &ev->timer_events->next_event);
83         *timeout = timeval_min(timeout, &diff);
84
85         return true;
86 }
87
88 bool run_events(struct tevent_context *ev,
89                 int selrtn, fd_set *read_fds, fd_set *write_fds)
90 {
91         struct tevent_fd *fde;
92         struct timeval now;
93
94         if (ev->signal_events &&
95             tevent_common_check_signal(ev)) {
96                 return true;
97         }
98
99         if (ev->immediate_events &&
100             tevent_common_loop_immediate(ev)) {
101                 return true;
102         }
103
104         GetTimeOfDay(&now);
105
106         if ((ev->timer_events != NULL)
107             && (timeval_compare(&now, &ev->timer_events->next_event) >= 0)) {
108                 /* this older events system did not auto-free timed
109                    events on running them, and had a race condition
110                    where the event could be called twice if the
111                    talloc_free of the te happened after the callback
112                    made a call which invoked the event loop. To avoid
113                    this while still allowing old code which frees the
114                    te, we need to create a temporary context which
115                    will be used to ensure the te is freed. We also
116                    remove the te from the timed event list before we
117                    call the handler, to ensure we can't loop */
118
119                 struct tevent_timer *te = ev->timer_events;
120                 TALLOC_CTX *tmp_ctx = talloc_new(ev);
121
122                 DEBUG(10, ("Running timed event \"%s\" %p\n",
123                            ev->timer_events->handler_name, ev->timer_events));
124
125                 DLIST_REMOVE(ev->timer_events, te);
126                 talloc_steal(tmp_ctx, te);
127
128                 te->handler(ev, te, now, te->private_data);
129
130                 talloc_free(tmp_ctx);
131                 return true;
132         }
133
134         if (selrtn == 0) {
135                 /*
136                  * No fd ready
137                  */
138                 return false;
139         }
140
141         for (fde = ev->fd_events; fde; fde = fde->next) {
142                 uint16 flags = 0;
143
144                 if (FD_ISSET(fde->fd, read_fds)) flags |= EVENT_FD_READ;
145                 if (FD_ISSET(fde->fd, write_fds)) flags |= EVENT_FD_WRITE;
146
147                 if (flags & fde->flags) {
148                         fde->handler(ev, fde, flags, fde->private_data);
149                         return true;
150                 }
151         }
152
153         return false;
154 }
155
156
157 struct timeval *get_timed_events_timeout(struct tevent_context *ev,
158                                          struct timeval *to_ret)
159 {
160         struct timeval now;
161
162         if ((ev->timer_events == NULL) && (ev->immediate_events == NULL)) {
163                 return NULL;
164         }
165         if (ev->immediate_events != NULL) {
166                 *to_ret = timeval_zero();
167                 return to_ret;
168         }
169
170         now = timeval_current();
171         *to_ret = timeval_until(&now, &ev->timer_events->next_event);
172
173         DEBUG(10, ("timed_events_timeout: %d/%d\n", (int)to_ret->tv_sec,
174                 (int)to_ret->tv_usec));
175
176         return to_ret;
177 }
178
179 static int s3_event_loop_once(struct tevent_context *ev, const char *location)
180 {
181         struct timeval now, to;
182         fd_set r_fds, w_fds;
183         int maxfd = 0;
184         int ret;
185
186         FD_ZERO(&r_fds);
187         FD_ZERO(&w_fds);
188
189         to.tv_sec = 9999;       /* Max timeout */
190         to.tv_usec = 0;
191
192         if (run_events(ev, 0, NULL, NULL)) {
193                 return 0;
194         }
195
196         GetTimeOfDay(&now);
197
198         if (!event_add_to_select_args(ev, &now, &r_fds, &w_fds, &to, &maxfd)) {
199                 return -1;
200         }
201
202         ret = sys_select(maxfd+1, &r_fds, &w_fds, NULL, &to);
203
204         if (ret == -1 && errno != EINTR) {
205                 tevent_debug(ev, TEVENT_DEBUG_FATAL,
206                              "sys_select() failed: %d:%s\n",
207                              errno, strerror(errno));
208                 return -1;
209         }
210
211         run_events(ev, ret, &r_fds, &w_fds);
212         return 0;
213 }
214
215 void event_context_reinit(struct tevent_context *ev)
216 {
217         tevent_common_context_destructor(ev);
218         return;
219 }
220
221 static int s3_event_context_init(struct tevent_context *ev)
222 {
223         return 0;
224 }
225
226 void dump_event_list(struct tevent_context *ev)
227 {
228         struct tevent_timer *te;
229         struct tevent_fd *fe;
230         struct timeval evt, now;
231
232         if (!ev) {
233                 return;
234         }
235
236         now = timeval_current();
237
238         DEBUG(10,("dump_event_list:\n"));
239
240         for (te = ev->timer_events; te; te = te->next) {
241
242                 evt = timeval_until(&now, &te->next_event);
243
244                 DEBUGADD(10,("Timed Event \"%s\" %p handled in %d seconds (at %s)\n",
245                            te->handler_name,
246                            te,
247                            (int)evt.tv_sec,
248                            http_timestring(talloc_tos(), te->next_event.tv_sec)));
249         }
250
251         for (fe = ev->fd_events; fe; fe = fe->next) {
252
253                 DEBUGADD(10,("FD Event %d %p, flags: 0x%04x\n",
254                            fe->fd,
255                            fe,
256                            fe->flags));
257         }
258 }
259
260 static const struct tevent_ops s3_event_ops = {
261         .context_init           = s3_event_context_init,
262         .add_fd                 = tevent_common_add_fd,
263         .set_fd_close_fn        = tevent_common_fd_set_close_fn,
264         .get_fd_flags           = tevent_common_fd_get_flags,
265         .set_fd_flags           = tevent_common_fd_set_flags,
266         .add_timer              = tevent_common_add_timer,
267         .schedule_immediate     = tevent_common_schedule_immediate,
268         .add_signal             = tevent_common_add_signal,
269         .loop_once              = s3_event_loop_once,
270         .loop_wait              = tevent_common_loop_wait,
271 };
272
273 static bool s3_tevent_init(void)
274 {
275         static bool initialized;
276         if (initialized) {
277                 return true;
278         }
279         initialized = tevent_register_backend("s3", &s3_event_ops);
280         tevent_set_default_backend("s3");
281         return initialized;
282 }
283
284 /*
285   this is used to catch debug messages from events
286 */
287 static void s3_event_debug(void *context, enum tevent_debug_level level,
288                            const char *fmt, va_list ap)  PRINTF_ATTRIBUTE(3,0);
289
290 static void s3_event_debug(void *context, enum tevent_debug_level level,
291                            const char *fmt, va_list ap)
292 {
293         int samba_level = -1;
294         char *s = NULL;
295         switch (level) {
296         case TEVENT_DEBUG_FATAL:
297                 samba_level = 0;
298                 break;
299         case TEVENT_DEBUG_ERROR:
300                 samba_level = 1;
301                 break;
302         case TEVENT_DEBUG_WARNING:
303                 samba_level = 2;
304                 break;
305         case TEVENT_DEBUG_TRACE:
306                 samba_level = 11;
307                 break;
308
309         };
310         if (vasprintf(&s, fmt, ap) == -1) {
311                 return;
312         }
313         DEBUG(samba_level, ("s3_event: %s", s));
314         free(s);
315 }
316
317 struct tevent_context *s3_tevent_context_init(TALLOC_CTX *mem_ctx)
318 {
319         struct tevent_context *ev;
320
321         s3_tevent_init();
322
323         ev = tevent_context_init_byname(mem_ctx, "s3");
324         if (ev) {
325                 tevent_set_debug(ev, s3_event_debug, NULL);
326         }
327
328         return ev;
329 }
330