Blame common-src/event.c

Packit Service 392537
/*
Packit Service 392537
 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
Packit Service 392537
 * Copyright (c) 1999 University of Maryland at College Park
Packit Service 392537
 * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
Packit Service 392537
 * Copyright (c) 2013-2016 Carbonite, Inc.  All Rights Reserved.
Packit Service 392537
 * All Rights Reserved.
Packit Service 392537
 *
Packit Service 392537
 * Permission to use, copy, modify, distribute, and sell this software and its
Packit Service 392537
 * documentation for any purpose is hereby granted without fee, provided that
Packit Service 392537
 * the above copyright notice appear in all copies and that both that
Packit Service 392537
 * copyright notice and this permission notice appear in supporting
Packit Service 392537
 * documentation, and that the name of U.M. not be used in advertising or
Packit Service 392537
 * publicity pertaining to distribution of the software without specific,
Packit Service 392537
 * written prior permission.  U.M. makes no representations about the
Packit Service 392537
 * suitability of this software for any purpose.  It is provided "as is"
Packit Service 392537
 * without express or implied warranty.
Packit Service 392537
 *
Packit Service 392537
 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
Packit Service 392537
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
Packit Service 392537
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Packit Service 392537
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
Packit Service 392537
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
Packit Service 392537
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Packit Service 392537
 *
Packit Service 392537
 * Authors: the Amanda Development Team.  Its members are listed in a
Packit Service 392537
 * file named AUTHORS, in the root directory of this distribution.
Packit Service 392537
 */
Packit Service 392537
/*
Packit Service 392537
 * $Id: event.c,v 1.24 2006/06/16 10:55:05 martinea Exp $
Packit Service 392537
 *
Packit Service 392537
 * Event handler.  Serializes different kinds of events to allow for
Packit Service 392537
 * a uniform interface, central state storage, and centralized
Packit Service 392537
 * interdependency logic.
Packit Service 392537
 *
Packit Service 392537
 * This is a compatibility wrapper over Glib's GMainLoop.  New code should
Packit Service 392537
 * use Glib's interface directly.
Packit Service 392537
 *
Packit Service 392537
 * Each event_handle is associated with a unique GSource, identified by it
Packit Service 392537
 * event_source_id.
Packit Service 392537
 */
Packit Service 392537
Packit Service 392537
#include "amanda.h"
Packit Service 392537
#include "conffile.h"
Packit Service 392537
#include "event.h"
Packit Service 392537
#include "glib-util.h"
Packit Service 392537
Packit Service 392537
/* TODO: use mem chunks to allocate event_handles */
Packit Service 392537
/* TODO: lock stuff for threading */
Packit Service 392537
Packit Service 392537
/* Write a debugging message if the config variable debug_event
Packit Service 392537
 * is greater than or equal to i */
Packit Service 392537
#define event_debug(i, ...) do {	\
Packit Service 392537
       if ((i) <= debug_event) {	\
Packit Service 392537
           dbprintf(__VA_ARGS__);	\
Packit Service 392537
       }				\
Packit Service 392537
} while (0)
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * The opaque handle passed back to the caller.  This is typedefed to
Packit Service 392537
 * event_handle_t in our header file.
Packit Service 392537
 */
Packit Service 392537
struct event_handle {
Packit Service 392537
    event_fn_t fn;		/* function to call when this fires */
Packit Service 392537
    void *arg;			/* argument to pass to previous function */
Packit Service 392537
Packit Service 392537
    event_type_t type;		/* type of event */
Packit Service 392537
    event_id_t data;		/* type data */
Packit Service 392537
Packit Service 392537
    GSource *source;		/* Glib event source, if one exists */
Packit Service 392537
    guint source_id;	        /* ID of the glib event source */
Packit Service 392537
Packit Service 392537
    gboolean has_fired;		/* for use by event_wait() */
Packit Service 392537
    gboolean is_dead;		/* should this event be deleted? */
Packit Service 392537
};
Packit Service 392537
Packit Service 392537
/* A list of all extant event_handle objects, used for searching for particular
Packit Service 392537
 * events and for deleting dead events */
