Add libverto sources in util/verto
authorGreg Hudson <ghudson@mit.edu>
Fri, 2 Sep 2011 17:07:35 +0000 (17:07 +0000)
committerGreg Hudson <ghudson@mit.edu>
Fri, 2 Sep 2011 17:07:35 +0000 (17:07 +0000)
These are from the source repository as of 2011-08-24, since there are
no formal releases yet.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25125 dc483132-0cff-0310-8789-dd5450dbe970

src/util/verto/verto-module.h [new file with mode: 0644]
src/util/verto/verto.c [new file with mode: 0644]
src/util/verto/verto.h [new file with mode: 0644]

diff --git a/src/util/verto/verto-module.h b/src/util/verto/verto-module.h
new file mode 100644 (file)
index 0000000..89d6e56
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*** THE FOLLOWING ARE FOR IMPLEMENTATION MODULES ONLY ***/
+
+#ifndef VERTO_MODULE_H_
+#define VERTO_MODULE_H_
+
+#include <verto.h>
+
+#define VERTO_MODULE_VERSION 1
+#define VERTO_MODULE_TABLE verto_module_table
+#define VERTO_MODULE(name, symb, types) \
+    static verto_ctx_funcs name ## _funcs = { \
+        name ## _ctx_free, \
+        name ## _ctx_run, \
+        name ## _ctx_run_once, \
+        name ## _ctx_break, \
+        name ## _ctx_add, \
+        name ## _ctx_del \
+    }; \
+    verto_module VERTO_MODULE_TABLE = { \
+        VERTO_MODULE_VERSION, \
+        # name, \
+        # symb, \
+        types, \
+        verto_new_ ## name, \
+        verto_default_ ## name, \
+    };
+
+typedef verto_ctx *(*verto_ctx_constructor)();
+
+typedef struct {
+    unsigned int vers;
+    const char *name;
+    const char *symb;
+    verto_ev_type types;
+    verto_ctx_constructor new_ctx;
+    verto_ctx_constructor def_ctx;
+} verto_module;
+
+typedef struct {
+    void  (*ctx_free)(void *ctx);
+    void  (*ctx_run)(void *ctx);
+    void  (*ctx_run_once)(void *ctx);
+    void  (*ctx_break)(void *ctx);
+    void *(*ctx_add)(void *ctx, const verto_ev *ev, verto_ev_flag *flags);
+    void  (*ctx_del)(void *ctx, const verto_ev *ev, void *evpriv);
+} verto_ctx_funcs;
+
+/**
+ * Converts an existing implementation specific loop to a verto_ctx.
+ *
+ * This function also sets the internal default implementation so that future
+ * calls to verto_new(NULL) or verto_default(NULL) will use this specific
+ * implementation.
+ *
+ * @param name The name of the module (unquoted)
+ * @param priv The context private to store
+ * @return A new _ev_ctx, or NULL on error. Call verto_free() when done.
+ */
+#define verto_convert(name, priv) \
+        verto_convert_funcs(&name ## _funcs, &VERTO_MODULE_TABLE, priv)
+
+/**
+ * Converts an existing implementation specific loop to a verto_ctx.
+ *
+ * This function also sets the internal default implementation so that future
+ * calls to verto_new(NULL) or verto_default(NULL) will use this specific
+ * implementation.
+ *
+ * If you are a module implementation, you probably want the macro above.  This
+ * function is generally used directly only when an application is attempting
+ * to expose a home-grown event loop to verto.
+ *
+ * @param name The name of the module (unquoted)
+ * @param priv The context private to store
+ * @return A new _ev_ctx, or NULL on error. Call verto_free() when done.
+ */
+verto_ctx *
+verto_convert_funcs(const verto_ctx_funcs *funcs,
+                    const verto_module *module,
+                    void *priv);
+
+/**
+ * Calls the callback of the verto_ev and then frees it via verto_del().
+ *
+ * The verto_ev is not freed (verto_del() is not called) if it is a signal event.
+ *
+ * @see verto_add_read()
+ * @see verto_add_write()
+ * @see verto_add_timeout()
+ * @see verto_add_idle()
+ * @see verto_add_signal()
+ * @see verto_add_child()
+ * @see verto_del()
+ * @param ev The verto_ev
+ */
+void
+verto_fire(verto_ev *ev);
+
+/**
+ * Sets the status of the pid/handle which caused this event to fire.
+ *
+ * This function does nothing if the verto_ev is not a child type.
+ *
+ * @see verto_add_child()
+ * @param ev The verto_ev to set the status in.
+ * @param status The pid/handle status.
+ */
+void
+verto_set_proc_status(verto_ev *ev, verto_proc_status status);
+
+#endif /* VERTO_MODULE_H_ */
diff --git a/src/util/verto/verto.c b/src/util/verto/verto.c
new file mode 100644 (file)
index 0000000..f59e8ba
--- /dev/null
@@ -0,0 +1,764 @@
+/*
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define _GNU_SOURCE /* For dladdr(), asprintf() */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include <libgen.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <dlfcn.h>
+#endif
+
+#include <verto-module.h>
+
+#ifdef WIN32
+#define pdlmsuffix ".dll"
+#define pdlmtype HMODULE
+#define pdlopenl(filename) LoadLibraryEx(filename, NULL, DONT_RESOLVE_DLL_REFERENCES)
+#define pdlclose(module) FreeLibrary((pdlmtype) module)
+#define pdlsym(mod, sym) ((void *) GetProcAddress(mod, sym))
+
+static pdlmtype
+pdlreopen(const char *filename, pdlmtype module)
+{
+    pdlclose(module);
+    return LoadLibrary(filename);
+}
+
+static char *pdlerror() {
+    char *amsg;
+    LPTSTR msg;
+
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
+                      | FORMAT_MESSAGE_FROM_SYSTEM
+                      | FORMAT_MESSAGE_IGNORE_INSERTS,
+                  NULL, GetLastError(),
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                  (LPTSTR) &msg, 0, NULL);
+    amsg = strdup((const char*) msg);
+    LocalFree(msg);
+    return amsg;
+}
+
+static bool
+pdlsymlinked(const char *modn, const char *symb) {
+    return (GetProcAddress(GetModuleHandle(modn), symb) != NULL ||
+            GetProcAddress(GetModuleHandle(NULL), symb) != NULL);
+}
+
+static bool
+pdladdrmodname(void *addr, char **buf) {
+    MEMORY_BASIC_INFORMATION info;
+    HMODULE mod;
+    char modname[MAX_PATH];
+
+    if (!VirtualQuery(addr, &info, sizeof(info)))
+        return false;
+    mod = (HMODULE) info.AllocationBase;
+
+    if (!GetModuleFileNameA(mod, modname, MAX_PATH))
+        return false;
+
+    if (buf) {
+        *buf = strdup(modname);
+        if (!buf)
+            return false;
+    }
+
+    return true;
+}
+#else
+#define pdlmsuffix ".so"
+#define pdlmtype void *
+#define pdlopenl(filename) dlopen(filename, RTLD_LAZY | RTLD_LOCAL)
+#define pdlclose(module) dlclose((pdlmtype) module)
+#define pdlreopen(filename, module) module
+#define pdlsym(mod, sym) dlsym(mod, sym)
+#define pdlerror() strdup(dlerror())
+
+static int
+pdlsymlinked(const char* modn, const char* symb)
+{
+    void* mod = dlopen(NULL, RTLD_LAZY | RTLD_LOCAL);
+    if (mod) {
+        void* sym = dlsym(mod, symb);
+        dlclose(mod);
+        return sym != NULL;
+    }
+    return 0;
+}
+
+static int
+pdladdrmodname(void *addr, char **buf) {
+    Dl_info dlinfo;
+    if (!dladdr(addr, &dlinfo))
+        return 0;
+    if (buf) {
+        *buf = strdup(dlinfo.dli_fname);
+        if (!*buf)
+            return 0;
+    }
+    return 1;
+}
+#endif
+
+#ifndef NSIG
+#ifdef _NSIG
+#define NSIG _NSIG
+#else
+#define NSIG SIGRTMIN
+#endif
+#endif
+
+#define  _str(s) # s
+#define __str(s) _str(s)
+#define vnew(type) ((type*) malloc(sizeof(type)))
+#define vnew0(type) ((type*) memset(vnew(type), 0, sizeof(type)))
+
+struct _verto_ctx {
+    void *dll;
+    void *modpriv;
+    verto_ev_type types;
+    verto_ctx_funcs funcs;
+    verto_ev *events;
+};
+
+typedef struct {
+    verto_proc proc;
+    verto_proc_status status;
+} verto_child;
+
+struct _verto_ev {
+    verto_ev *next;
+    verto_ctx *ctx;
+    verto_ev_type type;
+    verto_callback *callback;
+    verto_callback *onfree;
+    void *priv;
+    void *modpriv;
+    verto_ev_flag flags;
+    verto_ev_flag actual;
+    size_t depth;
+    int deleted;
+    union {
+        int fd;
+        int signal;
+        time_t interval;
+        verto_child child;
+    } option;
+};
+
+const verto_module *defmodule;
+
+static int
+_vasprintf(char **strp, const char *fmt, va_list ap) {
+    va_list apc;
+    int size = 0;
+
+    va_copy(apc, ap);
+    size = vsnprintf(NULL, 0, fmt, apc);
+    va_end(apc);
+
+    *strp = malloc(size + 1);
+    if (!size)
+        return -1;
+
+    return vsnprintf(*strp, size + 1, fmt, ap);
+}
+
+static int
+_asprintf(char **strp, const char *fmt, ...) {
+    va_list ap;
+    int size = 0;
+
+    va_start(ap, fmt);
+    size = _vasprintf(strp, fmt, ap);
+    va_end(ap);
+    return size;
+}
+
+static int
+do_load_file(const char *filename, int reqsym, verto_ev_type reqtypes,
+             pdlmtype *dll, const verto_module **module)
+{
+    *dll = pdlopenl(filename);
+    if (!*dll) {
+        /* printf("%s -- %s\n", filename, pdlerror()); */
+        return 0;
+    }
+
+    *module = (verto_module*) pdlsym(*dll, __str(VERTO_MODULE_TABLE));
+    if (!*module || (*module)->vers != VERTO_MODULE_VERSION
+            || !(*module)->new_ctx || !(*module)->def_ctx)
+        goto error;
+
+    /* Check to make sure that we have our required symbol if reqsym == true */
+    if ((*module)->symb && reqsym && !pdlsymlinked(NULL, (*module)->symb))
+        goto error;
+
+    /* Check to make sure that this module supports our required features */
+    if (reqtypes != VERTO_EV_TYPE_NONE && ((*module)->types & reqtypes) != reqtypes)
+        goto error;
+
+    /* Re-open in execution mode */
+    *dll = pdlreopen(filename, *dll);
+    if (!*dll)
+        return 0;
+
+    /* Get the module struct again */
+    *module = (verto_module*) pdlsym(*dll, __str(VERTO_MODULE_TABLE));
+    if (!*module)
+        goto error;
+
+    return 1;
+
+    error:
+        pdlclose(*dll);
+        return 0;
+}
+
+static int
+do_load_dir(const char *dirname, const char *prefix, const char *suffix,
+            int reqsym, verto_ev_type reqtypes, pdlmtype *dll,
+            const verto_module **module)
+{
+    *module = NULL;
+    DIR *dir = opendir(dirname);
+    if (!dir)
+        return 0;
+
+    struct dirent *ent = NULL;
+    while ((ent = readdir(dir))) {
+        size_t flen = strlen(ent->d_name);
+        size_t slen = strlen(suffix);
+
+        if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name))
+            continue;
+        if (strstr(ent->d_name, prefix) != ent->d_name)
+            continue;
+        if (flen < slen || strcmp(ent->d_name + flen - slen, suffix))
+            continue;
+
+        char *tmp = NULL;
+        if (_asprintf(&tmp, "%s/%s", dirname, ent->d_name) < 0)
+            continue;
+
+        int success = do_load_file(tmp, reqsym, reqtypes, dll, module);
+        free(tmp);
+        if (success)
+            break;
+        *module = NULL;
+    }
+
+    closedir(dir);
+    return *module != NULL;
+}
+
+static int
+load_module(const char *impl, verto_ev_type reqtypes, pdlmtype *dll,
+            const verto_module **module)
+{
+    int success = 0;
+    char *prefix = NULL;
+    char *suffix = NULL;
+    char *tmp = NULL;
+
+    if (!pdladdrmodname(verto_convert_funcs, &prefix))
+        return 0;
+
+    /* Example output:
+     *    prefix == /usr/lib/libverto-
+     *    impl == glib
+     *    suffix == .so.0
+     * Put them all together: /usr/lib/libverto-glib.so.0 */
+    suffix = strstr(prefix, pdlmsuffix);
+    if (!suffix || strlen(suffix) < 1 || !(suffix = strdup(suffix))) {
+        free(prefix);
+        return 0;
+    }
+    strcpy(prefix + strlen(prefix) - strlen(suffix), "-");
+
+    if (impl) {
+        /* Try to do a load by the path */
+        if (strchr(impl, '/'))
+            success = do_load_file(impl, 0, reqtypes, dll, module);
+        if (!success) {
+            /* Try to do a load by the name */
+            tmp = NULL;
+            if (_asprintf(&tmp, "%s%s%s", prefix, impl, suffix) > 0) {
+                success = do_load_file(tmp, 0, reqtypes, dll, module);
+                free(tmp);
+            }
+        }
+    } else {
+        /* First, try the default implementation (aka 'the cache')*/
+        *dll = NULL;
+        *module = NULL;
+
+        if (defmodule != NULL
+                && (reqtypes == VERTO_EV_TYPE_NONE
+                        || (defmodule->types & reqtypes) == reqtypes))
+            *module = defmodule;
+
+        if (!(success = *module != NULL)) {
+            /* NULL was passed, so we will use the dirname of
+             * the prefix to try and find any possible plugins */
+            tmp = strdup(prefix);
+            if (tmp) {
+                char *dname = strdup(dirname(tmp));
+                free(tmp);
+
+                tmp = strdup(basename(prefix));
+                free(prefix);
+                prefix = tmp;
+
+                if (dname && prefix) {
+                    /* Attempt to find a module we are already linked to */
+                    success = do_load_dir(dname, prefix, suffix, 1, reqtypes,
+                                          dll, module);
+                    if (!success) {
+#ifdef DEFAULT_LIBRARY
+                        /* Attempt to find the default module */
+                        success = load_module(DEFAULT_LIBRARY, reqtypes, dll, module);
+                        if (!success)
+#endif /* DEFAULT_LIBRARY */
+                        /* Attempt to load any plugin (we're desperate) */
+                        success = do_load_dir(dname, prefix, suffix, 0,
+                                              reqtypes, dll, module);
+                    }
+                }
+
+                free(dname);
+            }
+        }
+    }
+
+    free(suffix);
+    free(prefix);
+    return success;
+}
+
+static verto_ev *
+make_ev(verto_ctx *ctx, verto_callback *callback,
+        verto_ev_type type, verto_ev_flag flags)
+{
+    verto_ev *ev = NULL;
+
+    if (!ctx || !callback)
+        return NULL;
+
+    ev = malloc(sizeof(verto_ev));
+    if (ev) {
+        memset(ev, 0, sizeof(verto_ev));
+        ev->ctx        = ctx;
+        ev->type       = type;
+        ev->callback   = callback;
+        ev->flags      = flags;
+    }
+
+    return ev;
+}
+
+static void
+push_ev(verto_ctx *ctx, verto_ev *ev)
+{
+    if (!ctx || !ev)
+        return;
+
+    verto_ev *tmp = ctx->events;
+    ctx->events = ev;
+    ctx->events->next = tmp;
+}
+
+static void
+remove_ev(verto_ev **origin, verto_ev *item)
+{
+    if (!origin || !*origin || !item)
+        return;
+
+    if (*origin == item)
+        *origin = (*origin)->next;
+    else
+        remove_ev(&((*origin)->next), item);
+}
+
+static void
+signal_ignore(verto_ctx *ctx, verto_ev *ev)
+{
+}
+
+verto_ctx *
+verto_new(const char *impl, verto_ev_type reqtypes)
+{
+    pdlmtype dll = NULL;
+    const verto_module *module = NULL;
+    verto_ctx *ctx = NULL;
+
+    if (!load_module(impl, reqtypes, &dll, &module))
+        return NULL;
+
+    ctx = module->new_ctx();
+    if (!ctx && dll)
+        pdlclose(dll);
+    if (ctx && defmodule != module)
+        ctx->dll = dll;
+
+    return ctx;
+}
+
+verto_ctx *
+verto_default(const char *impl, verto_ev_type reqtypes)
+{
+    pdlmtype dll = NULL;
+    const verto_module *module = NULL;
+    verto_ctx *ctx = NULL;
+
+    if (!load_module(impl, reqtypes, &dll, &module))
+        return NULL;
+
+    ctx = module->def_ctx();
+    if (!ctx && dll)
+        pdlclose(dll);
+    if (ctx && defmodule != module)
+        ctx->dll = dll;
+
+    return ctx;
+}
+
+int
+verto_set_default(const char *impl, verto_ev_type reqtypes)
+{
+    pdlmtype dll = NULL; /* we will leak the dll */
+    return (!defmodule && impl && load_module(impl, reqtypes, &dll, &defmodule));
+}
+
+void
+verto_free(verto_ctx *ctx)
+{
+#ifndef WIN32
+    int i;
+    sigset_t old;
+    sigset_t block;
+    struct sigaction act;
+#endif
+
+    if (!ctx)
+        return;
+
+    /* Cancel all pending events */
+    while (ctx->events)
+        verto_del(ctx->events);
+
+    /* Free the private */
+    ctx->funcs.ctx_free(ctx->modpriv);
+
+    /* Unload the module */
+    if (ctx->dll) {
+        /* If dlclose() unmaps memory that is registered as a signal handler
+         * we have to remove that handler otherwise if that signal is fired
+         * we jump into unmapped memory. So we loop through and test each
+         * handler to see if it is in unmapped memory.  If it is, we set it
+         * back to the default handler. Lastly, if a signal were to fire it
+         * could be a race condition. So we mask out all signals during this
+         * process.
+         */
+#ifndef WIN32
+        sigfillset(&block);
+        sigprocmask(SIG_SETMASK, &block, &old);
+#endif
+        pdlclose(ctx->dll);
+#ifndef WIN32
+        for (i=1 ; i < NSIG ; i++) {
+            if (sigaction(i, NULL, &act) == 0) {
+                if (act.sa_flags & SA_SIGINFO) {
+                    if (!pdladdrmodname(act.sa_sigaction, NULL))
+                        signal(i, SIG_DFL);
+                } else if (act.sa_handler != SIG_DFL
+                        && act.sa_handler != SIG_IGN) {
+                    if (!pdladdrmodname(act.sa_handler, NULL))
+                        signal(i, SIG_DFL);
+                }
+            }
+        }
+        sigprocmask(SIG_SETMASK, &old, NULL);
+#endif
+    }
+
+    free(ctx);
+}
+
+void
+verto_run(verto_ctx *ctx)
+{
+    if (!ctx)
+        return;
+    ctx->funcs.ctx_run(ctx->modpriv);
+}
+
+void
+verto_run_once(verto_ctx *ctx)
+{
+    if (!ctx)
+        return;
+    ctx->funcs.ctx_run_once(ctx->modpriv);
+}
+
+void
+verto_break(verto_ctx *ctx)
+{
+    if (!ctx)
+        return;
+    ctx->funcs.ctx_break(ctx->modpriv);
+}
+
+#define doadd(set, type) \
+    verto_ev *ev = make_ev(ctx, callback, type, flags); \
+    if (ev) { \
+        set; \
+        ev->actual = ev->flags; \
+        ev->modpriv = ctx->funcs.ctx_add(ctx->modpriv, ev, &ev->actual); \
+        if (!ev->modpriv) { \
+            free(ev); \
+            return NULL; \
+        } \
+        push_ev(ctx, ev); \
+    } \
+    return ev;
+
+verto_ev *
+verto_add_io(verto_ctx *ctx, verto_ev_flag flags,
+             verto_callback *callback, int fd)
+{
+    if (fd < 0 || !(flags & (VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_IO_WRITE)))
+        return NULL;
+    doadd(ev->option.fd = fd, VERTO_EV_TYPE_IO);
+}
+
+verto_ev *
+verto_add_timeout(verto_ctx *ctx, verto_ev_flag flags,
+                  verto_callback *callback, time_t interval)
+{
+    doadd(ev->option.interval = interval, VERTO_EV_TYPE_TIMEOUT);
+}
+
+verto_ev *
+verto_add_idle(verto_ctx *ctx, verto_ev_flag flags,
+               verto_callback *callback)
+{
+    doadd(, VERTO_EV_TYPE_IDLE);
+}
+
+verto_ev *
+verto_add_signal(verto_ctx *ctx, verto_ev_flag flags,
+                 verto_callback *callback, int signal)
+{
+    if (signal < 0)
+        return NULL;
+#ifndef WIN32
+    if (signal == SIGCHLD)
+        return NULL;
+#endif
+    if (callback == VERTO_SIG_IGN) {
+        callback = signal_ignore;
+        if (!(flags & VERTO_EV_FLAG_PERSIST))
+            return NULL;
+    }
+    doadd(ev->option.signal = signal, VERTO_EV_TYPE_SIGNAL);
+}
+
+verto_ev *
+verto_add_child(verto_ctx *ctx, verto_ev_flag flags,
+                verto_callback *callback, verto_proc proc)
+{
+    if (flags & VERTO_EV_FLAG_PERSIST) /* persist makes no sense */
+        return NULL;
+#ifdef WIN32
+    if (proc == NULL)
+#else
+    if (proc < 1)
+#endif
+        return NULL;
+    doadd(ev->option.child.proc = proc, VERTO_EV_TYPE_CHILD);
+}
+
+int
+verto_set_private(verto_ev *ev, void *priv, verto_callback *free)
+{
+    if (!ev)
+        return 0;
+    if (ev->onfree && free)
+        ev->onfree(ev->ctx, ev);
+    ev->priv = priv;
+    ev->onfree = free;
+    return 1;
+}
+
+void *
+verto_get_private(const verto_ev *ev)
+{
+    return ev->priv;
+}
+
+verto_ev_type
+verto_get_type(const verto_ev *ev)
+{
+    return ev->type;
+}
+
+verto_ev_flag
+verto_get_flags(const verto_ev *ev)
+{
+    return ev->flags;
+}
+
+int
+verto_get_fd(const verto_ev *ev)
+{
+    if (ev && (ev->type == VERTO_EV_TYPE_IO))
+        return ev->option.fd;
+    return -1;
+}
+
+time_t
+verto_get_interval(const verto_ev *ev)
+{
+    if (ev && (ev->type == VERTO_EV_TYPE_TIMEOUT))
+        return ev->option.interval;
+    return 0;
+}
+
+int
+verto_get_signal(const verto_ev *ev)
+{
+    if (ev && (ev->type == VERTO_EV_TYPE_SIGNAL))
+        return ev->option.signal;
+    return -1;
+}
+
+verto_proc
+verto_get_proc(const verto_ev *ev) {
+    if (ev && ev->type == VERTO_EV_TYPE_CHILD)
+        return ev->option.child.proc;
+    return (verto_proc) 0;
+}
+
+verto_proc_status
+verto_get_proc_status(const verto_ev *ev)
+{
+    return ev->option.child.status;
+}
+
+void
+verto_del(verto_ev *ev)
+{
+    if (!ev)
+        return;
+
+    /* If the event is freed in the callback, we just set a flag so that
+     * verto_fire() can actually do the delete when the callback completes.
+     *
+     * If we don't do this, than verto_fire() will access freed memory. */
+    if (ev->depth > 0) {
+        ev->deleted = 1;
+        return;
+    }
+
+    if (ev->onfree)
+        ev->onfree(ev->ctx, ev);
+    ev->ctx->funcs.ctx_del(ev->ctx->modpriv, ev, ev->modpriv);
+    remove_ev(&(ev->ctx->events), ev);
+    free(ev);
+}
+
+verto_ev_type
+verto_get_supported_types(verto_ctx *ctx)
+{
+    return ctx->types;
+}
+
+/*** THE FOLLOWING ARE FOR IMPLEMENTATION MODULES ONLY ***/
+
+verto_ctx *
+verto_convert_funcs(const verto_ctx_funcs *funcs,
+               const verto_module *module,
+               void *ctx_private)
+{
+    verto_ctx *ctx = NULL;
+
+    if (!funcs || !module || !ctx_private)
+        return NULL;
+
+    ctx = vnew0(verto_ctx);
+    if (!ctx)
+        return NULL;
+
+    ctx->modpriv = ctx_private;
+    ctx->funcs = *funcs;
+    ctx->types = module->types;
+
+    if (!defmodule)
+        defmodule = module;
+
+    return ctx;
+}
+
+void
+verto_fire(verto_ev *ev)
+{
+    void *priv;
+
+    ev->depth++;
+    ev->callback(ev->ctx, ev);
+    ev->depth--;
+
+    if (ev->depth == 0) {
+        if (!(ev->flags & VERTO_EV_FLAG_PERSIST) || ev->deleted)
+            verto_del(ev);
+        else if (!ev->actual & VERTO_EV_FLAG_PERSIST) {
+            ev->actual = ev->flags;
+            priv = ev->ctx->funcs.ctx_add(ev->ctx->modpriv, ev, &ev->actual);
+            assert(priv); /* TODO: create an error callback */
+            ev->ctx->funcs.ctx_del(ev->ctx->modpriv, ev, ev->modpriv);
+            ev->modpriv = priv;
+        }
+    }
+}
+
+void
+verto_set_proc_status(verto_ev *ev, verto_proc_status status)
+{
+    if (ev && ev->type == VERTO_EV_TYPE_CHILD)
+        ev->option.child.status = status;
+}
diff --git a/src/util/verto/verto.h b/src/util/verto/verto.h
new file mode 100644 (file)
index 0000000..452638d
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef VERTO_H_
+#define VERTO_H_
+
+#include <time.h>   /* For time_t */
+#include <unistd.h> /* For pid_t */
+
+#ifdef WIN32
+#include <windows.h>
+typedef HANDLE verto_proc;
+typedef DWORD verto_proc_status;
+#else
+typedef pid_t verto_proc;
+typedef int verto_proc_status;
+#endif
+
+#define VERTO_SIG_IGN ((verto_callback *) 1)
+
+typedef struct _verto_ctx verto_ctx;
+typedef struct _verto_ev verto_ev;
+
+typedef enum {
+    VERTO_EV_TYPE_NONE = 0,
+    VERTO_EV_TYPE_IO = 1,
+    VERTO_EV_TYPE_TIMEOUT = 1 << 1,
+    VERTO_EV_TYPE_IDLE = 1 << 2,
+    VERTO_EV_TYPE_SIGNAL = 1 << 3,
+    VERTO_EV_TYPE_CHILD = 1 << 4
+} verto_ev_type;
+
+typedef enum {
+    VERTO_EV_FLAG_NONE = 0,
+    VERTO_EV_FLAG_PERSIST = 1,
+    VERTO_EV_FLAG_PRIORITY_LOW = 1 << 1,
+    VERTO_EV_FLAG_PRIORITY_MEDIUM = 1 << 2,
+    VERTO_EV_FLAG_PRIORITY_HIGH = 1 << 3,
+    VERTO_EV_FLAG_IO_READ = 1 << 4,
+    VERTO_EV_FLAG_IO_WRITE = 1 << 5,
+    _VERTO_EV_FLAG_MAX = VERTO_EV_FLAG_IO_WRITE
+} verto_ev_flag;
+
+typedef void (verto_callback)(verto_ctx *ctx, verto_ev *ev);
+
+/**
+ * Creates a new event context using an optionally specified implementation
+ * and/or optionally specified required features.
+ *
+ * If you are an application that has already decided on using a particular
+ * event loop implementation, you should not call this function, but instead
+ * import the verto-NAME.h header and link against the verto-NAME.so, where
+ * NAME is the implementation you wish to use.
+ *
+ * If you are a library, you should generally avoid creating event contexts
+ * on your own but allow applications to pass in a verto_ctx you can use.
+ *
+ * There are two cases where you should use this function.  The first is
+ * where you have a need to choose an implementation at run time, usually
+ * for testing purposes.  The second and more common is when you simply
+ * wish to remain implementation agnostic.  In this later case, you should
+ * always call like this: verto_new(NULL, ...).  This lets verto choose the best
+ * implementation to use.
+ *
+ * If impl is not NULL, a new context is returned which is backed by the
+ * implementation specified. If the implementation specified is not
+ * available or if the required types (reqtypes) are not provided by the
+ * named implementation, NULL is returned. The parameter 'impl' can specify:
+ *   * The full path to an implementation library
+ *   * The name of the implementation library (i.e. - "glib" or "libev")
+ *
+ * If impl is NULL, verto will attempt to automatically determine the
+ * best implementation to use.
+ *
+ * First, verto will attempt to use an existing, previously loaded
+ * implementation. This is handled automatically by internal caching of either
+ * the first implementation loaded or the one specified by verto_set_default().
+ *
+ * Second, verto will attempt to discern if you are already linked to any
+ * of the supported implementations (to avoid wasting memory by loading
+ * extra unnecessary libraries).  If you are linked to one supported
+ * implementation, that implementation will be chosen.  If you are linked
+ * to more than one supported implementation one of the ones linked to
+ * will be chosen, but the order of the particular choice is undefined.
+ *
+ * Third, verto will attempt to load the compile-time default, if defined at
+ * build time and available at runtime.
+ *
+ * Last, verto will attempt to load any implementation installed. The specific
+ * order of this step is undefined.
+ *
+ * In all cases above, if the implementation does not support all the specified
+ * features (reqtypes), it will be skipped and processing will continue from
+ * where it left off. This means that if verto_new() returns non-NULL it is
+ * guaranteed to support the features you specified.
+ *
+ * @see verto_set_default()
+ * @param impl The implementation to use, or NULL.
+ * @param reqtypes A bitwise or'd list of required event type features.
+ * @return A new _ev_ctx, or NULL on error.  Call verto_free() when done.
+ */
+verto_ctx *
+verto_new(const char *impl, verto_ev_type reqtypes);
+
+/**
+ * Gets the default event context using an optionally specified implementation.
+ *
+ * This function is essentially a singleton version of verto_new().  However,
+ * since this function must return the same loop as the *_default() call of
+ * the underlying implementation (if such a function exists), it is NOT a
+ * global singleton, but a per-implementation singleton. For this reason, you
+ * must call verto_free() when you are done with this loop. Even after calling
+ * verto_free() on the default verto_ctx, you can safely call verto_default()
+ * again and receive a new reference to the same (internally default) loop.
+ *
+ * In all other respects, verto_default() acts exactly like verto_new().
+ *
+ * @see verto_new()
+ * @see verto_free()
+ * @param impl The implementation to use, or NULL.
+ * @param reqtypes A bitwise or'd list of required event type features.
+ * @return The default _ev_ctx, or NULL on error.  Call verto_free() when done.
+ */
+verto_ctx *
+verto_default(const char *impl, verto_ev_type reqtypes);
+
+/**
+ * Sets the default implementation to use by its name.
+ *
+ * This function returns 1 on success and 0 on failure.  It can fail for the
+ * following reasons:
+ *   1. The default implementation was already set via verto_set_default().
+ *   2. The implementation specified could not be found.
+ *   3. The implementation specified didn't support the features specified.
+ *   4. The impl argument was NULL.
+ *   5. verto_new() was already called.
+ *   6. verto_default() was already called.
+ *   7. verto_new_NAME() was already called.
+ *   8. verto_default_NAME() was already called.
+ *   9. verto_convert_NAME() was already called.
+ *
+ * @see verto_new()
+ * @see verto_default()
+ * @param impl The implementation to use.
+ * @param reqtypes A bitwise or'd list of required event type features.
+ * @return The default _ev_ctx, or NULL on error.  Call verto_free() when done.
+ */
+int
+verto_set_default(const char *impl, verto_ev_type reqtypes);
+
+/**
+ * Frees a verto_ctx.
+ *
+ * When called on a default verto_ctx, the reference will be freed but the
+ * internal default loop will still be available via another call to
+ * verto_default().
+ *
+ * @see verto_new()
+ * @see verto_default()
+ * @param ctx The verto_ctx to free.
+ */
+void
+verto_free(verto_ctx *ctx);
+
+/**
+ * Run the verto_ctx forever, or at least until verto_break() is called.
+ *
+ * @see verto_break()
+ * @param ctx The verto_ctx to run.
+ */
+void
+verto_run(verto_ctx *ctx);
+
+/**
+ * Run the verto_ctx once. May block.
+ *
+ * @param ctx The verto_ctx to run once.
+ */
+void
+verto_run_once(verto_ctx *ctx);
+
+/**
+ * Exits the currently running verto_ctx.
+ *
+ * @see verto_run()
+ * @param ctx The verto_ctx to exit.
+ */
+void
+verto_break(verto_ctx *ctx);
+
+/**
+ * Adds a callback executed when a file descriptor is ready to be read/written.
+ *
+ * All verto_ev events are automatically freed when their parent verto_ctx is
+ * freed. You do not need to free them manually. If VERTO_EV_FLAG_PERSIST is
+ * provided, the event will repeat until verto_del() is called. If
+ * VERTO_EV_FLAG_PERSIST is not provided, the event will be freed automatically
+ * after its execution. In either case, you may call verto_del() at any time
+ * to prevent the event from executing.
+ *
+ * NOTE: On Windows, the underlying select() only works with sockets. As such,
+ * any attempt to add a non-socket io event on Windows will produce undefined
+ * results and may even crash.
+ *
+ * @see verto_del()
+ * @param ctx The verto_ctx which will fire the callback.
+ * @param flags The flags to set (at least one VERTO_EV_FLAG_IO* required).
+ * @param callback The callback to fire.
+ * @param fd The file descriptor to watch for reads.
+ * @return The verto_ev registered with the event context or NULL on error.
+ */
+verto_ev *
+verto_add_io(verto_ctx *ctx, verto_ev_flag flags,
+             verto_callback *callback, int fd);
+
+/**
+ * Adds a callback executed after a period of time.
+ *
+ * All verto_ev events are automatically freed when their parent verto_ctx is
+ * freed. You do not need to free them manually. If VERTO_EV_FLAG_PERSIST is
+ * provided, the event will repeat until verto_del() is called. If
+ * VERTO_EV_FLAG_PERSIST is not provided, the event will be freed automatically
+ * after its execution. In either case, you may call verto_del() at any time
+ * to prevent the event from executing.
+ *
+ * @see verto_del()
+ * @param ctx The verto_ctx which will fire the callback.
+ * @param flags The flags to set.
+ * @param callback The callback to fire.
+ * @param interval Time period to wait before firing (in milliseconds).
+ * @return The verto_ev registered with the event context.
+ */
+verto_ev *
+verto_add_timeout(verto_ctx *ctx, verto_ev_flag flags,
+                  verto_callback *callback, time_t interval);
+
+/**
+ * Adds a callback executed when there is nothing else to do.
+ *
+ * All verto_ev events are automatically freed when their parent verto_ctx is
+ * freed. You do not need to free them manually. If VERTO_EV_FLAG_PERSIST is
+ * provided, the event will repeat until verto_del() is called. If
+ * VERTO_EV_FLAG_PERSIST is not provided, the event will be freed automatically
+ * after its execution. In either case, you may call verto_del() at any time
+ * to prevent the event from executing.
+ *
+ * @see verto_del()
+ * @param ctx The verto_ctx which will fire the callback.
+ * @param flags The flags to set.
+ * @param callback The callback to fire.
+ * @return The verto_ev registered with the event context.
+ */
+verto_ev *
+verto_add_idle(verto_ctx *ctx, verto_ev_flag flags,
+               verto_callback *callback);
+
+/**
+ * Adds a callback executed when a signal is received.
+ *
+ * All verto_ev events are automatically freed when their parent verto_ctx is
+ * freed. You do not need to free them manually. If VERTO_EV_FLAG_PERSIST is
+ * provided, the event will repeat until verto_del() is called. If
+ * VERTO_EV_FLAG_PERSIST is not provided, the event will be freed automatically
+ * after its execution. In either case, you may call verto_del() at any time
+ * to prevent the event from executing.
+ *
+ * NOTE: If you attempt to ignore a signal without the VERTO_EV_FLAG_PERSIST
+ * flag, this function fails.
+ *
+ * NOTE: SIGCHLD is expressly not supported. If you want this notification,
+ * please use verto_add_child().
+ *
+ * WARNNIG: Signal events can only be reliably received in the default _ev_ctx
+ * in some implementations.  Attempting to receive signal events in non-default
+ * loops may result in assert() failures.
+ *
+ * WARNING: While verto does its best to protect you from crashes, there is
+ * essentially no way to do signal events if you mix multiple implementations in
+ * a single process. Attempting to do so will result in undefined behavior,
+ * and potentially even a crash. You have been warned.
+ *
+ * @see verto_add_child()
+ * @see verto_repeat()
+ * @see verto_del()
+ * @param ctx The verto_ctx which will fire the callback.
+ * @param flags The flags to set.
+ * @param callback The callback to fire.
+ * @param signal The signal to watch for.
+ * @return The verto_ev registered with the event context.
+ */
+verto_ev *
+verto_add_signal(verto_ctx *ctx, verto_ev_flag flags,
+                 verto_callback *callback, int signal);
+
+/**
+ * Adds a callback executed when a child process exits.
+ *
+ * This event will be freed automatically after its execution. Due to the
+ * nature of a process' life-cycle, child events cannot persist (processes only
+ * exit once). This function returns NULL if you attempt to use
+ * VERTO_EV_FLAG_PERSIST. You may, of course, call verto_del() at any time to
+ * prevent the callback from firing.
+ *
+ * @see verto_del()
+ * @param ctx The verto_ctx which will fire the callback.
+ * @param flags The flags to set.
+ * @param callback The callback to fire.
+ * @param child The pid (POSIX) or handle (Win32) of the child to watch for.
+ * @return The verto_ev registered with the event context.
+ */
+verto_ev *
+verto_add_child(verto_ctx *ctx, verto_ev_flag flags,
+                verto_callback *callback, verto_proc proc);
+
+/**
+ * Sets the private pointer of the verto_ev.
+ *
+ * The free callback will be called in two cases:
+ *   1. When the event is deleted (manually or automatically)
+ *   2. When verto_set_private() is called again, unless
+ *      free is NULL.
+ *
+ * @see verto_get_private()
+ * @param ev The verto_ev
+ * @param priv The private value to store
+ * @param free The callback used to free the data or NULL
+ * @return 1 on success or 0 on failure
+ */
+int
+verto_set_private(verto_ev *ev, void *priv, verto_callback *free);
+
+/**
+ * Gets the private pointer of the verto_ev.
+ *
+ * @see verto_set_private()
+ * @param ev The verto_ev
+ * @return The verto_ev private pointer
+ */
+void *
+verto_get_private(const verto_ev *ev);
+
+/**
+ * Gets the type of the verto_ev.
+ *
+ * @see verto_add_io()
+ * @see verto_add_timeout()
+ * @see verto_add_idle()
+ * @see verto_add_signal()
+ * @see verto_add_child()
+ * @param ev The verto_ev
+ * @return The verto_ev type
+ */
+verto_ev_type
+verto_get_type(const verto_ev *ev);
+
+/**
+ * Gets the flags associated with the given verto_ev.
+ *
+ * @see verto_add_io()
+ * @see verto_add_timeout()
+ * @see verto_add_idle()
+ * @see verto_add_signal()
+ * @see verto_add_child()
+ * @param ev The verto_ev
+ * @return The verto_ev type
+ */
+verto_ev_flag
+verto_get_flags(const verto_ev *ev);
+
+/**
+ * Gets the file descriptor associated with a read/write verto_ev.
+ *
+ * @see verto_add_io()
+ * @param ev The verto_ev to retrieve the file descriptor from.
+ * @return The file descriptor, or -1 if not a read/write event.
+ */
+int
+verto_get_fd(const verto_ev *ev);
+
+/**
+ * Gets the interval associated with a timeout verto_ev.
+ *
+ * @see verto_add_timeout()
+ * @param ev The verto_ev to retrieve the interval from.
+ * @return The interval, or 0 if not a timeout event.
+ */
+time_t
+verto_get_interval(const verto_ev *ev);
+
+/**
+ * Gets the signal associated with a signal verto_ev.
+ *
+ * @see verto_add_signal()
+ * @param ev The verto_ev to retrieve the signal from.
+ * @return The signal, or -1 if not a signal event.
+ */
+int
+verto_get_signal(const verto_ev *ev);
+
+/**
+ * Gets the process associated with a child verto_ev.
+ *
+ * @see verto_add_child()
+ * @param ev The verto_ev to retrieve the process from.
+ * @return The pid/handle, or 0/NULL if not a child event (POSIX/Win32).
+ */
+verto_proc
+verto_get_proc(const verto_ev *ev);
+
+/**
+ * Gets the status of the process which caused this event to fire.
+ *
+ * @see verto_add_child()
+ * @param ev The verto_ev to retrieve the status from.
+ * @return The pid/handle status.
+ */
+verto_proc_status
+verto_get_proc_status(const verto_ev *ev);
+
+/**
+ * Removes an event from from the event context and frees it.
+ *
+ * The event and its contents cannot be used after this call.
+ *
+ * @see verto_add_io()
+ * @see verto_add_timeout()
+ * @see verto_add_idle()
+ * @see verto_add_signal()
+ * @see verto_add_child()
+ * @param ev The event to delete.
+ */
+void
+verto_del(verto_ev *ev);
+
+/**
+ * Returns the event types supported by this implementation.
+ *
+ * @param ctx The verto_ctx to query.
+ * @return The event types supported.
+ */
+verto_ev_type
+verto_get_supported_types(verto_ctx *ctx);
+
+#endif /* VERTO_H_ */