r23779: Change from v2 or later to v3 or later.
[tprouty/samba.git] / source / 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 struct timed_event {
25         struct timed_event *next, *prev;
26         struct event_context *event_ctx;
27         struct timeval when;
28         const char *event_name;
29         void (*handler)(struct event_context *event_ctx,
30                         struct timed_event *te,
31                         const struct timeval *now,
32                         void *private_data);
33         void *private_data;
34 };
35
36 struct fd_event {
37         struct fd_event *prev, *next;
38         struct event_context *event_ctx;
39         int fd;
40         uint16_t flags; /* see EVENT_FD_* flags */
41         void (*handler)(struct event_context *event_ctx,
42                         struct fd_event *event,
43                         uint16 flags,
44                         void *private_data);
45         void *private_data;
46 };
47
48 #define EVENT_FD_WRITEABLE(fde) \
49         event_set_fd_flags(fde, event_get_fd_flags(fde) | EVENT_FD_WRITE)
50 #define EVENT_FD_READABLE(fde) \
51         event_set_fd_flags(fde, event_get_fd_flags(fde) | EVENT_FD_READ)
52
53 #define EVENT_FD_NOT_WRITEABLE(fde) \
54         event_set_fd_flags(fde, event_get_fd_flags(fde) & ~EVENT_FD_WRITE)
55 #define EVENT_FD_NOT_READABLE(fde) \
56         event_set_fd_flags(fde, event_get_fd_flags(fde) & ~EVENT_FD_READ)
57
58 struct event_context {
59         struct timed_event *timed_events;
60         struct fd_event *fd_events;
61 };
62
63 static int timed_event_destructor(struct timed_event *te)
64 {
65         DEBUG(10, ("Destroying timed event %lx \"%s\"\n", (unsigned long)te,
66                 te->event_name));
67         DLIST_REMOVE(te->event_ctx->timed_events, te);
68         return 0;
69 }
70
71 /****************************************************************************
72  Add te by time.
73 ****************************************************************************/
74
75 static void add_event_by_time(struct timed_event *te)
76 {
77         struct event_context *ctx = te->event_ctx;
78         struct timed_event *last_te, *cur_te;
79
80         /* Keep the list ordered by time. We must preserve this. */
81         last_te = NULL;
82         for (cur_te = ctx->timed_events; cur_te; cur_te = cur_te->next) {
83                 /* if the new event comes before the current one break */
84                 if (!timeval_is_zero(&cur_te->when) &&
85                                 timeval_compare(&te->when, &cur_te->when) < 0) {
86                         break;
87                 }
88                 last_te = cur_te;
89         }
90
91         DLIST_ADD_AFTER(ctx->timed_events, te, last_te);
92 }
93
94 /****************************************************************************
95  Schedule a function for future calling, cancel with TALLOC_FREE().
96  It's the responsibility of the handler to call TALLOC_FREE() on the event
97  handed to it.
98 ****************************************************************************/
99
100 struct timed_event *event_add_timed(struct event_context *event_ctx,
101                                 TALLOC_CTX *mem_ctx,
102                                 struct timeval when,
103                                 const char *event_name,
104                                 void (*handler)(struct event_context *event_ctx,
105                                                 struct timed_event *te,
106                                                 const struct timeval *now,
107                                                 void *private_data),
108                                 void *private_data)
109 {
110         struct timed_event *te;
111
112         te = TALLOC_P(mem_ctx, struct timed_event);
113         if (te == NULL) {
114                 DEBUG(0, ("talloc failed\n"));
115                 return NULL;
116         }
117
118         te->event_ctx = event_ctx;
119         te->when = when;
120         te->event_name = event_name;
121         te->handler = handler;
122         te->private_data = private_data;
123
124         add_event_by_time(te);
125
126         talloc_set_destructor(te, timed_event_destructor);
127
128         DEBUG(10, ("Added timed event \"%s\": %lx\n", event_name,
129                         (unsigned long)te));
130         return te;
131 }
132
133 static int fd_event_destructor(struct fd_event *fde)
134 {
135         struct event_context *event_ctx = fde->event_ctx;
136
137         DLIST_REMOVE(event_ctx->fd_events, fde);
138         return 0;
139 }
140
141 struct fd_event *event_add_fd(struct event_context *event_ctx,
142                               TALLOC_CTX *mem_ctx,
143                               int fd, uint16_t flags,
144                               void (*handler)(struct event_context *event_ctx,
145                                               struct fd_event *event,
146                                               uint16 flags,
147                                               void *private_data),
148                               void *private_data)
149 {
150         struct fd_event *fde;
151
152         if (!(fde = TALLOC_P(mem_ctx, struct fd_event))) {
153                 return NULL;
154         }
155
156         fde->event_ctx = event_ctx;
157         fde->fd = fd;
158         fde->flags = flags;
159         fde->handler = handler;
160         fde->private_data = private_data;
161
162         DLIST_ADD(event_ctx->fd_events, fde);
163
164         talloc_set_destructor(fde, fd_event_destructor);
165         return fde;
166 }
167
168 void event_fd_set_writeable(struct fd_event *fde)
169 {
170         fde->flags |= EVENT_FD_WRITE;
171 }
172
173 void event_fd_set_not_writeable(struct fd_event *fde)
174 {
175         fde->flags &= ~EVENT_FD_WRITE;
176 }
177
178 void event_fd_set_readable(struct fd_event *fde)
179 {
180         fde->flags |= EVENT_FD_READ;
181 }
182
183 void event_fd_set_not_readable(struct fd_event *fde)
184 {
185         fde->flags &= ~EVENT_FD_READ;
186 }
187
188 /*
189  * Return if there's something in the queue
190  */
191
192 BOOL event_add_to_select_args(struct event_context *event_ctx,
193                               const struct timeval *now,
194                               fd_set *read_fds, fd_set *write_fds,
195                               struct timeval *timeout, int *maxfd)
196 {
197         struct fd_event *fde;
198         struct timeval diff;
199         BOOL ret = False;
200
201         for (fde = event_ctx->fd_events; fde; fde = fde->next) {
202                 if (fde->flags & EVENT_FD_READ) {
203                         FD_SET(fde->fd, read_fds);
204                         ret = True;
205                 }
206                 if (fde->flags & EVENT_FD_WRITE) {
207                         FD_SET(fde->fd, write_fds);
208                         ret = True;
209                 }
210
211                 if ((fde->flags & (EVENT_FD_READ|EVENT_FD_WRITE))
212                     && (fde->fd > *maxfd)) {
213                         *maxfd = fde->fd;
214                 }
215         }
216
217         if (event_ctx->timed_events == NULL) {
218                 return ret;
219         }
220
221         diff = timeval_until(now, &event_ctx->timed_events->when);
222         *timeout = timeval_min(timeout, &diff);
223
224         return True;
225 }
226
227 BOOL events_pending(struct event_context *event_ctx)
228 {
229         struct fd_event *fde;
230
231         if (event_ctx->timed_events != NULL) {
232                 return True;
233         }
234         for (fde = event_ctx->fd_events; fde; fde = fde->next) {
235                 if (fde->flags & (EVENT_FD_READ|EVENT_FD_WRITE)) {
236                         return True;
237                 }
238         }
239         return False;
240 }
241
242 BOOL run_events(struct event_context *event_ctx,
243                 int selrtn, fd_set *read_fds, fd_set *write_fds)
244 {
245         BOOL fired = False;
246         struct fd_event *fde, *next;
247
248         /* Run all events that are pending, not just one (as we
249            did previously. */
250
251         while (event_ctx->timed_events) {
252                 struct timeval now;
253                 GetTimeOfDay(&now);
254
255                 if (timeval_compare(
256                             &now, &event_ctx->timed_events->when) < 0) {
257                         /* Nothing to do yet */
258                         DEBUG(11, ("run_events: Nothing to do\n"));
259                         break;
260                 }
261
262                 DEBUG(10, ("Running event \"%s\" %lx\n",
263                            event_ctx->timed_events->event_name,
264                            (unsigned long)event_ctx->timed_events));
265
266                 event_ctx->timed_events->handler(
267                         event_ctx,
268                         event_ctx->timed_events, &now,
269                         event_ctx->timed_events->private_data);
270
271                 fired = True;
272         }
273
274         if (fired) {
275                 /*
276                  * We might have changed the socket status during the timed
277                  * events, return to run select again.
278                  */
279                 return True;
280         }
281
282         if (selrtn == 0) {
283                 /*
284                  * No fd ready
285                  */
286                 return fired;
287         }
288
289         for (fde = event_ctx->fd_events; fde; fde = next) {
290                 uint16 flags = 0;
291
292                 next = fde->next;
293                 if (FD_ISSET(fde->fd, read_fds)) flags |= EVENT_FD_READ;
294                 if (FD_ISSET(fde->fd, write_fds)) flags |= EVENT_FD_WRITE;
295
296                 if (flags) {
297                         fde->handler(event_ctx, fde, flags, fde->private_data);
298                         fired = True;
299                 }
300         }
301
302         return fired;
303 }
304
305
306 struct timeval *get_timed_events_timeout(struct event_context *event_ctx,
307                                          struct timeval *to_ret)
308 {
309         struct timeval now;
310
311         if (event_ctx->timed_events == NULL) {
312                 return NULL;
313         }
314
315         now = timeval_current();
316         *to_ret = timeval_until(&now, &event_ctx->timed_events->when);
317
318         DEBUG(10, ("timed_events_timeout: %d/%d\n", (int)to_ret->tv_sec,
319                 (int)to_ret->tv_usec));
320
321         return to_ret;
322 }
323
324 int event_loop_once(struct event_context *ev)
325 {
326         struct timeval now, to;
327         fd_set r_fds, w_fds;
328         int maxfd = 0;
329         int ret;
330
331         FD_ZERO(&r_fds);
332         FD_ZERO(&w_fds);
333
334         to.tv_sec = 9999;       /* Max timeout */
335         to.tv_usec = 0;
336
337         GetTimeOfDay(&now);
338
339         if (!event_add_to_select_args(ev, &now, &r_fds, &w_fds, &to, &maxfd)) {
340                 return -1;
341         }
342
343         if (timeval_is_zero(&to)) {
344                 run_events(ev, 0, NULL, NULL);
345                 return 0;
346         }
347
348         ret = sys_select(maxfd, &r_fds, &w_fds, NULL, &to);
349
350         if (ret == -1 && errno != EINTR) {
351                 return -1;
352         }
353
354         run_events(ev, ret, &r_fds, &w_fds);
355         return 0;
356 }
357
358 struct event_context *event_context_init(TALLOC_CTX *mem_ctx)
359 {
360         return TALLOC_ZERO_P(NULL, struct event_context);
361 }
362
363 int set_event_dispatch_time(struct event_context *event_ctx,
364                             const char *event_name, struct timeval when)
365 {
366         struct timed_event *te;
367
368         for (te = event_ctx->timed_events; te; te = te->next) {
369                 if (strcmp(event_name, te->event_name) == 0) {
370                         DLIST_REMOVE(event_ctx->timed_events, te);
371                         te->when = when;
372                         add_event_by_time(te);
373                         return 1;
374                 }
375         }
376         return 0;
377 }
378
379 /* Returns 1 if event was found and cancelled, 0 otherwise. */
380
381 int cancel_named_event(struct event_context *event_ctx,
382                        const char *event_name)
383 {
384         struct timed_event *te;
385
386         for (te = event_ctx->timed_events; te; te = te->next) {
387                 if (strcmp(event_name, te->event_name) == 0) {
388                         TALLOC_FREE(te);
389                         return 1;
390                 }
391         }
392         return 0;
393 }
394
395 void dump_event_list(struct event_context *event_ctx)
396 {
397         struct timed_event *te;
398         struct fd_event *fe;
399         struct timeval evt, now;
400
401         if (!event_ctx) {
402                 return;
403         }
404
405         now = timeval_current();
406
407         DEBUG(10,("dump_event_list:\n"));
408
409         for (te = event_ctx->timed_events; te; te = te->next) {
410
411                 evt = timeval_until(&now, &te->when);
412
413                 DEBUGADD(10,("Timed Event \"%s\" %lx handled in %d seconds\n",
414                            te->event_name,
415                            (unsigned long)te,
416                            (int)evt.tv_sec));
417         }
418
419         for (fe = event_ctx->fd_events; fe; fe = fe->next) {
420
421                 DEBUGADD(10,("FD Event %d %lx, flags: 0x%04x\n",
422                            fe->fd,
423                            (unsigned long)fe,
424                            fe->flags));
425         }
426 }