r174: Win95 registry files (like USER.DAT) can now be partially parsed
[kai/samba.git] / source / lib / events.c
1 /* 
2    Unix SMB/CIFS implementation.
3    main select loop and event handling
4    Copyright (C) Andrew Tridgell 2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22   PLEASE READ THIS BEFORE MODIFYING!
23
24   This module is a general abstraction for the main select loop and
25   event handling. Do not ever put any localised hacks in here, instead
26   register one of the possible event types and implement that event
27   somewhere else.
28
29   There are 4 types of event handling that are handled in this module:
30
31   1) a file descriptor becoming readable or writeable. This is mostly
32      used for network sockets, but can be used for any type of file
33      descriptor. You may only register one handler for each file
34      descriptor/io combination or you will get unpredictable results
35      (this means that you can have a handler for read events, and a
36      separate handler for write events, but not two handlers that are
37      both handling read events)
38
39   2) a timed event. You can register an event that happens at a
40      specific time.  You can register as many of these as you
41      like. When they are called the handler can choose to set the time
42      for the next event. If next_event is not set then the event is removed.
43
44   3) an event that happens every time through the select loop. These
45      sorts of events should be very fast, as they will occur a
46      lot. Mostly used for things like destroying a talloc context or
47      checking a signal flag.
48
49   4) an event triggered by a signal. These can be one shot or
50      repeated. You can have more than one handler registered for a
51      single signal if you want to.
52
53   To setup a set of events you first need to create a event_context
54   structure using the function event_context_init(); This returns a
55   'struct event_context' that you use in all subsequent calls.
56
57   After that you can add/remove events that you are interested in
58   using event_add_*() and event_remove_*().
59
60   Finally, you call event_loop_wait() to block waiting for one of the
61   events to occor. In normal operation event_loop_wait() will loop
62   forever, unless you call event_loop_exit() from inside one of your
63   handler functions.
64
65 */
66
67 #include "includes.h"
68
69 /*
70   create a event_context structure. This must be the first events
71   call, and all subsequent calls pass this event_context as the first
72   element. Event handlers also receive this as their first argument.
73 */
74 struct event_context *event_context_init(void)
75 {
76         struct event_context *ev;
77
78         ev = malloc(sizeof(*ev));
79         if (!ev) return NULL;
80
81         /* start off with no events */
82         ZERO_STRUCTP(ev);
83
84         return ev;
85 }
86
87
88
89 /*
90   add a fd based event
91   return NULL on failure (memory allocation error)
92 */
93 struct fd_event *event_add_fd(struct event_context *ev, struct fd_event *e) 
94 {
95         e = memdup(e, sizeof(*e));
96         if (!e) return NULL;
97         DLIST_ADD(ev->fd_events, e);
98         e->ref_count = 1;
99         if (e->fd > ev->maxfd) {
100                 ev->maxfd = e->fd;
101         }
102         return e;
103 }
104
105
106 /*
107   recalculate the maxfd
108 */
109 static void calc_maxfd(struct event_context *ev)
110 {
111         struct fd_event *e;
112         ev->maxfd = 0;
113         for (e=ev->fd_events; e; e=e->next) {
114                 if (e->ref_count && 
115                     e->fd > ev->maxfd) {
116                         ev->maxfd = e->fd;
117                 }
118         }
119 }
120
121 /* to mark the ev->maxfd invalid
122  * this means we need to recalculate it
123  */
124 #define EVENT_INVALID_MAXFD (-1)
125
126 /*
127   remove a fd based event
128   the event to remove is matched by looking at the handler
129   function and the file descriptor
130   return False on failure (event not found)
131 */
132 BOOL event_remove_fd(struct event_context *ev, struct fd_event *e1)
133 {
134         struct fd_event *e;
135         for (e=ev->fd_events; e; e=e->next) {
136                 if (e->ref_count &&
137                     e->fd == e1->fd && 
138                     e->handler == e1->handler) {
139                         e->ref_count--;
140                         return True;
141                 }
142         }
143         return False;
144 }
145
146 /*
147   remove all fd based events that match a specified fd
148 */
149 void event_remove_fd_all(struct event_context *ev, int fd)
150 {
151         struct fd_event *e;
152         for (e=ev->fd_events; e; e=e->next) {
153                 if (e->ref_count && e->fd == fd) {
154                         e->ref_count--;
155                 }
156         }
157 }
158
159 /*
160   remove all fd based events that match a specified handler
161 */
162 void event_remove_fd_all_handler(struct event_context *ev, void *handler)
163 {
164         struct fd_event *e;
165         for (e=ev->fd_events; e; e=e->next) {
166                 if (e->ref_count &&
167                     handler == (void *)e->handler) {
168                         e->ref_count--;
169                 }
170         }
171 }
172
173
174 /*
175   add a timed event
176   return NULL on failure (memory allocation error)
177 */
178 struct timed_event *event_add_timed(struct event_context *ev, struct timed_event *e) 
179 {
180         e = memdup(e, sizeof(*e));
181         if (!e) return NULL;
182         e->ref_count = 1;
183         DLIST_ADD(ev->timed_events, e);
184         return e;
185 }
186
187 /*
188   remove a timed event
189   the event to remove is matched only on the handler function
190   return False on failure (event not found)
191 */
192 BOOL event_remove_timed(struct event_context *ev, struct timed_event *e1) 
193 {
194         struct timed_event *e;
195         for (e=ev->timed_events; e; e=e->next) {
196                 if (e->ref_count &&
197                     e->handler == e1->handler) {
198                         e->ref_count--;
199                         return True;
200                 }
201         }
202         return False;
203 }
204
205 /*
206   add a loop event
207   return NULL on failure (memory allocation error)
208 */
209 struct loop_event *event_add_loop(struct event_context *ev, struct loop_event *e)
210 {
211         e = memdup(e, sizeof(*e));
212         if (!e) return NULL;
213         e->ref_count = 1;
214         DLIST_ADD(ev->loop_events, e);
215         return e;
216 }
217
218 /*
219   remove a loop event
220   the event to remove is matched only on the handler function
221   return False on failure (memory allocation error)
222 */
223 BOOL event_remove_loop(struct event_context *ev, struct loop_event *e1) 
224 {
225         struct loop_event *e;
226         for (e=ev->loop_events; e; e=e->next) {
227                 if (e->ref_count &&
228                     e->handler == e1->handler) {
229                         e->ref_count--;
230                         return True;
231                 }
232         }
233         return False;
234 }
235
236
237 /*
238   tell the event loop to exit with the specified code
239 */
240 void event_loop_exit(struct event_context *ev, int code)
241 {
242         ev->exit.exit_now = True;
243         ev->exit.code = code;
244 }
245
246 /*
247   go into an event loop using the events defined in ev this function
248   will return with the specified code if one of the handlers calls
249   event_loop_exit()
250
251   also return (with code 0) if all fd events are removed
252 */
253 int event_loop_wait(struct event_context *ev)
254 {
255         time_t t;
256
257         ZERO_STRUCT(ev->exit);
258         ev->maxfd = EVENT_INVALID_MAXFD;
259
260         t = time(NULL);
261
262         while (ev->fd_events && !ev->exit.exit_now) {
263                 fd_set r_fds, w_fds;
264                 struct fd_event *fe;
265                 struct loop_event *le;
266                 struct timed_event *te;
267                 int selrtn;
268                 struct timeval tval;
269
270                 /* the loop events are called on each loop. Be careful to allow the 
271                    event to remove itself */
272                 for (le=ev->loop_events;le;) {
273                         struct loop_event *next = le->next;
274                         if (le->ref_count == 0) {
275                                 DLIST_REMOVE(ev->loop_events, le);
276                                 free(le);
277                         } else {
278                                 le->ref_count++;
279                                 le->handler(ev, le, t);
280                                 le->ref_count--;
281                         }
282                         le = next;
283                 }
284
285                 ZERO_STRUCT(tval);
286                 FD_ZERO(&r_fds);
287                 FD_ZERO(&w_fds);
288
289                 /* setup any fd events */
290                 for (fe=ev->fd_events; fe; ) {
291                         struct fd_event *next = fe->next;
292                         if (fe->ref_count == 0) {
293                                 DLIST_REMOVE(ev->fd_events, fe);
294                                 if (ev->maxfd == fe->fd) {
295                                         ev->maxfd = EVENT_INVALID_MAXFD;
296                                 }
297                                 free(fe);
298                         } else {
299                                 if (fe->flags & EVENT_FD_READ) {
300                                         FD_SET(fe->fd, &r_fds);
301                                 }
302                                 if (fe->flags & EVENT_FD_WRITE) {
303                                         FD_SET(fe->fd, &w_fds);
304                                 }
305                         }
306                         fe = next;
307                 }
308
309                 /* start with a reasonable max timeout */
310                 tval.tv_sec = 600;
311                 
312                 /* work out the right timeout for all timed events */
313                 for (te=ev->timed_events;te;te=te->next) {
314                         int timeout = te->next_event - t;
315                         if (timeout < 0) {
316                                 timeout = 0;
317                         }
318                         if (te->ref_count &&
319                             timeout < tval.tv_sec) {
320                                 tval.tv_sec = timeout;
321                         }
322                 }
323
324                 /* only do a select() if there're fd_events
325                  * otherwise we would block for a the time in tval,
326                  * and if there're no fd_events present anymore we want to
327                  * leave the event loop directly
328                  */
329                 if (ev->fd_events) {
330                         /* we maybe need to recalculate the maxfd */
331                         if (ev->maxfd == EVENT_INVALID_MAXFD) {
332                                 calc_maxfd(ev);
333                         }
334
335                         /* TODO:
336                          * we don't use sys_select() as it isn't thread
337                          * safe. We need to replace the magic pipe handling in
338                          * sys_select() with something in the events
339                          * structure - for now just use select() 
340                          */
341                         selrtn = select(ev->maxfd+1, &r_fds, &w_fds, NULL, &tval);
342
343                         t = time(NULL);
344
345                         if (selrtn == -1 && errno == EBADF) {
346                                 /* the socket is dead! this should never
347                                    happen as the socket should have first been
348                                    made readable and that should have removed
349                                    the event, so this must be a bug. This is a
350                                    fatal error. */
351                                 DEBUG(0,("EBADF on event_loop_wait - exiting\n"));
352                                 return -1;
353                         }
354
355                         if (selrtn > 0) {
356                                 /* at least one file descriptor is ready - check
357                                    which ones and call the handler, being careful to allow
358                                    the handler to remove itself when called */
359                                 for (fe=ev->fd_events; fe; fe=fe->next) {
360                                         uint16 flags = 0;
361                                         if (FD_ISSET(fe->fd, &r_fds)) flags |= EVENT_FD_READ;
362                                         if (FD_ISSET(fe->fd, &w_fds)) flags |= EVENT_FD_WRITE;
363                                         if (fe->ref_count && flags) {
364                                                 fe->ref_count++;
365                                                 fe->handler(ev, fe, t, flags);
366                                                 fe->ref_count--;
367                                         }
368                                 }
369                         }
370                 }
371
372                 /* call any timed events that are now due */
373                 for (te=ev->timed_events;te;) {
374                         struct timed_event *next = te->next;
375                         if (te->ref_count == 0) {
376                                 DLIST_REMOVE(ev->timed_events, te);
377                                 free(te);
378                         } else if (te->next_event <= t) {
379                                 te->ref_count++;
380                                 te->handler(ev, te, t);
381                                 te->ref_count--;
382                                 if (te->next_event <= t) {
383                                         /* the handler didn't set a time for the 
384                                            next event - remove the event */
385                                         event_remove_timed(ev, te);
386                                 }
387                         }
388                         te = next;
389                 }               
390
391         }
392
393         return ev->exit.code;
394 }