Packit Service 392537
GSList *all_events = NULL;
Packit Service 392537
Packit Service 392537
#if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31))
Packit Service 392537
# pragma GCC diagnostic push
Packit Service 392537
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
Packit Service 392537
#endif
Packit Service 392537
GStaticMutex event_mutex = G_STATIC_MUTEX_INIT;
Packit Service 392537
#if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31))
Packit Service 392537
# pragma GCC diagnostic pop
Packit Service 392537
#endif
Packit Service 392537
Packit Service 392537
/* should event_loop_run stop? */
Packit Service 392537
gboolean stop = FALSE;
Packit Service 392537
gboolean global_return_when_empty = TRUE;
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * Utility functions
Packit Service 392537
 */
Packit Service 392537
Packit Service 392537
static const char *event_type2str(event_type_t type);
Packit Service 392537
static gboolean any_mainloop_events(void);
Packit Service 392537
Packit Service 392537
/* "Fire" an event handle, by calling its callback function */
Packit Service 392537
#define	fire(eh) do { \
Packit Service 392537
	event_debug(1, "firing %p: %s/%jd\n", eh, event_type2str((eh)->type), (eh)->data); \
Packit Service 392537
	if (*(eh)->fn) { (*(eh)->fn)((eh)->arg); } \
Packit Service 392537
	(eh)->has_fired = TRUE; \
Packit Service 392537
} while(0)
Packit Service 392537
Packit Service 392537
/* Adapt a Glib callback to an event_handle_t callback; assumes that the
Packit Service 392537
 * user_ptr for the Glib callback is a pointer to the event_handle_t.  */
Packit Service 392537
static gboolean 
Packit Service 392537
event_handle_callback(
Packit Service 392537
    gpointer user_ptr)
