--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */