Imported Upstream version 3.3.2
[debian/amanda] / common-src / event.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: event.c,v 1.24 2006/06/16 10:55:05 martinea Exp $
28  *
29  * Event handler.  Serializes different kinds of events to allow for
30  * a uniform interface, central state storage, and centralized
31  * interdependency logic.
32  *
33  * This is a compatibility wrapper over Glib's GMainLoop.  New code should
34  * use Glib's interface directly.
35  *
36  * Each event_handle is associated with a unique GSource, identified by it
37  * event_source_id.
38  */
39
40 #include "amanda.h"
41 #include "conffile.h"
42 #include "event.h"
43 #include "glib-util.h"
44
45 /* TODO: use mem chunks to allocate event_handles */
46 /* TODO: lock stuff for threading */
47
48 /* Write a debugging message if the config variable debug_event
49  * is greater than or equal to i */
50 #define event_debug(i, ...) do {        \
51        if ((i) <= debug_event) {        \
52            dbprintf(__VA_ARGS__);       \
53        }                                \
54 } while (0)
55
56 /*
57  * The opaque handle passed back to the caller.  This is typedefed to
58  * event_handle_t in our header file.
59  */
60 struct event_handle {
61     event_fn_t fn;              /* function to call when this fires */
62     void *arg;                  /* argument to pass to previous function */
63
64     event_type_t type;          /* type of event */
65     event_id_t data;            /* type data */
66
67     GSource *source;            /* Glib event source, if one exists */
68     guint source_id;            /* ID of the glib event source */
69
70     gboolean has_fired;         /* for use by event_wait() */
71     gboolean is_dead;           /* should this event be deleted? */
72 };
73
74 /* A list of all extant event_handle objects, used for searching for particular
75  * events and for deleting dead events */
76 GSList *all_events;
77
78 /*
79  * Utility functions
80  */
81
82 static const char *event_type2str(event_type_t type);
83
84 /* "Fire" an event handle, by calling its callback function */
85 #define fire(eh) do { \
86         event_debug(1, "firing %p: %s/%jd\n", eh, event_type2str((eh)->type), (eh)->data); \
87         (*(eh)->fn)((eh)->arg); \
88         (eh)->has_fired = TRUE; \
89 } while(0)
90
91 /* Adapt a Glib callback to an event_handle_t callback; assumes that the
92  * user_ptr for the Glib callback is a pointer to the event_handle_t.  */
93 static gboolean 
94 event_handle_callback(
95     gpointer user_ptr)
96 {
97     event_handle_t *hdl = (event_handle_t *)user_ptr;
98
99     /* if the handle is dead, then don't fire the callback; this means that
100      * we're in the process of freeing the event */
101     if (!hdl->is_dead) {
102         fire(hdl);
103     }
104
105     /* don't ever let GMainLoop destroy GSources */
106     return TRUE;
107 }
108
109 /*
110  * Public functions
111  */
112
113 event_handle_t *
114 event_register(
115     event_id_t data,
116     event_type_t type,
117     event_fn_t fn,
118     void *arg)
119 {
120     event_handle_t *handle;
121     GIOCondition cond;
122
123     /* sanity-checking */
124     if ((type == EV_READFD) || (type == EV_WRITEFD)) {
125         /* make sure we aren't given a high fd that will overflow a fd_set */
126         if (data >= (int)FD_SETSIZE) {
127             error(_("event_register: Invalid file descriptor %jd"), data);
128             /*NOTREACHED*/
129         }
130     } else if (type == EV_TIME) {
131         if (data <= 0) {
132             error(_("event_register: interval for EV_TIME must be greater than 0; got %jd"), data);
133         }
134     }
135
136     handle = g_new0(event_handle_t, 1);
137     handle->fn = fn;
138     handle->arg = arg;
139     handle->type = type;
140     handle->data = data;
141     handle->is_dead = FALSE;
142
143     event_debug(1, _("event: register: %p->data=%jd, type=%s\n"),
144                     handle, handle->data, event_type2str(handle->type));
145
146     /* add to the list of events */
147     all_events = g_slist_prepend(all_events, (gpointer)handle);
148
149     /* and set up the GSource for this event */
150     switch (type) {
151         case EV_READFD:
152         case EV_WRITEFD:
153             /* create a new source */
154             if (type == EV_READFD) {
155                 cond = G_IO_IN | G_IO_HUP | G_IO_ERR;
156             } else {
157                 cond = G_IO_OUT | G_IO_ERR;
158             }
159
160             handle->source = new_fdsource(data, cond);
161
162             /* attach it to the default GMainLoop */
163             g_source_attach(handle->source, NULL);
164             handle->source_id = g_source_get_id(handle->source);
165
166             /* And set its callbacks */
167             g_source_set_callback(handle->source, event_handle_callback,
168                                   (gpointer)handle, NULL);
169
170             /* drop our reference to it, so when it's detached, it will be
171              * destroyed. */
172             g_source_unref(handle->source);
173             break;
174
175         case EV_TIME:
176             /* Glib provides a nice shortcut for timeouts.  The *1000 converts
177              * seconds to milliseconds. */
178             handle->source_id = g_timeout_add(data * 1000, event_handle_callback,
179                                               (gpointer)handle);
180
181             /* But it doesn't give us the source directly.. */
182             handle->source = g_main_context_find_source_by_id(NULL, handle->source_id);
183             /* EV_TIME must always be handled after EV_READ */
184             g_source_set_priority(handle->source, 10);
185             break;
186
187         case EV_WAIT:
188             /* nothing to do -- these are handled independently of GMainLoop */
189             break;
190
191         default:
192             error(_("Unknown event type %s"), event_type2str(type));
193     }
194
195     return handle;
196 }
197
198 /*
199  * Mark an event to be released.  Because we may be traversing the queue
200  * when this is called, we must wait until later to actually remove
201  * the event.
202  */
203 void
204 event_release(
205     event_handle_t *handle)
206 {
207     assert(handle != NULL);
208
209     event_debug(1, _("event: release (mark): %p data=%jd, type=%s\n"),
210                     handle, handle->data,
211                     event_type2str(handle->type));
212     assert(!handle->is_dead);
213
214     /* Mark it as dead and leave it for the event_loop to remove */
215     handle->is_dead = TRUE;
216 }
217
218 /*
219  * Fire all EV_WAIT events waiting on the specified id.
220  */
221 int
222 event_wakeup(
223     event_id_t id)
224 {
225     GSList *iter;
226     GSList *tofire = NULL;
227     int nwaken = 0;
228
229     event_debug(1, _("event: wakeup: enter (%jd)\n"), id);
230
231     /* search for any and all matching events, and record them.  This way
232      * we have determined the whole list of events we'll be firing *before*
233      * we fire any of them. */
234     for (iter = all_events; iter != NULL; iter = g_slist_next(iter)) {
235         event_handle_t *eh = (event_handle_t *)iter->data;
236         if (eh->type == EV_WAIT && eh->data == id && !eh->is_dead) {
237             tofire = g_slist_append(tofire, (gpointer)eh);
238         }
239     }
240
241     /* fire them */
242     for (iter = tofire; iter != NULL; iter = g_slist_next(iter)) {
243         event_handle_t *eh = (event_handle_t *)iter->data;
244         if (eh->type == EV_WAIT && eh->data == id && !eh->is_dead) {
245             event_debug(1, _("A: event: wakeup triggering: %p id=%jd\n"), eh, id);
246             fire(eh);
247             nwaken++;
248         }
249     }
250
251     /* and free the temporary list */
252     g_slist_free(tofire);
253
254     return (nwaken);
255 }
256
257
258 /*
259  * The event loop.
260  */
261
262 static void event_loop_wait (event_handle_t *, const int);
263
264 void
265 event_loop(
266     int nonblock)
267 {
268     event_loop_wait(NULL, nonblock);
269 }
270
271 void
272 event_wait(
273     event_handle_t *eh)
274 {
275     event_loop_wait(eh, 0);
276 }
277
278 /* Flush out any dead events in all_events.  Be careful that this
279  * isn't called while someone is iterating over all_events.
280  *
281  * @param wait_eh: the event handle we're waiting on, which shouldn't
282  *          be flushed.
283  */
284 static void
285 flush_dead_events(event_handle_t *wait_eh)
286 {
287     GSList *iter, *next;
288
289     for (iter = all_events; iter != NULL; iter = next) {
290         event_handle_t *hdl = (event_handle_t *)iter->data;
291         next = g_slist_next(iter);
292
293         /* (handle the case when wait_eh is dead by simply not deleting
294          * it; the next run of event_loop will take care of it) */
295         if (hdl->is_dead && hdl != wait_eh) {
296             all_events = g_slist_delete_link(all_events, iter);
297             if (hdl->source) g_source_destroy(hdl->source);
298
299             amfree(hdl);
300         }
301     }
302 }
303
304 /* Return TRUE if we have any events outstanding that can be dispatched
305  * by GMainLoop.  Recall EV_WAIT events appear in all_events, but are
306  * not dispatched by GMainLoop.  */
307 static gboolean
308 any_mainloop_events(void)
309 {
310     GSList *iter;
311
312     for (iter = all_events; iter != NULL; iter = g_slist_next(iter)) {
313         event_handle_t *hdl = (event_handle_t *)iter->data;
314         event_debug(2, _("list %p: %s/%jd\n"), hdl, event_type2str((hdl)->type), (hdl)->data);
315         if (hdl->type != EV_WAIT)
316             return TRUE;
317     }
318
319     return FALSE;
320 }
321
322 static void
323 event_loop_wait(
324     event_handle_t *wait_eh,
325     int nonblock)
326 {
327     event_debug(1, _("event: loop: enter: nonblockg=%d, eh=%p\n"), nonblock, wait_eh);
328
329     /* If we're waiting for a specific event, then reset its has_fired flag */
330     if (wait_eh) {
331         wait_eh->has_fired = FALSE;
332     }
333
334     /* Keep looping until there are no events, or until wait_eh has fired */
335     while (1) {
336         /* clean up first, so we don't accidentally check a dead source */
337         flush_dead_events(wait_eh);
338
339         /* if there's nothing to wait for, then don't block, but run an
340          * iteration so that any other users of GMainLoop will get a chance
341          * to run. */
342         if (!any_mainloop_events())
343             break;
344
345         /* Do an interation */
346         g_main_context_iteration(NULL, !nonblock);
347
348         /* If the event we've been waiting for has fired or been released, as
349          * appropriate, we're done.  See the comments for event_wait in event.h
350          * for the skinny on this weird expression. */
351         if (wait_eh && ((wait_eh->type == EV_WAIT && wait_eh->is_dead)
352                      || (wait_eh->type != EV_WAIT && wait_eh->has_fired)))
353             break;
354
355         /* Don't loop if we're not blocking */
356         if (nonblock)
357             break;
358     }
359
360     /* extra cleanup, to keep all_events short, and to delete wait_eh if it
361      * has been released. */
362     flush_dead_events(NULL);
363
364 }
365
366 GMainLoop *
367 default_main_loop(void)
368 {
369     static GMainLoop *loop = NULL;
370     if (!loop)
371         loop = g_main_loop_new(NULL, TRUE);
372     return loop;
373 }
374
375 /*
376  * Convert an event type into a string
377  */
378 static const char *
379 event_type2str(
380     event_type_t type)
381 {
382     static const struct {
383         event_type_t type;
384         const char name[12];
385     } event_types[] = {
386 #define X(s)    { s, stringize(s) }
387         X(EV_READFD),
388         X(EV_WRITEFD),
389         X(EV_TIME),
390         X(EV_WAIT),
391 #undef X
392     };
393     size_t i;
394
395     for (i = 0; i < (size_t)(sizeof(event_types) / sizeof(event_types[0])); i++)
396         if (type == event_types[i].type)
397             return (event_types[i].name);
398     return (_("BOGUS EVENT TYPE"));
399 }
400
401 /*
402  * FDSource -- a source for a file descriptor
403  *
404  * We could use Glib's GIOChannel for this, but it adds some buffering
405  * and Unicode functionality that we really don't want.  The custom GSource
406  * is simple enough anyway, and the Glib documentation describes it in prose.
407  */
408
409 typedef struct FDSource {
410     GSource source; /* must be the first element in the struct */
411     GPollFD pollfd; /* Our file descriptor */
412 } FDSource;
413
414 static gboolean
415 fdsource_prepare(
416     GSource *source G_GNUC_UNUSED,
417     gint *timeout_)
418 {
419     *timeout_ = -1; /* block forever, as far as we're concerned */
420     return FALSE;
421 }
422
423 static gboolean
424 fdsource_check(
425     GSource *source)
426 {
427     FDSource *fds = (FDSource *)source;
428
429     /* we need to be dispatched if any interesting events have been received by the FD */
430     return fds->pollfd.events & fds->pollfd.revents;
431 }
432
433 static gboolean
434 fdsource_dispatch(
435     GSource *source G_GNUC_UNUSED,
436     GSourceFunc callback,
437     gpointer user_data)
438 {
439     if (callback)
440         return callback(user_data);
441
442     /* Don't automatically detach the event source if there's no callback. */
443     return TRUE;
444 }
445
446 GSource *
447 new_fdsource(gint fd, GIOCondition events)
448 {
449     static GSourceFuncs *fdsource_funcs = NULL;
450     GSource *src;
451     FDSource *fds;
452
453     /* initialize these here to avoid a compiler warning */
454     if (!fdsource_funcs) {
455         fdsource_funcs = g_new0(GSourceFuncs, 1);
456         fdsource_funcs->prepare = fdsource_prepare;
457         fdsource_funcs->check = fdsource_check;
458         fdsource_funcs->dispatch = fdsource_dispatch;
459     }
460
461     src = g_source_new(fdsource_funcs, sizeof(FDSource));
462     fds = (FDSource *)src;
463
464     fds->pollfd.fd = fd;
465     fds->pollfd.events = events;
466     g_source_add_poll(src, &fds->pollfd);
467
468     return src;
469 }
470
471 /*
472  * ChildWatchSource -- a source for a file descriptor
473  *
474  * Newer versions of glib provide equivalent functionality; consider
475  * optionally using that, protected by a GLIB_CHECK_VERSION condition.
476  */
477
478 /* Versions before glib-2.4.0 didn't include a child watch source, and versions
479  * before 2.6.0 used unreliable signals.  On these versions, we implement
480  * a "dumb" version of our own invention.  This is dumb in the sense that it
481  * doesn't use SIGCHLD to detect a dead child, preferring to just poll at
482  * exponentially increasing interals.  Writing a smarter implementation runs into
483  * some tricky race conditions and extra machinery.  Since there are few, if any,
484  * users of a glib version this old, such machinery wouldn't get much testing.
485  *
486  * FreeBSD users have also reported problems with the glib child watch source,
487  * so we use the dumb version on FreeBSD, too.
488  */
489
490 #if (defined(__FreeBSD__) || GLIB_MAJOR_VERSION < 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 6))
491 typedef struct ChildWatchSource {
492     GSource source; /* must be the first element in the struct */
493
494     pid_t pid;
495
496     gint dead;
497     gint status;
498
499     gint timeout;
500 } ChildWatchSource;
501
502 /* this corresponds to rapid checks for about 10 seconds, after which the
503  * waitpid() check occurs every 2 seconds. */
504 #define CWS_BASE_TIMEOUT 20
505 #define CWS_MULT_TIMEOUT 1.1
506 #define CWS_MAX_TIMEOUT 2000
507
508 static gboolean
509 child_watch_source_prepare(
510     GSource *source,
511     gint *timeout_)
512 {
513     ChildWatchSource *cws = (ChildWatchSource *)source;
514
515     *timeout_ = cws->timeout;
516
517     cws->timeout *= CWS_MULT_TIMEOUT;
518     if (cws->timeout > CWS_MAX_TIMEOUT) cws->timeout = CWS_MAX_TIMEOUT;
519
520     return FALSE;
521 }
522
523 static gboolean
524 child_watch_source_check(
525     GSource *source)
526 {
527     ChildWatchSource *cws = (ChildWatchSource *)source;
528
529     /* is it dead? */
530     if (!cws->dead && waitpid(cws->pid, &cws->status, WNOHANG) > 0) {
531         cws->dead = TRUE;
532     }
533
534     return cws->dead;
535 }
536
537 static gboolean
538 child_watch_source_dispatch(
539     GSource *source G_GNUC_UNUSED,
540     GSourceFunc callback,
541     gpointer user_data)
542 {
543     ChildWatchSource *cws = (ChildWatchSource *)source;
544
545     /* this shouldn't happen, but just in case */
546     if (cws->dead) {
547         if (!callback) {
548             g_warning("child %jd died before callback was registered", (uintmax_t)cws->pid);
549             return FALSE;
550         }
551
552         ((ChildWatchFunc)callback)(cws->pid, cws->status, user_data);
553
554         /* Un-queue this source unconditionally -- the child can't die twice */
555         return FALSE;
556     }
557
558     return TRUE;
559 }
560
561 GSource *
562 new_child_watch_source(pid_t pid)
563 {
564     static GSourceFuncs *child_watch_source_funcs = NULL;
565     GSource *src;
566     ChildWatchSource *cws;
567
568     /* initialize these here to avoid a compiler warning */
569     if (!child_watch_source_funcs) {
570         child_watch_source_funcs = g_new0(GSourceFuncs, 1);
571         child_watch_source_funcs->prepare = child_watch_source_prepare;
572         child_watch_source_funcs->check = child_watch_source_check;
573         child_watch_source_funcs->dispatch = child_watch_source_dispatch;
574     }
575
576     src = g_source_new(child_watch_source_funcs, sizeof(ChildWatchSource));
577     cws = (ChildWatchSource *)src;
578
579     cws->pid = pid;
580     cws->dead = FALSE;
581     cws->timeout = CWS_BASE_TIMEOUT;
582
583     return src;
584 }
585 #else
586 /* In more recent versions of glib, we just use the built-in glib source */
587 GSource *
588 new_child_watch_source(pid_t pid)
589 {
590     return g_child_watch_source_new(pid);
591 }
592 #endif