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