From 040441eb53f60e42f3a7e8855f8414b34f8648b3 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Fri, 2 Sep 2011 17:07:35 +0000 Subject: [PATCH] Add libverto sources in util/verto 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 | 135 ++++++ src/util/verto/verto.c | 764 ++++++++++++++++++++++++++++++++++ src/util/verto/verto.h | 465 +++++++++++++++++++++ 3 files changed, 1364 insertions(+) create mode 100644 src/util/verto/verto-module.h create mode 100644 src/util/verto/verto.c create mode 100644 src/util/verto/verto.h diff --git a/src/util/verto/verto-module.h b/src/util/verto/verto-module.h new file mode 100644 index 000000000..89d6e5611 --- /dev/null +++ b/src/util/verto/verto-module.h @@ -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 + +#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 index 000000000..f59e8bae2 --- /dev/null +++ b/src/util/verto/verto.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include +#endif + +#include + +#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 index 000000000..452638de8 --- /dev/null +++ b/src/util/verto/verto.h @@ -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 /* For time_t */ +#include /* For pid_t */ + +#ifdef WIN32 +#include +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_ */ -- 2.26.2