Packit Service 392537
{
Packit Service 392537
    event_handle_t *hdl = (event_handle_t *)user_ptr;
Packit Service 392537
Packit Service 392537
    /* if the handle is dead, then don't fire the callback; this means that
Packit Service 392537
     * we're in the process of freeing the event */
Packit Service 392537
    if (!hdl->is_dead) {
Packit Service 392537
	fire(hdl);
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    /* don't ever let GMainLoop destroy GSources */
Packit Service 392537
    return TRUE;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * Public functions
Packit Service 392537
 *  DEPRECATED because not safe in multi-thread, callback can be called before event_register return
Packit Service 392537
 */
Packit Service 392537
/*
Packit Service 392537
event_handle_t *
Packit Service 392537
event_register(
Packit Service 392537
    event_id_t data,
Packit Service 392537
    event_type_t type,
Packit Service 392537
    event_fn_t fn,
Packit Service 392537
    void *arg)
Packit Service 392537
{
Packit Service 392537
    event_handle_t *handle;
Packit Service 392537
Packit Service 392537
    handle = event_create(data, type, fn, arg);
Packit Service 392537
    event_activate(handle);
Packit Service 392537
    return handle;
Packit Service 392537
}
Packit Service 392537
*/
Packit Service 392537
Packit Service 392537
event_handle_t *
Packit Service 392537
event_create(
Packit Service 392537
    event_id_t data,
Packit Service 392537
    event_type_t type,
Packit Service 392537
    event_fn_t fn,
Packit Service 392537
    void *arg)
Packit Service 392537
{
Packit Service 392537
    event_handle_t *handle;
Packit Service 392537
Packit Service 392537
    g_static_mutex_lock(&event_mutex);
Packit Service 392537
Packit Service 392537
    /* sanity-checking */
Packit Service 392537
    if ((type == EV_READFD) || (type == EV_WRITEFD)) {
Packit Service 392537
	/* make sure we aren't given a high fd that will overflow a fd_set */
Packit Service 392537
	if (data >= (int)FD_SETSIZE) {
Packit Service 392537
	    error(_("event_create: Invalid file descriptor %jd"), data);
Packit Service 392537
	    /*NOTREACHED*/
Packit Service 392537
	}
Packit Service 392537
    } else if (type == EV_TIME) {
Packit Service 392537
	if (data < 0) {
Packit Service 392537
	    error(_("event_create: interval for EV_TIME must be greater than 0; got %jd"), data);
Packit Service 392537
	}
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    handle = g_new0(event_handle_t, 1);
Packit Service 392537
    handle->fn = fn;
Packit Service 392537
    handle->arg = arg;
Packit Service 392537
    handle->type = type;
Packit Service 392537
    handle->data = data;
Packit Service 392537
    handle->is_dead = FALSE;
Packit Service 392537
Packit Service 392537
    event_debug(1, _("event: register: %p->data=%jd, type=%s\n"),
Packit Service 392537
		    handle, handle->data, event_type2str(handle->type));
Packit Service 392537
Packit Service 392537
    g_static_mutex_unlock(&event_mutex);
Packit Service 392537
    return handle;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
void
Packit Service 392537
event_activate(
Packit Service 392537
    event_handle_t *handle)
Packit Service 392537
{
Packit Service 392537
    GIOCondition cond;
Packit Service 392537
    assert(handle != NULL);
Packit Service 392537
Packit Service 392537
    g_static_mutex_lock(&event_mutex);
Packit Service 392537
Packit Service 392537
    /* add to the list of events */
Packit Service 392537
    all_events = g_slist_prepend(all_events, (gpointer)handle);
Packit Service 392537
Packit Service 392537
    /* and set up the GSource for this event */
Packit Service 392537
    switch (handle->type) {
Packit Service 392537
	case EV_READFD:
Packit Service 392537
	case EV_WRITEFD:
Packit Service 392537
	    /* create a new source */
Packit Service 392537
	    if (handle->type == EV_READFD) {
Packit Service 392537
		cond = G_IO_IN | G_IO_HUP | G_IO_ERR;
Packit Service 392537
	    } else {
Packit Service 392537
		cond = G_IO_OUT | G_IO_ERR;
Packit Service 392537
	    }
Packit Service 392537
Packit Service 392537
	    handle->source = new_fdsource(handle->data, cond);
Packit Service 392537
Packit Service 392537
	    /* attach it to the default GMainLoop */
Packit Service 392537
	    g_source_attach(handle->source, NULL);
Packit Service 392537
	    handle->source_id = g_source_get_id(handle->source);
Packit Service 392537
Packit Service 392537
	    /* And set its callbacks */
Packit Service 392537
	    g_source_set_callback(handle->source, event_handle_callback,
Packit Service 392537
				  (gpointer)handle, NULL);
Packit Service 392537
Packit Service 392537
	    /* drop our reference to it, so when it's detached, it will be
Packit Service 392537
	     * destroyed. */
Packit Service 392537
	    g_source_unref(handle->source);
Packit Service 392537
	    break;
Packit Service 392537
Packit Service 392537
	case EV_TIME:
Packit Service 392537
	    /* Glib provides a nice shortcut for timeouts.  The *1000 converts
Packit Service 392537
	     * seconds to milliseconds. */
Packit Service 392537
	    handle->source_id = g_timeout_add(handle->data * 1000, event_handle_callback,
Packit Service 392537
					      (gpointer)handle);
Packit Service 392537
Packit Service 392537
	    /* But it doesn't give us the source directly.. */
Packit Service 392537
	    handle->source = g_main_context_find_source_by_id(NULL, handle->source_id);
Packit Service 392537
	    /* EV_TIME must always be handled after EV_READ */
Packit Service 392537
	    g_source_set_priority(handle->source, 10);
Packit Service 392537
	    break;
Packit Service 392537
Packit Service 392537
	case EV_WAIT:
Packit Service 392537
	    /* nothing to do -- these are handled independently of GMainLoop */
Packit Service 392537
	    break;
Packit Service 392537
Packit Service 392537
	default:
Packit Service 392537
	    error(_("Unknown event type %s"), event_type2str(handle->type));
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    g_static_mutex_unlock(&event_mutex);
Packit Service 392537
    return;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * Mark an event to be released.  Because we may be traversing the queue
Packit Service 392537
 * when this is called, we must wait until later to actually remove
Packit Service 392537
 * the event.
Packit Service 392537
 */
Packit Service 392537
void
Packit Service 392537
event_release(
Packit Service 392537
    event_handle_t *handle)
Packit Service 392537
{
Packit Service 392537
    assert(handle != NULL);
Packit Service 392537
Packit Service 392537
    g_static_mutex_lock(&event_mutex);
Packit Service 392537
    event_debug(1, _("event: release (mark): %p data=%jd, type=%s\n"),
Packit Service 392537
		    handle, handle->data,
Packit Service 392537
		    event_type2str(handle->type));
Packit Service 392537
    assert(!handle->is_dead);
Packit Service 392537
Packit Service 392537
    /* Mark it as dead and leave it for the event_loop to remove */
Packit Service 392537
    handle->is_dead = TRUE;
Packit Service 392537
Packit Service 392537
    if (global_return_when_empty && !any_mainloop_events()) {
Packit Service 392537
	g_main_loop_quit(default_main_loop());
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    g_static_mutex_unlock(&event_mutex);
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * Fire all EV_WAIT events waiting on the specified id.
Packit Service 392537
 */
Packit Service 392537
int
Packit Service 392537
event_wakeup(
Packit Service 392537
    event_id_t id)
Packit Service 392537
{
Packit Service 392537
    GSList *iter;
Packit Service 392537
    GSList *tofire = NULL;
Packit Service 392537
    int nwaken = 0;
Packit Service 392537
Packit Service 392537
    g_static_mutex_lock(&event_mutex);
Packit Service 392537
    event_debug(1, _("event: wakeup: enter (%jd)\n"), id);
Packit Service 392537
Packit Service 392537
    /* search for any and all matching events, and record them.  This way
Packit Service 392537
     * we have determined the whole list of events we'll be firing *before*
Packit Service 392537
     * we fire any of them. */
Packit Service 392537
    for (iter = all_events; iter != NULL; iter = g_slist_next(iter)) {
Packit Service 392537
	event_handle_t *eh = (event_handle_t *)iter->data;
Packit Service 392537
	if (eh->type == EV_WAIT && eh->data == id && !eh->is_dead) {
Packit Service 392537
	    tofire = g_slist_append(tofire, (gpointer)eh);
Packit Service 392537
	}
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    /* fire them */
Packit Service 392537
    for (iter = tofire; iter != NULL; iter = g_slist_next(iter)) {
Packit Service 392537
	event_handle_t *eh = (event_handle_t *)iter->data;
Packit Service 392537
	if (eh->type == EV_WAIT && eh->data == id && !eh->is_dead) {
Packit Service 392537
	    event_debug(1, _("A: event: wakeup triggering: %p id=%jd\n"), eh, id);
Packit Service 392537
	    /* The lcok must be release before running the event */
Packit Service 392537
	    g_static_mutex_unlock(&event_mutex);
Packit Service 392537
	    fire(eh);
Packit Service 392537
	    g_static_mutex_lock(&event_mutex);
Packit Service 392537
	    nwaken++;
Packit Service 392537
	}
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    /* and free the temporary list */
Packit Service 392537
    g_slist_free(tofire);
Packit Service 392537
Packit Service 392537
    g_static_mutex_unlock(&event_mutex);
Packit Service 392537
    return (nwaken);
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * The event loop.
Packit Service 392537
 */
Packit Service 392537
Packit Service 392537
static void event_loop_wait (event_handle_t *, const int, gboolean return_when_empty);
Packit Service 392537
Packit Service 392537
void
Packit Service 392537
event_loop(
Packit Service 392537
    int nonblock)
Packit Service 392537
{
Packit Service 392537
    event_loop_wait(NULL, nonblock, TRUE);
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
void
Packit Service 392537
event_loop_run(
Packit Service 392537
    void)
Packit Service 392537
{
Packit Service 392537
    stop = FALSE;
Packit Service 392537
    event_loop_wait(NULL, 0, FALSE);
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
void
Packit Service 392537
event_loop_quit(
Packit Service 392537
    void)
Packit Service 392537
{
Packit Service 392537
    stop = TRUE;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
void
Packit Service 392537
event_wait(
Packit Service 392537
    event_handle_t *eh)
Packit Service 392537
{
Packit Service 392537
    event_loop_wait(eh, 0, TRUE);
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
/* Flush out any dead events in all_events.  Be careful that this
Packit Service 392537
 * isn't called while someone is iterating over all_events.
Packit Service 392537
 *
Packit Service 392537
 * @param wait_eh: the event handle we're waiting on, which shouldn't
Packit Service 392537
 *	    be flushed.
Packit Service 392537
 */
Packit Service 392537
static void
Packit Service 392537
flush_dead_events(event_handle_t *wait_eh)
Packit Service 392537
{
Packit Service 392537
    GSList *iter, *next;
Packit Service 392537
Packit Service 392537
    for (iter = all_events; iter != NULL; iter = next) {
Packit Service 392537
	event_handle_t *hdl = (event_handle_t *)iter->data;
Packit Service 392537
	next = g_slist_next(iter);
Packit Service 392537
Packit Service 392537
	/* (handle the case when wait_eh is dead by simply not deleting
Packit Service 392537
	 * it; the next run of event_loop will take care of it) */
Packit Service 392537
	if (hdl->is_dead && hdl != wait_eh) {
Packit Service 392537
	    all_events = g_slist_delete_link(all_events, iter);
Packit Service 392537
	    if (hdl->source) g_source_destroy(hdl->source);
Packit Service 392537
Packit Service 392537
	    amfree(hdl);
Packit Service 392537
	}
Packit Service 392537
    }
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
/* Return TRUE if we have any events outstanding that can be dispatched
Packit Service 392537
 * by GMainLoop.  Recall EV_WAIT events appear in all_events, but are
Packit Service 392537
 * not dispatched by GMainLoop.  */
Packit Service 392537
static gboolean
Packit Service 392537
any_mainloop_events(void)
Packit Service 392537
{
Packit Service 392537
    GSList *iter;
Packit Service 392537
    gboolean ret = FALSE;
Packit Service 392537
Packit Service 392537
    for (iter = all_events; iter != NULL; iter = g_slist_next(iter)) {
Packit Service 392537
	event_handle_t *hdl = (event_handle_t *)iter->data;
Packit Service 392537
	event_debug(2, _("list %p: %s %s/%jd\n"), hdl, hdl->is_dead?"dead":"alive", event_type2str((hdl)->type), (hdl)->data);
Packit Service 392537
	if (hdl->type != EV_WAIT && !hdl->is_dead)
Packit Service 392537
	    ret = TRUE;
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    return ret;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
static void
Packit Service 392537
event_loop_wait(
Packit Service 392537
    event_handle_t *wait_eh,
Packit Service 392537
    int nonblock,
Packit Service 392537
    gboolean return_when_empty)
Packit Service 392537
{
Packit Service 392537
    global_return_when_empty = return_when_empty;
Packit Service 392537
    g_static_mutex_lock(&event_mutex);
Packit Service 392537
    event_debug(1, _("event: loop: enter: nonblockg=%d, eh=%p\n"), nonblock, wait_eh);
Packit Service 392537
Packit Service 392537
    /* If we're waiting for a specific event, then reset its has_fired flag */
Packit Service 392537
    if (wait_eh) {
Packit Service 392537
	wait_eh->has_fired = FALSE;
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    /* Keep looping until there are no events, or until wait_eh has fired */
Packit Service 392537
    while (1) {
Packit Service 392537
	/* clean up first, so we don't accidentally check a dead source */
Packit Service 392537
	flush_dead_events(wait_eh);
Packit Service 392537
Packit Service 392537
	/* if there's nothing to wait for, then don't block, but run an
Packit Service 392537
	 * iteration so that any other users of GMainLoop will get a chance
Packit Service 392537
	 * to run. */
Packit Service 392537
	if (return_when_empty && !any_mainloop_events())
Packit Service 392537
	    break;
Packit Service 392537
Packit Service 392537
	/* Do an iteration */
Packit Service 392537
	/* Relese the lock before running an iteration */
Packit Service 392537
	g_static_mutex_unlock(&event_mutex);
Packit Service 392537
	g_main_context_iteration(NULL, !nonblock);
Packit Service 392537
	g_static_mutex_lock(&event_mutex);
Packit Service 392537
Packit Service 392537
	/* stop if we're told to */
Packit Service 392537
	if (!return_when_empty && stop)
Packit Service 392537
	    break;
Packit Service 392537
Packit Service 392537
	/* If the event we've been waiting for has fired or been released, as
Packit Service 392537
	 * appropriate, we're done.  See the comments for event_wait in event.h
Packit Service 392537
	 * for the skinny on this weird expression. */
Packit Service 392537
	if (wait_eh && ((wait_eh->type == EV_WAIT && wait_eh->is_dead)
Packit Service 392537
	             || (wait_eh->type != EV_WAIT && wait_eh->has_fired)))
Packit Service 392537
	    break;
Packit Service 392537
Packit Service 392537
	/* Don't loop if we're not blocking */
Packit Service 392537
	if (nonblock)
Packit Service 392537
	    break;
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    /* extra cleanup, to keep all_events short, and to delete wait_eh if it
Packit Service 392537
     * has been released. */
Packit Service 392537
    flush_dead_events(NULL);
Packit Service 392537
Packit Service 392537
    g_static_mutex_unlock(&event_mutex);
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
GMainLoop *
Packit Service 392537
default_main_loop(void)
Packit Service 392537
{
Packit Service 392537
    static GMainLoop *loop = NULL;
Packit Service 392537
    if (!loop)
Packit Service 392537
	loop = g_main_loop_new(NULL, TRUE);
Packit Service 392537
    return loop;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * Convert an event type into a string
Packit Service 392537
 */
Packit Service 392537
static const char *
Packit Service 392537
event_type2str(
Packit Service 392537
    event_type_t type)
Packit Service 392537
{
Packit Service 392537
    static const struct {
Packit Service 392537
	event_type_t type;
Packit Service 392537
	const char name[12];
Packit Service 392537
    } event_types[] = {
Packit Service 392537
#define	X(s)	{ s, stringize(s) }
Packit Service 392537
	X(EV_READFD),
Packit Service 392537
	X(EV_WRITEFD),
Packit Service 392537
	X(EV_TIME),
Packit Service 392537
	X(EV_WAIT),
Packit Service 392537
#undef X
Packit Service 392537
    };
Packit Service 392537
    size_t i;
Packit Service 392537
Packit Service 392537
    for (i = 0; i < G_N_ELEMENTS(event_types); i++)
Packit Service 392537
	if (type == event_types[i].type)
Packit Service 392537
	    return (event_types[i].name);
Packit Service 392537
    return (_("BOGUS EVENT TYPE"));
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * FDSource -- a source for a file descriptor
Packit Service 392537
 *
Packit Service 392537
 * We could use Glib's GIOChannel for this, but it adds some buffering
Packit Service 392537
 * and Unicode functionality that we really don't want.  The custom GSource
Packit Service 392537
 * is simple enough anyway, and the Glib documentation describes it in prose.
Packit Service 392537
 */
Packit Service 392537
Packit Service 392537
typedef struct FDSource {
Packit Service 392537
    GSource source; /* must be the first element in the struct */
Packit Service 392537
    GPollFD pollfd; /* Our file descriptor */
Packit Service 392537
} FDSource;
Packit Service 392537
Packit Service 392537
static gboolean
Packit Service 392537
fdsource_prepare(
Packit Service 392537
    GSource *source G_GNUC_UNUSED,
Packit Service 392537
    gint *timeout_)
Packit Service 392537
{
Packit Service 392537
    *timeout_ = -1; /* block forever, as far as we're concerned */
Packit Service 392537
    return FALSE;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
static gboolean
Packit Service 392537
fdsource_check(
Packit Service 392537
    GSource *source)
Packit Service 392537
{
Packit Service 392537
    FDSource *fds = (FDSource *)source;
Packit Service 392537
Packit Service 392537
    /* we need to be dispatched if any interesting events have been received by the FD */
Packit Service 392537
    return fds->pollfd.events & fds->pollfd.revents;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
static gboolean
Packit Service 392537
fdsource_dispatch(
Packit Service 392537
    GSource *source G_GNUC_UNUSED,
Packit Service 392537
    GSourceFunc callback,
Packit Service 392537
    gpointer user_data)
Packit Service 392537
{
Packit Service 392537
    if (callback)
Packit Service 392537
	return callback(user_data);
Packit Service 392537
Packit Service 392537
    /* Don't automatically detach the event source if there's no callback. */
Packit Service 392537
    return TRUE;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
GSource *
Packit Service 392537
new_fdsource(gint fd, GIOCondition events)
Packit Service 392537
{
Packit Service 392537
    static GSourceFuncs *fdsource_funcs = NULL;
Packit Service 392537
    GSource *src;
Packit Service 392537
    FDSource *fds;
Packit Service 392537
Packit Service 392537
    /* initialize these here to avoid a compiler warning */
Packit Service 392537
    if (!fdsource_funcs) {
Packit Service 392537
	fdsource_funcs = g_new0(GSourceFuncs, 1);
Packit Service 392537
	fdsource_funcs->prepare = fdsource_prepare;
Packit Service 392537
	fdsource_funcs->check = fdsource_check;
Packit Service 392537
	fdsource_funcs->dispatch = fdsource_dispatch;
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    src = g_source_new(fdsource_funcs, sizeof(FDSource));
Packit Service 392537
    fds = (FDSource *)src;
Packit Service 392537
Packit Service 392537
    fds->pollfd.fd = fd;
Packit Service 392537
    fds->pollfd.events = events;
Packit Service 392537
    g_source_add_poll(src, &fds->pollfd);
Packit Service 392537
Packit Service 392537
    return src;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
/*
Packit Service 392537
 * ChildWatchSource -- a source for a file descriptor
Packit Service 392537
 *
Packit Service 392537
 * Newer versions of glib provide equivalent functionality; consider
Packit Service 392537
 * optionally using that, protected by a GLIB_CHECK_VERSION condition.
Packit Service 392537
 */
Packit Service 392537
Packit Service 392537
/* Versions before glib-2.4.0 didn't include a child watch source, and versions
Packit Service 392537
 * before 2.6.0 used unreliable signals.  On these versions, we implement
Packit Service 392537
 * a "dumb" version of our own invention.  This is dumb in the sense that it
Packit Service 392537
 * doesn't use SIGCHLD to detect a dead child, preferring to just poll at
Packit Service 392537
 * exponentially increasing interals.  Writing a smarter implementation runs into
Packit Service 392537
 * some tricky race conditions and extra machinery.  Since there are few, if any,
Packit Service 392537
 * users of a glib version this old, such machinery wouldn't get much testing.
Packit Service 392537
 *
Packit Service 392537
 * FreeBSD users have also reported problems with the glib child watch source,
Packit Service 392537
 * so we use the dumb version on FreeBSD, too.
Packit Service 392537
 */
Packit Service 392537
Packit Service 392537
#if (defined(__FreeBSD__) || GLIB_MAJOR_VERSION < 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 6))
Packit Service 392537
typedef struct ChildWatchSource {
Packit Service 392537
    GSource source; /* must be the first element in the struct */
Packit Service 392537
Packit Service 392537
    pid_t pid;
Packit Service 392537
Packit Service 392537
    gint dead;
Packit Service 392537
    gint status;
Packit Service 392537
Packit Service 392537
    gint timeout;
Packit Service 392537
} ChildWatchSource;
Packit Service 392537
Packit Service 392537
/* this corresponds to rapid checks for about 10 seconds, after which the
Packit Service 392537
 * waitpid() check occurs every 2 seconds. */
Packit Service 392537
#define CWS_BASE_TIMEOUT 20
Packit Service 392537
#define CWS_MULT_TIMEOUT 1.1
Packit Service 392537
#define CWS_MAX_TIMEOUT 2000
Packit Service 392537
Packit Service 392537
static gboolean
Packit Service 392537
child_watch_source_prepare(
Packit Service 392537
    GSource *source,
Packit Service 392537
    gint *timeout_)
Packit Service 392537
{
Packit Service 392537
    ChildWatchSource *cws = (ChildWatchSource *)source;
Packit Service 392537
Packit Service 392537
    *timeout_ = cws->timeout;
Packit Service 392537
Packit Service 392537
    cws->timeout *= CWS_MULT_TIMEOUT;
Packit Service 392537
    if (cws->timeout > CWS_MAX_TIMEOUT) cws->timeout = CWS_MAX_TIMEOUT;
Packit Service 392537
Packit Service 392537
    return FALSE;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
static gboolean
Packit Service 392537
child_watch_source_check(
Packit Service 392537
    GSource *source)
Packit Service 392537
{
Packit Service 392537
    ChildWatchSource *cws = (ChildWatchSource *)source;
Packit Service 392537
Packit Service 392537
    /* is it dead? */
Packit Service 392537
    if (!cws->dead && waitpid(cws->pid, &cws->status, WNOHANG) > 0) {
Packit Service 392537
	cws->dead = TRUE;
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    return cws->dead;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
static gboolean
Packit Service 392537
child_watch_source_dispatch(
Packit Service 392537
    GSource *source G_GNUC_UNUSED,
Packit Service 392537
    GSourceFunc callback,
Packit Service 392537
    gpointer user_data)
Packit Service 392537
{
Packit Service 392537
    ChildWatchSource *cws = (ChildWatchSource *)source;
Packit Service 392537
Packit Service 392537
    /* this shouldn't happen, but just in case */
Packit Service 392537
    if (cws->dead) {
Packit Service 392537
	if (!callback) {
Packit Service 392537
	    g_warning("child %jd died before callback was registered", (uintmax_t)cws->pid);
Packit Service 392537
	    return FALSE;
Packit Service 392537
	}
Packit Service 392537
Packit Service 392537
	((ChildWatchFunc)callback)(cws->pid, cws->status, user_data);
Packit Service 392537
Packit Service 392537
	/* Un-queue this source unconditionally -- the child can't die twice */
Packit Service 392537
	return FALSE;
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    return TRUE;
Packit Service 392537
}
Packit Service 392537
Packit Service 392537
GSource *
Packit Service 392537
new_child_watch_source(pid_t pid)
Packit Service 392537
{
Packit Service 392537
    static GSourceFuncs *child_watch_source_funcs = NULL;
Packit Service 392537
    GSource *src;
Packit Service 392537
    ChildWatchSource *cws;
Packit Service 392537
Packit Service 392537
    /* initialize these here to avoid a compiler warning */
Packit Service 392537
    if (!child_watch_source_funcs) {
Packit Service 392537
	child_watch_source_funcs = g_new0(GSourceFuncs, 1);
Packit Service 392537
	child_watch_source_funcs->prepare = child_watch_source_prepare;
Packit Service 392537
	child_watch_source_funcs->check = child_watch_source_check;
Packit Service 392537
	child_watch_source_funcs->dispatch = child_watch_source_dispatch;
Packit Service 392537
    }
Packit Service 392537
Packit Service 392537
    src = g_source_new(child_watch_source_funcs, sizeof(ChildWatchSource));
Packit Service 392537
    cws = (ChildWatchSource *)src;
Packit Service 392537
Packit Service 392537
    cws->pid = pid;
Packit Service 392537
    cws->dead = FALSE;
Packit Service 392537
    cws->timeout = CWS_BASE_TIMEOUT;
Packit Service 392537
Packit Service 392537
    return src;
Packit Service 392537
}
Packit Service 392537
#else
Packit Service 392537
/* In more recent versions of glib, we just use the built-in glib source */
Packit Service 392537
GSource *
Packit Service 392537
new_child_watch_source(pid_t pid)
Packit Service 392537
{
Packit Service 392537
    return g_child_watch_source_new(pid);
Packit Service 392537
}
Packit Service 392537
#endif