From 3c0e396987e736a04cfa19052177d4d06e6714c8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 22 Jan 2011 14:09:59 +1030 Subject: [PATCH] lca2011: hacky import of tevent. --- ccan/tevent/_info | 23 ++ ccan/tevent/tevent.c | 457 +++++++++++++++++++++++ ccan/tevent/tevent.h | 664 +++++++++++++++++++++++++++++++++ ccan/tevent/tevent_debug.c | 95 +++++ ccan/tevent/tevent_fd.c | 88 +++++ ccan/tevent/tevent_immediate.c | 138 +++++++ ccan/tevent/tevent_internal.h | 181 +++++++++ ccan/tevent/tevent_select.c | 250 +++++++++++++ ccan/tevent/tevent_signal.c | 424 +++++++++++++++++++++ ccan/tevent/tevent_standard.c | 567 ++++++++++++++++++++++++++++ ccan/tevent/tevent_timed.c | 267 +++++++++++++ ccan/tevent/tevent_util.c | 88 +++++ ccan/tevent/tevent_util.h | 198 ++++++++++ 13 files changed, 3440 insertions(+) create mode 100644 ccan/tevent/_info create mode 100644 ccan/tevent/tevent.c create mode 100644 ccan/tevent/tevent.h create mode 100644 ccan/tevent/tevent_debug.c create mode 100644 ccan/tevent/tevent_fd.c create mode 100644 ccan/tevent/tevent_immediate.c create mode 100644 ccan/tevent/tevent_internal.h create mode 100644 ccan/tevent/tevent_select.c create mode 100644 ccan/tevent/tevent_signal.c create mode 100644 ccan/tevent/tevent_standard.c create mode 100644 ccan/tevent/tevent_timed.c create mode 100644 ccan/tevent/tevent_util.c create mode 100644 ccan/tevent/tevent_util.h diff --git a/ccan/tevent/_info b/ccan/tevent/_info new file mode 100644 index 0000000..1d50b73 --- /dev/null +++ b/ccan/tevent/_info @@ -0,0 +1,23 @@ +#include +#include "config.h" + +/** + * tevent - reduced version of the SAMBA tevent library + * + * Cut down and modified for CCAN as an example. + * + * License: GPL + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/talloc\n"); + return 0; + } + + return 1; +} diff --git a/ccan/tevent/tevent.c b/ccan/tevent/tevent.c new file mode 100644 index 0000000..a1825a7 --- /dev/null +++ b/ccan/tevent/tevent.c @@ -0,0 +1,457 @@ +/* + Unix SMB/CIFS implementation. + main select loop and event handling + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* + PLEASE READ THIS BEFORE MODIFYING! + + This module is a general abstraction for the main select loop and + event handling. Do not ever put any localised hacks in here, instead + register one of the possible event types and implement that event + somewhere else. + + There are 2 types of event handling that are handled in this module: + + 1) a file descriptor becoming readable or writeable. This is mostly + used for network sockets, but can be used for any type of file + descriptor. You may only register one handler for each file + descriptor/io combination or you will get unpredictable results + (this means that you can have a handler for read events, and a + separate handler for write events, but not two handlers that are + both handling read events) + + 2) a timed event. You can register an event that happens at a + specific time. You can register as many of these as you + like. They are single shot - add a new timed event in the event + handler to get another event. + + To setup a set of events you first need to create a event_context + structure using the function tevent_context_init(); This returns a + 'struct tevent_context' that you use in all subsequent calls. + + After that you can add/remove events that you are interested in + using tevent_add_*() and talloc_free() + + Finally, you call tevent_loop_wait_once() to block waiting for one of the + events to occor or tevent_loop_wait() which will loop + forever. + +*/ +#include +#include +#include +#include +#include +#include + +struct tevent_ops_list { + struct tevent_ops_list *next, *prev; + const char *name; + const struct tevent_ops *ops; +}; + +/* list of registered event backends */ +static struct tevent_ops_list *tevent_backends = NULL; +static char *tevent_default_backend = NULL; + +/* + register an events backend +*/ +bool tevent_register_backend(const char *name, const struct tevent_ops *ops) +{ + struct tevent_ops_list *e; + + for (e = tevent_backends; e != NULL; e = e->next) { + if (0 == strcmp(e->name, name)) { + /* already registered, skip it */ + return true; + } + } + + e = talloc(NULL, struct tevent_ops_list); + if (e == NULL) return false; + + e->name = name; + e->ops = ops; + DLIST_ADD(tevent_backends, e); + + return true; +} + +/* + set the default event backend + */ +void tevent_set_default_backend(const char *backend) +{ + talloc_free(tevent_default_backend); + tevent_default_backend = talloc_strdup(NULL, backend); +} + +/* + initialise backends if not already done +*/ +static void tevent_backend_init(void) +{ + tevent_select_init(); + tevent_standard_init(); +#ifdef HAVE_EPOLL + tevent_epoll_init(); +#endif +} + +/* + list available backends +*/ +const char **tevent_backend_list(TALLOC_CTX *mem_ctx) +{ + const char **list = NULL; + struct tevent_ops_list *e; + + tevent_backend_init(); + + for (e=tevent_backends;e;e=e->next) { + list = ev_str_list_add(list, e->name); + } + + talloc_steal(mem_ctx, list); + + return list; +} + +int tevent_common_context_destructor(struct tevent_context *ev) +{ + struct tevent_fd *fd, *fn; + struct tevent_timer *te, *tn; + struct tevent_immediate *ie, *in; + struct tevent_signal *se, *sn; + + if (ev->pipe_fde) { + talloc_free(ev->pipe_fde); + close(ev->pipe_fds[0]); + close(ev->pipe_fds[1]); + ev->pipe_fde = NULL; + } + + for (fd = ev->fd_events; fd; fd = fn) { + fn = fd->next; + fd->event_ctx = NULL; + DLIST_REMOVE(ev->fd_events, fd); + } + + for (te = ev->timer_events; te; te = tn) { + tn = te->next; + te->event_ctx = NULL; + DLIST_REMOVE(ev->timer_events, te); + } + + for (ie = ev->immediate_events; ie; ie = in) { + in = ie->next; + ie->event_ctx = NULL; + ie->cancel_fn = NULL; + DLIST_REMOVE(ev->immediate_events, ie); + } + + for (se = ev->signal_events; se; se = sn) { + sn = se->next; + se->event_ctx = NULL; + DLIST_REMOVE(ev->signal_events, se); + /* + * This is important, Otherwise signals + * are handled twice in child. eg, SIGHUP. + * one added in parent, and another one in + * the child. -- BoYang + */ + tevent_cleanup_pending_signal_handlers(se); + } + + return 0; +} + +/* + create a event_context structure for a specific implemementation. + This must be the first events call, and all subsequent calls pass + this event_context as the first element. Event handlers also + receive this as their first argument. + + This function is for allowing third-party-applications to hook in gluecode + to their own event loop code, so that they can make async usage of our client libs + + NOTE: use tevent_context_init() inside of samba! +*/ +static struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx, + const struct tevent_ops *ops) +{ + struct tevent_context *ev; + int ret; + + ev = talloc_zero(mem_ctx, struct tevent_context); + if (!ev) return NULL; + + talloc_set_destructor(ev, tevent_common_context_destructor); + + ev->ops = ops; + + ret = ev->ops->context_init(ev); + if (ret != 0) { + talloc_free(ev); + return NULL; + } + + return ev; +} + +/* + create a event_context structure. This must be the first events + call, and all subsequent calls pass this event_context as the first + element. Event handlers also receive this as their first argument. +*/ +struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx, + const char *name) +{ + struct tevent_ops_list *e; + + tevent_backend_init(); + + if (name == NULL) { + name = tevent_default_backend; + } + if (name == NULL) { + name = "standard"; + } + + for (e=tevent_backends;e;e=e->next) { + if (strcmp(name, e->name) == 0) { + return tevent_context_init_ops(mem_ctx, e->ops); + } + } + return NULL; +} + + +/* + create a event_context structure. This must be the first events + call, and all subsequent calls pass this event_context as the first + element. Event handlers also receive this as their first argument. +*/ +struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx) +{ + return tevent_context_init_byname(mem_ctx, NULL); +} + +/* + add a fd based event + return NULL on failure (memory allocation error) +*/ +struct tevent_fd *_tevent_add_fd(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int fd, + uint16_t flags, + tevent_fd_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + return ev->ops->add_fd(ev, mem_ctx, fd, flags, handler, private_data, + handler_name, location); +} + +/* + set a close function on the fd event +*/ +void tevent_fd_set_close_fn(struct tevent_fd *fde, + tevent_fd_close_fn_t close_fn) +{ + if (!fde) return; + if (!fde->event_ctx) return; + fde->event_ctx->ops->set_fd_close_fn(fde, close_fn); +} + +static void tevent_fd_auto_close_fn(struct tevent_context *ev, + struct tevent_fd *fde, + int fd, + void *private_data) +{ + close(fd); +} + +void tevent_fd_set_auto_close(struct tevent_fd *fde) +{ + tevent_fd_set_close_fn(fde, tevent_fd_auto_close_fn); +} + +/* + return the fd event flags +*/ +uint16_t tevent_fd_get_flags(struct tevent_fd *fde) +{ + if (!fde) return 0; + if (!fde->event_ctx) return 0; + return fde->event_ctx->ops->get_fd_flags(fde); +} + +/* + set the fd event flags +*/ +void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags) +{ + if (!fde) return; + if (!fde->event_ctx) return; + fde->event_ctx->ops->set_fd_flags(fde, flags); +} + +bool tevent_signal_support(struct tevent_context *ev) +{ + if (ev->ops->add_signal) { + return true; + } + return false; +} + +static void (*tevent_abort_fn)(const char *reason); + +void tevent_set_abort_fn(void (*abort_fn)(const char *reason)) +{ + tevent_abort_fn = abort_fn; +} + +/* + add a timer event + return NULL on failure +*/ +struct tevent_timer *_tevent_add_timer(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + struct timeval next_event, + tevent_timer_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + return ev->ops->add_timer(ev, mem_ctx, next_event, handler, private_data, + handler_name, location); +} + +/* + allocate an immediate event + return NULL on failure (memory allocation error) +*/ +struct tevent_immediate *_tevent_create_immediate(TALLOC_CTX *mem_ctx, + const char *location) +{ + struct tevent_immediate *im; + + im = talloc(mem_ctx, struct tevent_immediate); + if (im == NULL) return NULL; + + im->prev = NULL; + im->next = NULL; + im->event_ctx = NULL; + im->create_location = location; + im->handler = NULL; + im->private_data = NULL; + im->handler_name = NULL; + im->schedule_location = NULL; + im->cancel_fn = NULL; + im->additional_data = NULL; + + return im; +} + +/* + schedule an immediate event + return NULL on failure +*/ +void _tevent_schedule_immediate(struct tevent_immediate *im, + struct tevent_context *ev, + tevent_immediate_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + ev->ops->schedule_immediate(im, ev, handler, private_data, + handler_name, location); +} + +/* + add a signal event + + sa_flags are flags to sigaction(2) + + return NULL on failure +*/ +struct tevent_signal *_tevent_add_signal(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int signum, + int sa_flags, + tevent_signal_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + return ev->ops->add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data, + handler_name, location); +} + +/* + do a single event loop using the events defined in ev +*/ +int _tevent_loop_once(struct tevent_context *ev, const char *location) +{ + return ev->ops->loop_once(ev, location); +} + +/* + return on failure or (with 0) if all fd events are removed +*/ +int tevent_common_loop_wait(struct tevent_context *ev, + const char *location) +{ + /* + * loop as long as we have events pending + */ + while (ev->fd_events || + ev->timer_events || + ev->immediate_events || + ev->signal_events) { + int ret; + ret = _tevent_loop_once(ev, location); + if (ret != 0) { + tevent_debug(ev, TEVENT_DEBUG_FATAL, + "_tevent_loop_once() failed: %d - %s\n", + ret, strerror(errno)); + return ret; + } + } + + tevent_debug(ev, TEVENT_DEBUG_WARNING, + "tevent_common_loop_wait() out of events\n"); + return 0; +} + +/* + return on failure or (with 0) if all fd events are removed +*/ +int _tevent_loop_wait(struct tevent_context *ev, const char *location) +{ + return ev->ops->loop_wait(ev, location); +} + diff --git a/ccan/tevent/tevent.h b/ccan/tevent/tevent.h new file mode 100644 index 0000000..284cf6a --- /dev/null +++ b/ccan/tevent/tevent.h @@ -0,0 +1,664 @@ +/* + Unix SMB/CIFS implementation. + + generalised event loop handling + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 2005-2009 + Copyright (C) Volker Lendecke 2008 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#ifndef __TEVENT_H__ +#define __TEVENT_H__ + +#include +#include +#include +#include + +struct tevent_context; +struct tevent_ops; +struct tevent_fd; +struct tevent_timer; +struct tevent_immediate; +struct tevent_signal; + +/** + * @defgroup tevent The tevent API + * + * The tevent low-level API + * + * This API provides the public interface to manage events in the tevent + * mainloop. Functions are provided for managing low-level events such + * as timer events, fd events and signal handling. + * + * @{ + */ + +/* event handler types */ +/** + * Called when a file descriptor monitored by tevent has + * data to be read or written on it. + */ +typedef void (*tevent_fd_handler_t)(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data); + +/** + * Called when tevent is ceasing the monitoring of a file descriptor. + */ +typedef void (*tevent_fd_close_fn_t)(struct tevent_context *ev, + struct tevent_fd *fde, + int fd, + void *private_data); + +/** + * Called when a tevent timer has fired. + */ +typedef void (*tevent_timer_handler_t)(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data); + +/** + * Called when a tevent immediate event is invoked. + */ +typedef void (*tevent_immediate_handler_t)(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data); + +/** + * Called after tevent detects the specified signal. + */ +typedef void (*tevent_signal_handler_t)(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data); + +/** + * @brief Create a event_context structure. + * + * This must be the first events call, and all subsequent calls pass this + * event_context as the first element. Event handlers also receive this as + * their first argument. + * + * @param[in] mem_ctx The memory context to use. + * + * @return An allocated tevent context, NULL on error. + * + * @see tevent_context_init() + */ +struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx); + +/** + * @brief Create a event_context structure and name it. + * + * This must be the first events call, and all subsequent calls pass this + * event_context as the first element. Event handlers also receive this as + * their first argument. + * + * @param[in] mem_ctx The memory context to use. + * + * @param[in] name The name for the tevent context. + * + * @return An allocated tevent context, NULL on error. + */ +struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx, const char *name); + +/** + * @brief List available backends. + * + * @param[in] mem_ctx The memory context to use. + * + * @return A string vector with a terminating NULL element, NULL + * on error. + */ +const char **tevent_backend_list(TALLOC_CTX *mem_ctx); + +/** + * @brief Set the default tevent backent. + * + * @param[in] backend The name of the backend to set. + */ +void tevent_set_default_backend(const char *backend); + +#ifdef DOXYGEN +/** + * @brief Add a file descriptor based event. + * + * @param[in] ev The event context to work on. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] fd The file descriptor to base the event on. + * + * @param[in] flags #TEVENT_FD_READ or #TEVENT_FD_WRITE + * + * @param[in] handler The callback handler for the event. + * + * @param[in] private_data The private data passed to the callback handler. + * + * @return The file descriptor based event, NULL on error. + * + * @note To cancel the monitoring of a file descriptor, call talloc_free() + * on the object returned by this function. + */ +struct tevent_fd *tevent_add_fd(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int fd, + uint16_t flags, + tevent_fd_handler_t handler, + void *private_data); +#else +struct tevent_fd *_tevent_add_fd(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int fd, + uint16_t flags, + tevent_fd_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); +#define tevent_add_fd(ev, mem_ctx, fd, flags, handler, private_data) \ + _tevent_add_fd(ev, mem_ctx, fd, flags, handler, private_data, \ + #handler, __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Add a timed event + * + * @param[in] ev The event context to work on. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] next_event Timeval specifying the absolute time to fire this + * event. This is not an offset. + * + * @param[in] handler The callback handler for the event. + * + * @param[in] private_data The private data passed to the callback handler. + * + * @return The newly-created timer event, or NULL on error. + * + * @note To cancel a timer event before it fires, call talloc_free() on the + * event returned from this function. This event is automatically + * talloc_free()-ed after its event handler files, if it hasn't been freed yet. + * + * @note Unlike some mainloops, tevent timers are one-time events. To set up + * a recurring event, it is necessary to call tevent_add_timer() again during + * the handler processing. + * + * @note Due to the internal mainloop processing, a timer set to run + * immediately will do so after any other pending timers fire, but before + * any further file descriptor or signal handling events fire. Callers should + * not rely on this behavior! + */ +struct tevent_timer *tevent_add_timer(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + struct timeval next_event, + tevent_timer_handler_t handler, + void *private_data); +#else +struct tevent_timer *_tevent_add_timer(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + struct timeval next_event, + tevent_timer_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); +#define tevent_add_timer(ev, mem_ctx, next_event, handler, private_data) \ + _tevent_add_timer(ev, mem_ctx, next_event, handler, private_data, \ + #handler, __location__) +#endif + +#ifdef DOXYGEN +/** + * Initialize an immediate event object + * + * This object can be used to trigger an event to occur immediately after + * returning from the current event (before any other event occurs) + * + * @param[in] mem_ctx The talloc memory context to use as the parent + * + * @return An empty tevent_immediate object. Use tevent_schedule_immediate + * to populate and use it. + * + * @note Available as of tevent 0.9.8 + */ +struct tevent_immediate *tevent_create_immediate(TALLOC_CTX *mem_ctx); +#else +struct tevent_immediate *_tevent_create_immediate(TALLOC_CTX *mem_ctx, + const char *location); +#define tevent_create_immediate(mem_ctx) \ + _tevent_create_immediate(mem_ctx, __location__) +#endif + +#ifdef DOXYGEN + +/** + * Schedule an event for immediate execution. This event will occur + * immediately after returning from the current event (before any other + * event occurs) + * + * @param[in] im The tevent_immediate object to populate and use + * @param[in] ctx The tevent_context to run this event + * @param[in] handler The event handler to run when this event fires + * @param[in] private_data Data to pass to the event handler + */ +void tevent_schedule_immediate(struct tevent_immediate *im, + struct tevent_context *ctx, + tevent_immediate_handler_t handler, + void *private_data); +#else +void _tevent_schedule_immediate(struct tevent_immediate *im, + struct tevent_context *ctx, + tevent_immediate_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); +#define tevent_schedule_immediate(im, ctx, handler, private_data) \ + _tevent_schedule_immediate(im, ctx, handler, private_data, \ + #handler, __location__); +#endif + +#ifdef DOXYGEN +/** + * @brief Add a tevent signal handler + * + * tevent_add_signal() creates a new event for handling a signal the next + * time through the mainloop. It implements a very simple traditional signal + * handler whose only purpose is to add the handler event into the mainloop. + * + * @param[in] ev The event context to work on. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] signum The signal to trap + * + * @param[in] handler The callback handler for the signal. + * + * @param[in] sa_flags sigaction flags for this signal handler. + * + * @param[in] private_data The private data passed to the callback handler. + * + * @return The newly-created signal handler event, or NULL on error. + * + * @note To cancel a signal handler, call talloc_free() on the event returned + * from this function. + */ +struct tevent_signal *tevent_add_signal(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int signum, + int sa_flags, + tevent_signal_handler_t handler, + void *private_data); +#else +struct tevent_signal *_tevent_add_signal(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int signum, + int sa_flags, + tevent_signal_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); +#define tevent_add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data) \ + _tevent_add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data, \ + #handler, __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Pass a single time through the mainloop + * + * This will process any appropriate signal, immediate, fd and timer events + * + * @param[in] ev The event context to process + * + * @return Zero on success, nonzero if an internal error occurred + */ +int tevent_loop_once(struct tevent_context *ev); +#else +int _tevent_loop_once(struct tevent_context *ev, const char *location); +#define tevent_loop_once(ev) \ + _tevent_loop_once(ev, __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Run the mainloop + * + * The mainloop will run until there are no events remaining to be processed + * + * @param[in] ev The event context to process + * + * @return Zero if all events have been processed. Nonzero if an internal + * error occurred. + */ +int tevent_loop_wait(struct tevent_context *ev); +#else +int _tevent_loop_wait(struct tevent_context *ev, const char *location); +#define tevent_loop_wait(ev) \ + _tevent_loop_wait(ev, __location__) +#endif + + +/** + * Assign a function to run when a tevent_fd is freed + * + * This function is a destructor for the tevent_fd. It does not automatically + * close the file descriptor. If this is the desired behavior, then it must be + * performed by the close_fn. + * + * @param[in] fde File descriptor event on which to set the destructor + * @param[in] close_fn Destructor to execute when fde is freed + */ +void tevent_fd_set_close_fn(struct tevent_fd *fde, + tevent_fd_close_fn_t close_fn); + +/** + * Automatically close the file descriptor when the tevent_fd is freed + * + * This function calls close(fd) internally. + * + * @param[in] fde File descriptor event to auto-close + */ +void tevent_fd_set_auto_close(struct tevent_fd *fde); + +/** + * Return the flags set on this file descriptor event + * + * @param[in] fde File descriptor event to query + * + * @return The flags set on the event. See #TEVENT_FD_READ and + * #TEVENT_FD_WRITE + */ +uint16_t tevent_fd_get_flags(struct tevent_fd *fde); + +/** + * Set flags on a file descriptor event + * + * @param[in] fde File descriptor event to set + * @param[in] flags Flags to set on the event. See #TEVENT_FD_READ and + * #TEVENT_FD_WRITE + */ +void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags); + +/** + * Query whether tevent supports signal handling + * + * @param[in] ev An initialized tevent context + * + * @return True if this platform and tevent context support signal handling + */ +bool tevent_signal_support(struct tevent_context *ev); + +void tevent_set_abort_fn(void (*abort_fn)(const char *reason)); + +/* bits for file descriptor event flags */ + +/** + * Monitor a file descriptor for write availability + */ +#define TEVENT_FD_READ 1 +/** + * Monitor a file descriptor for data to be read + */ +#define TEVENT_FD_WRITE 2 + +/** + * Convenience function for declaring a tevent_fd writable + */ +#define TEVENT_FD_WRITEABLE(fde) \ + tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) | TEVENT_FD_WRITE) + +/** + * Convenience function for declaring a tevent_fd readable + */ +#define TEVENT_FD_READABLE(fde) \ + tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) | TEVENT_FD_READ) + +/** + * Convenience function for declaring a tevent_fd non-writable + */ +#define TEVENT_FD_NOT_WRITEABLE(fde) \ + tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) & ~TEVENT_FD_WRITE) + +/** + * Convenience function for declaring a tevent_fd non-readable + */ +#define TEVENT_FD_NOT_READABLE(fde) \ + tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) & ~TEVENT_FD_READ) + +/** + * Debug level of tevent + */ +enum tevent_debug_level { + TEVENT_DEBUG_FATAL, + TEVENT_DEBUG_ERROR, + TEVENT_DEBUG_WARNING, + TEVENT_DEBUG_TRACE +}; + +/** + * @brief The tevent debug callbac. + * + * @param[in] context The memory context to use. + * + * @param[in] level The debug level. + * + * @param[in] fmt The format string. + * + * @param[in] ap The arguments for the format string. + */ +typedef void (*tevent_debug_fn)(void *context, + enum tevent_debug_level level, + const char *fmt, + va_list ap) PRINTF_FMT(3,0); + +/** + * Set destination for tevent debug messages + * + * @param[in] ev Event context to debug + * @param[in] debug Function to handle output printing + * @param[in] context The context to pass to the debug function. + * + * @return Always returns 0 as of version 0.9.8 + * + * @note Default is to emit no debug messages + */ +int tevent_set_debug(struct tevent_context *ev, + tevent_debug_fn debug, + void *context); + +/** + * Designate stderr for debug message output + * + * @param[in] ev Event context to debug + * + * @note This function will only output TEVENT_DEBUG_FATAL, TEVENT_DEBUG_ERROR + * and TEVENT_DEBUG_WARNING messages. For TEVENT_DEBUG_TRACE, please define a + * function for tevent_set_debug() + */ +int tevent_set_debug_stderr(struct tevent_context *ev); + +/** + * @defgroup tevent_helpers The tevent helper functiions + * @ingroup tevent + * + * @todo description + * + * @{ + */ + +/** + * @brief Compare two timeval values. + * + * @param[in] tv1 The first timeval value to compare. + * + * @param[in] tv2 The second timeval value to compare. + * + * @return 0 if they are equal. + * 1 if the first time is greater than the second. + * -1 if the first time is smaller than the second. + */ +int tevent_timeval_compare(const struct timeval *tv1, + const struct timeval *tv2); + +/** + * @brief Get a zero timval value. + * + * @return A zero timval value. + */ +struct timeval tevent_timeval_zero(void); + +/** + * @brief Get a timeval value for the current time. + * + * @return A timval value with the current time. + */ +struct timeval tevent_timeval_current(void); + +/** + * @brief Get a timeval structure with the given values. + * + * @param[in] secs The seconds to set. + * + * @param[in] usecs The milliseconds to set. + * + * @return A timeval structure with the given values. + */ +struct timeval tevent_timeval_set(uint32_t secs, uint32_t usecs); + +/** + * @brief Get the difference between two timeval values. + * + * @param[in] tv1 The first timeval. + * + * @param[in] tv2 The second timeval. + * + * @return A timeval structure with the difference between the + * first and the second value. + */ +struct timeval tevent_timeval_until(const struct timeval *tv1, + const struct timeval *tv2); + +/** + * @brief Check if a given timeval structure is zero. + * + * @param[in] tv The timeval to check if it is zero. + * + * @return True if it is zero, false otherwise. + */ +bool tevent_timeval_is_zero(const struct timeval *tv); + +/** + * @brief Add the given amount of time to a timeval structure. + * + * @param[in] tv The timeval structure to add the time. + * + * @param[in] secs The seconds to add to the timeval. + * + * @param[in] usecs The milliseconds to add to the timeval. + * + * @return The timeval structure with the new time. + */ +struct timeval tevent_timeval_add(const struct timeval *tv, uint32_t secs, + uint32_t usecs); + +/** + * @brief Get a timeval in the future with a specified offset from now. + * + * @param[in] secs The seconds of the offset from now. + * + * @param[in] usecs The milliseconds of the offset from now. + * + * @return A timval with the given offset in the future. + */ +struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs); + +/* @} */ + +/** + * @defgroup tevent_ops The tevent operation functions + * @ingroup tevent + * + * The following structure and registration functions are exclusively + * needed for people writing and pluggin a different event engine. + * There is nothing useful for normal tevent user in here. + * @{ + */ + +struct tevent_ops { + /* context init */ + int (*context_init)(struct tevent_context *ev); + + /* fd_event functions */ + struct tevent_fd *(*add_fd)(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int fd, uint16_t flags, + tevent_fd_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); + void (*set_fd_close_fn)(struct tevent_fd *fde, + tevent_fd_close_fn_t close_fn); + uint16_t (*get_fd_flags)(struct tevent_fd *fde); + void (*set_fd_flags)(struct tevent_fd *fde, uint16_t flags); + + /* timed_event functions */ + struct tevent_timer *(*add_timer)(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + struct timeval next_event, + tevent_timer_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); + + /* immediate event functions */ + void (*schedule_immediate)(struct tevent_immediate *im, + struct tevent_context *ev, + tevent_immediate_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); + + /* signal functions */ + struct tevent_signal *(*add_signal)(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int signum, int sa_flags, + tevent_signal_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); + + /* loop functions */ + int (*loop_once)(struct tevent_context *ev, const char *location); + int (*loop_wait)(struct tevent_context *ev, const char *location); +}; + +bool tevent_register_backend(const char *name, const struct tevent_ops *ops); + +/* @} */ + + +#endif /* __TEVENT_H__ */ diff --git a/ccan/tevent/tevent_debug.c b/ccan/tevent/tevent_debug.c new file mode 100644 index 0000000..8df5c75 --- /dev/null +++ b/ccan/tevent/tevent_debug.c @@ -0,0 +1,95 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Jelmer Vernooij 2005 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include + +/******************************************************************** + * Debug wrapper functions, modeled (with lot's of code copied as is) + * after the ev debug wrapper functions + ********************************************************************/ + +/* + this allows the user to choose their own debug function +*/ +int tevent_set_debug(struct tevent_context *ev, + void (*debug)(void *context, + enum tevent_debug_level level, + const char *fmt, + va_list ap) PRINTF_FMT(3,0), + void *context) +{ + ev->debug_ops.debug = debug; + ev->debug_ops.context = context; + return 0; +} + +/* + debug function for ev_set_debug_stderr +*/ +static void tevent_debug_stderr(void *private_data, + enum tevent_debug_level level, + const char *fmt, + va_list ap) PRINTF_FMT(3,0); +static void tevent_debug_stderr(void *private_data, + enum tevent_debug_level level, + const char *fmt, va_list ap) +{ + if (level <= TEVENT_DEBUG_WARNING) { + vfprintf(stderr, fmt, ap); + } +} + +/* + convenience function to setup debug messages on stderr + messages of level TEVENT_DEBUG_WARNING and higher are printed +*/ +int tevent_set_debug_stderr(struct tevent_context *ev) +{ + return tevent_set_debug(ev, tevent_debug_stderr, ev); +} + +/* + * log a message + * + * The default debug action is to ignore debugging messages. + * This is the most appropriate action for a library. + * Applications using the library must decide where to + * redirect debugging messages +*/ +void tevent_debug(struct tevent_context *ev, enum tevent_debug_level level, + const char *fmt, ...) +{ + va_list ap; + if (!ev) { + return; + } + if (ev->debug_ops.debug == NULL) { + return; + } + va_start(ap, fmt); + ev->debug_ops.debug(ev->debug_ops.context, level, fmt, ap); + va_end(ap); +} + diff --git a/ccan/tevent/tevent_fd.c b/ccan/tevent/tevent_fd.c new file mode 100644 index 0000000..67e7e9c --- /dev/null +++ b/ccan/tevent/tevent_fd.c @@ -0,0 +1,88 @@ +/* + Unix SMB/CIFS implementation. + + common events code for fd events + + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include + +int tevent_common_fd_destructor(struct tevent_fd *fde) +{ + if (fde->event_ctx) { + DLIST_REMOVE(fde->event_ctx->fd_events, fde); + } + + if (fde->close_fn) { + fde->close_fn(fde->event_ctx, fde, fde->fd, fde->private_data); + fde->fd = -1; + } + + return 0; +} + +struct tevent_fd *tevent_common_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx, + int fd, uint16_t flags, + tevent_fd_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + struct tevent_fd *fde; + + fde = talloc(mem_ctx?mem_ctx:ev, struct tevent_fd); + if (!fde) return NULL; + + fde->event_ctx = ev; + fde->fd = fd; + fde->flags = flags; + fde->handler = handler; + fde->close_fn = NULL; + fde->private_data = private_data; + fde->handler_name = handler_name; + fde->location = location; + fde->additional_flags = 0; + fde->additional_data = NULL; + + DLIST_ADD(ev->fd_events, fde); + + talloc_set_destructor(fde, tevent_common_fd_destructor); + + return fde; +} +uint16_t tevent_common_fd_get_flags(struct tevent_fd *fde) +{ + return fde->flags; +} + +void tevent_common_fd_set_flags(struct tevent_fd *fde, uint16_t flags) +{ + if (fde->flags == flags) return; + fde->flags = flags; +} + +void tevent_common_fd_set_close_fn(struct tevent_fd *fde, + tevent_fd_close_fn_t close_fn) +{ + fde->close_fn = close_fn; +} diff --git a/ccan/tevent/tevent_immediate.c b/ccan/tevent/tevent_immediate.c new file mode 100644 index 0000000..3dd14df --- /dev/null +++ b/ccan/tevent/tevent_immediate.c @@ -0,0 +1,138 @@ +/* + Unix SMB/CIFS implementation. + + common events code for immediate events + + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include + +static void tevent_common_immediate_cancel(struct tevent_immediate *im) +{ + if (!im->event_ctx) { + return; + } + + tevent_debug(im->event_ctx, TEVENT_DEBUG_TRACE, + "Cancel immediate event %p \"%s\"\n", + im, im->handler_name); + + /* let the backend free im->additional_data */ + if (im->cancel_fn) { + im->cancel_fn(im); + } + + DLIST_REMOVE(im->event_ctx->immediate_events, im); + im->event_ctx = NULL; + im->handler = NULL; + im->private_data = NULL; + im->handler_name = NULL; + im->schedule_location = NULL; + im->cancel_fn = NULL; + im->additional_data = NULL; + + talloc_set_destructor(im, NULL); +} + +/* + destroy an immediate event +*/ +static int tevent_common_immediate_destructor(struct tevent_immediate *im) +{ + tevent_common_immediate_cancel(im); + return 0; +} + +/* + * schedule an immediate event on + */ +void tevent_common_schedule_immediate(struct tevent_immediate *im, + struct tevent_context *ev, + tevent_immediate_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + tevent_common_immediate_cancel(im); + + if (!handler) { + return; + } + + im->event_ctx = ev; + im->handler = handler; + im->private_data = private_data; + im->handler_name = handler_name; + im->schedule_location = location; + im->cancel_fn = NULL; + im->additional_data = NULL; + + DLIST_ADD_END(ev->immediate_events, im, struct tevent_immediate *); + talloc_set_destructor(im, tevent_common_immediate_destructor); + + tevent_debug(ev, TEVENT_DEBUG_TRACE, + "Schedule immediate event \"%s\": %p\n", + handler_name, im); +} + +/* + trigger the first immediate event and return true + if no event was triggered return false +*/ +bool tevent_common_loop_immediate(struct tevent_context *ev) +{ + struct tevent_immediate *im = ev->immediate_events; + tevent_immediate_handler_t handler; + void *private_data; + + if (!im) { + return false; + } + + tevent_debug(ev, TEVENT_DEBUG_TRACE, + "Run immediate event \"%s\": %p\n", + im->handler_name, im); + + /* + * remember the handler and then clear the event + * the handler might reschedule the event + */ + handler = im->handler; + private_data = im->private_data; + + DLIST_REMOVE(im->event_ctx->immediate_events, im); + im->event_ctx = NULL; + im->handler = NULL; + im->private_data = NULL; + im->handler_name = NULL; + im->schedule_location = NULL; + im->cancel_fn = NULL; + im->additional_data = NULL; + + talloc_set_destructor(im, NULL); + + handler(ev, im, private_data); + + return true; +} + diff --git a/ccan/tevent/tevent_internal.h b/ccan/tevent/tevent_internal.h new file mode 100644 index 0000000..682dc66 --- /dev/null +++ b/ccan/tevent/tevent_internal.h @@ -0,0 +1,181 @@ +#ifndef CCAN_TEVENT_TEVENT_INTERNAL_H +#define CCAN_TEVENT_TEVENT_INTERNAL_H +/* + Unix SMB/CIFS implementation. + + generalised event loop handling + + INTERNAL STRUCTS. THERE ARE NO API GUARANTEES. + External users should only ever have to include this header when + implementing new tevent backends. + + Copyright (C) Stefan Metzmacher 2005-2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +struct tevent_fd { + struct tevent_fd *prev, *next; + struct tevent_context *event_ctx; + int fd; + uint16_t flags; /* see TEVENT_FD_* flags */ + tevent_fd_handler_t handler; + tevent_fd_close_fn_t close_fn; + /* this is private for the specific handler */ + void *private_data; + /* this is for debugging only! */ + const char *handler_name; + const char *location; + /* this is private for the events_ops implementation */ + uint16_t additional_flags; + void *additional_data; +}; + +struct tevent_timer { + struct tevent_timer *prev, *next; + struct tevent_context *event_ctx; + struct timeval next_event; + tevent_timer_handler_t handler; + /* this is private for the specific handler */ + void *private_data; + /* this is for debugging only! */ + const char *handler_name; + const char *location; + /* this is private for the events_ops implementation */ + void *additional_data; +}; + +struct tevent_immediate { + struct tevent_immediate *prev, *next; + struct tevent_context *event_ctx; + tevent_immediate_handler_t handler; + /* this is private for the specific handler */ + void *private_data; + /* this is for debugging only! */ + const char *handler_name; + const char *create_location; + const char *schedule_location; + /* this is private for the events_ops implementation */ + void (*cancel_fn)(struct tevent_immediate *im); + void *additional_data; +}; + +struct tevent_signal { + struct tevent_signal *prev, *next; + struct tevent_context *event_ctx; + int signum; + int sa_flags; + tevent_signal_handler_t handler; + /* this is private for the specific handler */ + void *private_data; + /* this is for debugging only! */ + const char *handler_name; + const char *location; + /* this is private for the events_ops implementation */ + void *additional_data; +}; + +struct tevent_debug_ops { + void (*debug)(void *context, enum tevent_debug_level level, + const char *fmt, va_list ap) PRINTF_FMT(3,0); + void *context; +}; + +void tevent_debug(struct tevent_context *ev, enum tevent_debug_level level, + const char *fmt, ...) PRINTF_FMT(3,4); + +struct tevent_context { + /* the specific events implementation */ + const struct tevent_ops *ops; + + /* list of fd events - used by common code */ + struct tevent_fd *fd_events; + + /* list of timed events - used by common code */ + struct tevent_timer *timer_events; + + /* list of immediate events - used by common code */ + struct tevent_immediate *immediate_events; + + /* list of signal events - used by common code */ + struct tevent_signal *signal_events; + + /* this is private for the events_ops implementation */ + void *additional_data; + + /* pipe hack used with signal handlers */ + struct tevent_fd *pipe_fde; + int pipe_fds[2]; + + /* debugging operations */ + struct tevent_debug_ops debug_ops; +}; + + +int tevent_common_context_destructor(struct tevent_context *ev); +int tevent_common_loop_wait(struct tevent_context *ev, + const char *location); + +int tevent_common_fd_destructor(struct tevent_fd *fde); +struct tevent_fd *tevent_common_add_fd(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int fd, + uint16_t flags, + tevent_fd_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); +void tevent_common_fd_set_close_fn(struct tevent_fd *fde, + tevent_fd_close_fn_t close_fn); +uint16_t tevent_common_fd_get_flags(struct tevent_fd *fde); +void tevent_common_fd_set_flags(struct tevent_fd *fde, uint16_t flags); + +struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + struct timeval next_event, + tevent_timer_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); +struct timeval tevent_common_loop_timer_delay(struct tevent_context *); + +void tevent_common_schedule_immediate(struct tevent_immediate *im, + struct tevent_context *ev, + tevent_immediate_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); +bool tevent_common_loop_immediate(struct tevent_context *ev); + +struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int signum, + int sa_flags, + tevent_signal_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); +int tevent_common_check_signal(struct tevent_context *ev); +void tevent_cleanup_pending_signal_handlers(struct tevent_signal *se); + +bool tevent_standard_init(void); +bool tevent_select_init(void); +#ifdef HAVE_EPOLL +bool tevent_epoll_init(void); +#endif +#endif /* CCAN_TEVENT_TEVENT_INTERNAL_H */ diff --git a/ccan/tevent/tevent_select.c b/ccan/tevent/tevent_select.c new file mode 100644 index 0000000..dce40b4 --- /dev/null +++ b/ccan/tevent/tevent_select.c @@ -0,0 +1,250 @@ +/* + Unix SMB/CIFS implementation. + main select loop and event handling + Copyright (C) Andrew Tridgell 2003-2005 + Copyright (C) Stefan Metzmacher 2005-2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct select_event_context { + /* a pointer back to the generic event_context */ + struct tevent_context *ev; + + /* the maximum file descriptor number in fd_events */ + int maxfd; + + /* information for exiting from the event loop */ + int exit_code; +}; + +/* + create a select_event_context structure. +*/ +static int select_event_context_init(struct tevent_context *ev) +{ + struct select_event_context *select_ev; + + select_ev = talloc_zero(ev, struct select_event_context); + if (!select_ev) return -1; + select_ev->ev = ev; + + ev->additional_data = select_ev; + return 0; +} + +/* + recalculate the maxfd +*/ +static void calc_maxfd(struct select_event_context *select_ev) +{ + struct tevent_fd *fde; + + select_ev->maxfd = 0; + for (fde = select_ev->ev->fd_events; fde; fde = fde->next) { + if (fde->fd > select_ev->maxfd) { + select_ev->maxfd = fde->fd; + } + } +} + + +/* to mark the ev->maxfd invalid + * this means we need to recalculate it + */ +#define EVENT_INVALID_MAXFD (-1) + +/* + destroy an fd_event +*/ +static int select_event_fd_destructor(struct tevent_fd *fde) +{ + struct tevent_context *ev = fde->event_ctx; + struct select_event_context *select_ev = NULL; + + if (ev) { + select_ev = talloc_get_type(ev->additional_data, + struct select_event_context); + + if (select_ev->maxfd == fde->fd) { + select_ev->maxfd = EVENT_INVALID_MAXFD; + } + } + + return tevent_common_fd_destructor(fde); +} + +/* + add a fd based event + return NULL on failure (memory allocation error) +*/ +static struct tevent_fd *select_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx, + int fd, uint16_t flags, + tevent_fd_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + struct select_event_context *select_ev = talloc_get_type(ev->additional_data, + struct select_event_context); + struct tevent_fd *fde; + + fde = tevent_common_add_fd(ev, mem_ctx, fd, flags, + handler, private_data, + handler_name, location); + if (!fde) return NULL; + + if ((select_ev->maxfd != EVENT_INVALID_MAXFD) + && (fde->fd > select_ev->maxfd)) { + select_ev->maxfd = fde->fd; + } + talloc_set_destructor(fde, select_event_fd_destructor); + + return fde; +} + +/* + event loop handling using select() +*/ +static int select_event_loop_select(struct select_event_context *select_ev, struct timeval *tvalp) +{ + fd_set r_fds, w_fds; + struct tevent_fd *fde; + int selrtn; + + /* we maybe need to recalculate the maxfd */ + if (select_ev->maxfd == EVENT_INVALID_MAXFD) { + calc_maxfd(select_ev); + } + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + + /* setup any fd events */ + for (fde = select_ev->ev->fd_events; fde; fde = fde->next) { + if (fde->flags & TEVENT_FD_READ) { + FD_SET(fde->fd, &r_fds); + } + if (fde->flags & TEVENT_FD_WRITE) { + FD_SET(fde->fd, &w_fds); + } + } + + if (select_ev->ev->signal_events && + tevent_common_check_signal(select_ev->ev)) { + return 0; + } + + selrtn = select(select_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); + + if (selrtn == -1 && errno == EINTR && + select_ev->ev->signal_events) { + tevent_common_check_signal(select_ev->ev); + return 0; + } + + if (selrtn == -1 && errno == EBADF) { + /* the socket is dead! this should never + happen as the socket should have first been + made readable and that should have removed + the event, so this must be a bug. This is a + fatal error. */ + tevent_debug(select_ev->ev, TEVENT_DEBUG_FATAL, + "ERROR: EBADF on select_event_loop_once\n"); + select_ev->exit_code = EBADF; + return -1; + } + + if (selrtn == 0 && tvalp) { + /* we don't care about a possible delay here */ + tevent_common_loop_timer_delay(select_ev->ev); + return 0; + } + + if (selrtn > 0) { + /* at least one file descriptor is ready - check + which ones and call the handler, being careful to allow + the handler to remove itself when called */ + for (fde = select_ev->ev->fd_events; fde; fde = fde->next) { + uint16_t flags = 0; + + if (FD_ISSET(fde->fd, &r_fds)) flags |= TEVENT_FD_READ; + if (FD_ISSET(fde->fd, &w_fds)) flags |= TEVENT_FD_WRITE; + if (flags) { + fde->handler(select_ev->ev, fde, flags, fde->private_data); + break; + } + } + } + + return 0; +} + +/* + do a single event loop using the events defined in ev +*/ +static int select_event_loop_once(struct tevent_context *ev, const char *location) +{ + struct select_event_context *select_ev = talloc_get_type(ev->additional_data, + struct select_event_context); + struct timeval tval; + + if (ev->signal_events && + tevent_common_check_signal(ev)) { + return 0; + } + + if (ev->immediate_events && + tevent_common_loop_immediate(ev)) { + return 0; + } + + tval = tevent_common_loop_timer_delay(ev); + if (tevent_timeval_is_zero(&tval)) { + return 0; + } + + return select_event_loop_select(select_ev, &tval); +} + +static const struct tevent_ops select_event_ops = { + .context_init = select_event_context_init, + .add_fd = select_event_add_fd, + .set_fd_close_fn = tevent_common_fd_set_close_fn, + .get_fd_flags = tevent_common_fd_get_flags, + .set_fd_flags = tevent_common_fd_set_flags, + .add_timer = tevent_common_add_timer, + .schedule_immediate = tevent_common_schedule_immediate, + .add_signal = tevent_common_add_signal, + .loop_once = select_event_loop_once, + .loop_wait = tevent_common_loop_wait, +}; + +_PRIVATE_ bool tevent_select_init(void) +{ + return tevent_register_backend("select", &select_event_ops); +} diff --git a/ccan/tevent/tevent_signal.c b/ccan/tevent/tevent_signal.c new file mode 100644 index 0000000..f2efe76 --- /dev/null +++ b/ccan/tevent/tevent_signal.c @@ -0,0 +1,424 @@ +/* + Unix SMB/CIFS implementation. + + common events code for signal events + + Copyright (C) Andrew Tridgell 2007 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define TEVENT_NUM_SIGNALS 64 + +/* maximum number of SA_SIGINFO signals to hold in the queue. + NB. This *MUST* be a power of 2, in order for the ring buffer + wrap to work correctly. Thanks to Petr Vandrovec + for this. */ + +#define TEVENT_SA_INFO_QUEUE_COUNT 64 + +struct tevent_sigcounter { + uint32_t count; + uint32_t seen; +}; + +#define TEVENT_SIG_INCREMENT(s) (s).count++ +#define TEVENT_SIG_SEEN(s, n) (s).seen += (n) +#define TEVENT_SIG_PENDING(s) ((s).seen != (s).count) + +struct tevent_common_signal_list { + struct tevent_common_signal_list *prev, *next; + struct tevent_signal *se; +}; + +/* + the poor design of signals means that this table must be static global +*/ +static struct tevent_sig_state { + struct tevent_common_signal_list *sig_handlers[TEVENT_NUM_SIGNALS+1]; + struct sigaction *oldact[TEVENT_NUM_SIGNALS+1]; + struct tevent_sigcounter signal_count[TEVENT_NUM_SIGNALS+1]; + struct tevent_sigcounter got_signal; +#ifdef SA_SIGINFO + /* with SA_SIGINFO we get quite a lot of info per signal */ + siginfo_t *sig_info[TEVENT_NUM_SIGNALS+1]; + struct tevent_sigcounter sig_blocked[TEVENT_NUM_SIGNALS+1]; +#endif +} *sig_state; + +/* + return number of sigcounter events not processed yet +*/ +static uint32_t tevent_sig_count(struct tevent_sigcounter s) +{ + return s.count - s.seen; +} + +/* + signal handler - redirects to registered signals +*/ +static void tevent_common_signal_handler(int signum) +{ + char c = 0; + ssize_t res; + struct tevent_common_signal_list *sl; + struct tevent_context *ev = NULL; + int saved_errno = errno; + + TEVENT_SIG_INCREMENT(sig_state->signal_count[signum]); + TEVENT_SIG_INCREMENT(sig_state->got_signal); + + /* Write to each unique event context. */ + for (sl = sig_state->sig_handlers[signum]; sl; sl = sl->next) { + if (sl->se->event_ctx && sl->se->event_ctx != ev) { + ev = sl->se->event_ctx; + /* doesn't matter if this pipe overflows */ + res = write(ev->pipe_fds[1], &c, 1); + } + } + + errno = saved_errno; +} + +#ifdef SA_SIGINFO +/* + signal handler with SA_SIGINFO - redirects to registered signals +*/ +static void tevent_common_signal_handler_info(int signum, siginfo_t *info, + void *uctx) +{ + uint32_t count = tevent_sig_count(sig_state->signal_count[signum]); + /* sig_state->signal_count[signum].seen % TEVENT_SA_INFO_QUEUE_COUNT + * is the base of the unprocessed signals in the ringbuffer. */ + uint32_t ofs = (sig_state->signal_count[signum].seen + count) % + TEVENT_SA_INFO_QUEUE_COUNT; + sig_state->sig_info[signum][ofs] = *info; + + tevent_common_signal_handler(signum); + + /* handle SA_SIGINFO */ + if (count+1 == TEVENT_SA_INFO_QUEUE_COUNT) { + /* we've filled the info array - block this signal until + these ones are delivered */ + sigset_t set; + sigemptyset(&set); + sigaddset(&set, signum); + sigprocmask(SIG_BLOCK, &set, NULL); + TEVENT_SIG_INCREMENT(sig_state->sig_blocked[signum]); + } +} +#endif + +static int tevent_common_signal_list_destructor(struct tevent_common_signal_list *sl) +{ + if (sig_state->sig_handlers[sl->se->signum]) { + DLIST_REMOVE(sig_state->sig_handlers[sl->se->signum], sl); + } + return 0; +} + +/* + destroy a signal event +*/ +static int tevent_signal_destructor(struct tevent_signal *se) +{ + struct tevent_common_signal_list *sl; + sl = talloc_get_type(se->additional_data, + struct tevent_common_signal_list); + + if (se->event_ctx) { + DLIST_REMOVE(se->event_ctx->signal_events, se); + } + + talloc_free(sl); + + if (sig_state->sig_handlers[se->signum] == NULL) { + /* restore old handler, if any */ + if (sig_state->oldact[se->signum]) { + sigaction(se->signum, sig_state->oldact[se->signum], NULL); + sig_state->oldact[se->signum] = NULL; + } +#ifdef SA_SIGINFO + if (se->sa_flags & SA_SIGINFO) { + if (sig_state->sig_info[se->signum]) { + talloc_free(sig_state->sig_info[se->signum]); + sig_state->sig_info[se->signum] = NULL; + } + } +#endif + } + + return 0; +} + +/* + this is part of the pipe hack needed to avoid the signal race condition +*/ +static void signal_pipe_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *_private) +{ + char c[16]; + ssize_t res; + /* its non-blocking, doesn't matter if we read too much */ + res = read(fde->fd, c, sizeof(c)); +} + +/* + add a signal event + return NULL on failure (memory allocation error) +*/ +struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int signum, + int sa_flags, + tevent_signal_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + struct tevent_signal *se; + struct tevent_common_signal_list *sl; + sigset_t set, oldset; + + if (signum >= TEVENT_NUM_SIGNALS) { + errno = EINVAL; + return NULL; + } + + /* the sig_state needs to be on a global context as it can last across + multiple event contexts */ + if (sig_state == NULL) { + sig_state = talloc_zero(NULL, struct tevent_sig_state); + if (sig_state == NULL) { + return NULL; + } + } + + se = talloc(mem_ctx?mem_ctx:ev, struct tevent_signal); + if (se == NULL) return NULL; + + se->event_ctx = ev; + se->signum = signum; + se->sa_flags = sa_flags; + se->handler = handler; + se->private_data = private_data; + se->handler_name = handler_name; + se->location = location; + se->additional_data = NULL; + + sl = talloc(se, struct tevent_common_signal_list); + if (!sl) { + talloc_free(se); + return NULL; + } + sl->se = se; + se->additional_data = sl; + + /* Ensure, no matter the destruction order, that we always have a handle on the global sig_state */ + if (!talloc_reference(se, sig_state)) { + talloc_free(se); + return NULL; + } + + /* we need to setup the pipe hack handler if not already + setup */ + if (ev->pipe_fde == NULL) { + if (pipe(ev->pipe_fds) == -1) { + talloc_free(se); + return NULL; + } + ev_set_blocking(ev->pipe_fds[0], false); + ev_set_blocking(ev->pipe_fds[1], false); + ev->pipe_fde = tevent_add_fd(ev, ev, ev->pipe_fds[0], + TEVENT_FD_READ, + signal_pipe_handler, NULL); + if (!ev->pipe_fde) { + close(ev->pipe_fds[0]); + close(ev->pipe_fds[1]); + talloc_free(se); + return NULL; + } + } + + /* only install a signal handler if not already installed */ + if (sig_state->sig_handlers[signum] == NULL) { + struct sigaction act; + ZERO_STRUCT(act); + act.sa_handler = tevent_common_signal_handler; + act.sa_flags = sa_flags; +#ifdef SA_SIGINFO + if (sa_flags & SA_SIGINFO) { + act.sa_handler = NULL; + act.sa_sigaction = tevent_common_signal_handler_info; + if (sig_state->sig_info[signum] == NULL) { + sig_state->sig_info[signum] = + talloc_zero_array(sig_state, siginfo_t, + TEVENT_SA_INFO_QUEUE_COUNT); + if (sig_state->sig_info[signum] == NULL) { + talloc_free(se); + return NULL; + } + } + } +#endif + sig_state->oldact[signum] = talloc(sig_state, struct sigaction); + if (sig_state->oldact[signum] == NULL) { + talloc_free(se); + return NULL; + } + if (sigaction(signum, &act, sig_state->oldact[signum]) == -1) { + talloc_free(se); + return NULL; + } + } + + DLIST_ADD(se->event_ctx->signal_events, se); + + /* Make sure the signal doesn't come in while we're mangling list. */ + sigemptyset(&set); + sigaddset(&set, signum); + sigprocmask(SIG_BLOCK, &set, &oldset); + DLIST_ADD(sig_state->sig_handlers[signum], sl); + sigprocmask(SIG_SETMASK, &oldset, NULL); + + talloc_set_destructor(se, tevent_signal_destructor); + talloc_set_destructor(sl, tevent_common_signal_list_destructor); + + return se; +} + + +/* + check if a signal is pending + return != 0 if a signal was pending +*/ +int tevent_common_check_signal(struct tevent_context *ev) +{ + int i; + + if (!sig_state || !TEVENT_SIG_PENDING(sig_state->got_signal)) { + return 0; + } + + for (i=0;isignal_count[i]; + uint32_t count = tevent_sig_count(counter); +#ifdef SA_SIGINFO + /* Ensure we null out any stored siginfo_t entries + * after processing for debugging purposes. */ + bool clear_processed_siginfo = false; +#endif + + if (count == 0) { + continue; + } + for (sl=sig_state->sig_handlers[i];sl;sl=next) { + struct tevent_signal *se = sl->se; + next = sl->next; +#ifdef SA_SIGINFO + if (se->sa_flags & SA_SIGINFO) { + uint32_t j; + + clear_processed_siginfo = true; + + for (j=0;jsignal_count[i].seen + * % TEVENT_SA_INFO_QUEUE_COUNT is + * the base position of the unprocessed + * signals in the ringbuffer. */ + uint32_t ofs = (counter.seen + j) + % TEVENT_SA_INFO_QUEUE_COUNT; + se->handler(ev, se, i, 1, + (void*)&sig_state->sig_info[i][ofs], + se->private_data); + } + if (se->sa_flags & SA_RESETHAND) { + talloc_free(se); + } + continue; + } +#endif + se->handler(ev, se, i, count, NULL, se->private_data); + if (se->sa_flags & SA_RESETHAND) { + talloc_free(se); + } + } + +#ifdef SA_SIGINFO + if (clear_processed_siginfo) { + uint32_t j; + for (j=0;jsig_info[i][ofs], + '\0', + sizeof(siginfo_t)); + } + } +#endif + + TEVENT_SIG_SEEN(sig_state->signal_count[i], count); + TEVENT_SIG_SEEN(sig_state->got_signal, count); + +#ifdef SA_SIGINFO + if (TEVENT_SIG_PENDING(sig_state->sig_blocked[i])) { + /* We'd filled the queue, unblock the + signal now the queue is empty again. + Note we MUST do this after the + TEVENT_SIG_SEEN(sig_state->signal_count[i], count) + call to prevent a new signal running + out of room in the sig_state->sig_info[i][] + ring buffer. */ + sigset_t set; + sigemptyset(&set); + sigaddset(&set, i); + TEVENT_SIG_SEEN(sig_state->sig_blocked[i], + tevent_sig_count(sig_state->sig_blocked[i])); + sigprocmask(SIG_UNBLOCK, &set, NULL); + } +#endif + } + + return 1; +} + +void tevent_cleanup_pending_signal_handlers(struct tevent_signal *se) +{ + struct tevent_common_signal_list *sl; + sl = talloc_get_type(se->additional_data, + struct tevent_common_signal_list); + + tevent_common_signal_list_destructor(sl); + + if (sig_state->sig_handlers[se->signum] == NULL) { + if (sig_state->oldact[se->signum]) { + sigaction(se->signum, sig_state->oldact[se->signum], NULL); + sig_state->oldact[se->signum] = NULL; + } + } + return; +} diff --git a/ccan/tevent/tevent_standard.c b/ccan/tevent/tevent_standard.c new file mode 100644 index 0000000..cd9823c --- /dev/null +++ b/ccan/tevent/tevent_standard.c @@ -0,0 +1,567 @@ +/* + Unix SMB/CIFS implementation. + main select loop and event handling + Copyright (C) Andrew Tridgell 2003-2005 + Copyright (C) Stefan Metzmacher 2005-2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* + This is SAMBA's default event loop code + + - we try to use epoll if configure detected support for it + otherwise we use select() + - if epoll is broken on the system or the kernel doesn't support it + at runtime we fallback to select() +*/ + +#include +#include +#include +#include + +struct std_event_context { + /* a pointer back to the generic event_context */ + struct tevent_context *ev; + + /* the maximum file descriptor number in fd_events */ + int maxfd; + + /* information for exiting from the event loop */ + int exit_code; + + /* when using epoll this is the handle from epoll_create */ + int epoll_fd; + + /* our pid at the time the epoll_fd was created */ + pid_t pid; +}; + +/* use epoll if it is available */ +#if HAVE_EPOLL +/* + called when a epoll call fails, and we should fallback + to using select +*/ +static void epoll_fallback_to_select(struct std_event_context *std_ev, const char *reason) +{ + tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, + "%s (%s) - falling back to select()\n", + reason, strerror(errno)); + close(std_ev->epoll_fd); + std_ev->epoll_fd = -1; + talloc_set_destructor(std_ev, NULL); +} + +/* + map from TEVENT_FD_* to EPOLLIN/EPOLLOUT +*/ +static uint32_t epoll_map_flags(uint16_t flags) +{ + uint32_t ret = 0; + if (flags & TEVENT_FD_READ) ret |= (EPOLLIN | EPOLLERR | EPOLLHUP); + if (flags & TEVENT_FD_WRITE) ret |= (EPOLLOUT | EPOLLERR | EPOLLHUP); + return ret; +} + +/* + free the epoll fd +*/ +static int epoll_ctx_destructor(struct std_event_context *std_ev) +{ + if (std_ev->epoll_fd != -1) { + close(std_ev->epoll_fd); + } + std_ev->epoll_fd = -1; + return 0; +} + +/* + init the epoll fd +*/ +static void epoll_init_ctx(struct std_event_context *std_ev) +{ + std_ev->epoll_fd = epoll_create(64); + std_ev->pid = getpid(); + talloc_set_destructor(std_ev, epoll_ctx_destructor); +} + +static void epoll_add_event(struct std_event_context *std_ev, struct tevent_fd *fde); + +/* + reopen the epoll handle when our pid changes + see http://junkcode.samba.org/ftp/unpacked/junkcode/epoll_fork.c for an + demonstration of why this is needed + */ +static void epoll_check_reopen(struct std_event_context *std_ev) +{ + struct tevent_fd *fde; + + if (std_ev->pid == getpid()) { + return; + } + + close(std_ev->epoll_fd); + std_ev->epoll_fd = epoll_create(64); + if (std_ev->epoll_fd == -1) { + tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, + "Failed to recreate epoll handle after fork\n"); + return; + } + std_ev->pid = getpid(); + for (fde=std_ev->ev->fd_events;fde;fde=fde->next) { + epoll_add_event(std_ev, fde); + } +} + +#define EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT (1<<0) +#define EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR (1<<1) +#define EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR (1<<2) + +/* + add the epoll event to the given fd_event +*/ +static void epoll_add_event(struct std_event_context *std_ev, struct tevent_fd *fde) +{ + struct epoll_event event; + if (std_ev->epoll_fd == -1) return; + + fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; + + /* if we don't want events yet, don't add an epoll_event */ + if (fde->flags == 0) return; + + ZERO_STRUCT(event); + event.events = epoll_map_flags(fde->flags); + event.data.ptr = fde; + if (epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_ADD, fde->fd, &event) != 0) { + epoll_fallback_to_select(std_ev, "EPOLL_CTL_ADD failed"); + } + fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT; + + /* only if we want to read we want to tell the event handler about errors */ + if (fde->flags & TEVENT_FD_READ) { + fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; + } +} + +/* + delete the epoll event for given fd_event +*/ +static void epoll_del_event(struct std_event_context *std_ev, struct tevent_fd *fde) +{ + struct epoll_event event; + if (std_ev->epoll_fd == -1) return; + + fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; + + /* if there's no epoll_event, we don't need to delete it */ + if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT)) return; + + ZERO_STRUCT(event); + event.events = epoll_map_flags(fde->flags); + event.data.ptr = fde; + epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_DEL, fde->fd, &event); + fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT; +} + +/* + change the epoll event to the given fd_event +*/ +static void epoll_mod_event(struct std_event_context *std_ev, struct tevent_fd *fde) +{ + struct epoll_event event; + if (std_ev->epoll_fd == -1) return; + + fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; + + ZERO_STRUCT(event); + event.events = epoll_map_flags(fde->flags); + event.data.ptr = fde; + if (epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) { + epoll_fallback_to_select(std_ev, "EPOLL_CTL_MOD failed"); + } + + /* only if we want to read we want to tell the event handler about errors */ + if (fde->flags & TEVENT_FD_READ) { + fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; + } +} + +static void epoll_change_event(struct std_event_context *std_ev, struct tevent_fd *fde) +{ + bool got_error = (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR); + bool want_read = (fde->flags & TEVENT_FD_READ); + bool want_write= (fde->flags & TEVENT_FD_WRITE); + + if (std_ev->epoll_fd == -1) return; + + fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; + + /* there's already an event */ + if (fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT) { + if (want_read || (want_write && !got_error)) { + epoll_mod_event(std_ev, fde); + return; + } + /* + * if we want to match the select behavior, we need to remove the epoll_event + * when the caller isn't interested in events. + * + * this is because epoll reports EPOLLERR and EPOLLHUP, even without asking for them + */ + epoll_del_event(std_ev, fde); + return; + } + + /* there's no epoll_event attached to the fde */ + if (want_read || (want_write && !got_error)) { + epoll_add_event(std_ev, fde); + return; + } +} + +/* + event loop handling using epoll +*/ +static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tvalp) +{ + int ret, i; +#define MAXEVENTS 1 + struct epoll_event events[MAXEVENTS]; + int timeout = -1; + + if (std_ev->epoll_fd == -1) return -1; + + if (tvalp) { + /* it's better to trigger timed events a bit later than to early */ + timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000); + } + + if (std_ev->ev->signal_events && + tevent_common_check_signal(std_ev->ev)) { + return 0; + } + + ret = epoll_wait(std_ev->epoll_fd, events, MAXEVENTS, timeout); + + if (ret == -1 && errno == EINTR && std_ev->ev->signal_events) { + if (tevent_common_check_signal(std_ev->ev)) { + return 0; + } + } + + if (ret == -1 && errno != EINTR) { + epoll_fallback_to_select(std_ev, "epoll_wait() failed"); + return -1; + } + + if (ret == 0 && tvalp) { + /* we don't care about a possible delay here */ + tevent_common_loop_timer_delay(std_ev->ev); + return 0; + } + + for (i=0;iadditional_flags |= EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR; + /* + * if we only wait for TEVENT_FD_WRITE, we should not tell the + * event handler about it, and remove the epoll_event, + * as we only report errors when waiting for read events, + * to match the select() behavior + */ + if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR)) { + epoll_del_event(std_ev, fde); + continue; + } + flags |= TEVENT_FD_READ; + } + if (events[i].events & EPOLLIN) flags |= TEVENT_FD_READ; + if (events[i].events & EPOLLOUT) flags |= TEVENT_FD_WRITE; + if (flags) { + fde->handler(std_ev->ev, fde, flags, fde->private_data); + break; + } + } + + return 0; +} +#else +#define epoll_init_ctx(std_ev) +#define epoll_add_event(std_ev,fde) +#define epoll_del_event(std_ev,fde) +#define epoll_change_event(std_ev,fde) +#define epoll_event_loop(std_ev,tvalp) (-1) +#define epoll_check_reopen(std_ev) +#endif + +/* + create a std_event_context structure. +*/ +static int std_event_context_init(struct tevent_context *ev) +{ + struct std_event_context *std_ev; + + std_ev = talloc_zero(ev, struct std_event_context); + if (!std_ev) return -1; + std_ev->ev = ev; + std_ev->epoll_fd = -1; + + epoll_init_ctx(std_ev); + + ev->additional_data = std_ev; + return 0; +} + +/* + recalculate the maxfd +*/ +static void calc_maxfd(struct std_event_context *std_ev) +{ + struct tevent_fd *fde; + + std_ev->maxfd = 0; + for (fde = std_ev->ev->fd_events; fde; fde = fde->next) { + if (fde->fd > std_ev->maxfd) { + std_ev->maxfd = fde->fd; + } + } +} + + +/* to mark the ev->maxfd invalid + * this means we need to recalculate it + */ +#define EVENT_INVALID_MAXFD (-1) + +/* + destroy an fd_event +*/ +static int std_event_fd_destructor(struct tevent_fd *fde) +{ + struct tevent_context *ev = fde->event_ctx; + struct std_event_context *std_ev = NULL; + + if (ev) { + std_ev = talloc_get_type(ev->additional_data, + struct std_event_context); + + epoll_check_reopen(std_ev); + + if (std_ev->maxfd == fde->fd) { + std_ev->maxfd = EVENT_INVALID_MAXFD; + } + + epoll_del_event(std_ev, fde); + } + + return tevent_common_fd_destructor(fde); +} + +/* + add a fd based event + return NULL on failure (memory allocation error) +*/ +static struct tevent_fd *std_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx, + int fd, uint16_t flags, + tevent_fd_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + struct std_event_context *std_ev = talloc_get_type(ev->additional_data, + struct std_event_context); + struct tevent_fd *fde; + + epoll_check_reopen(std_ev); + + fde = tevent_common_add_fd(ev, mem_ctx, fd, flags, + handler, private_data, + handler_name, location); + if (!fde) return NULL; + + if ((std_ev->maxfd != EVENT_INVALID_MAXFD) + && (fde->fd > std_ev->maxfd)) { + std_ev->maxfd = fde->fd; + } + talloc_set_destructor(fde, std_event_fd_destructor); + + epoll_add_event(std_ev, fde); + + return fde; +} + +/* + set the fd event flags +*/ +static void std_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags) +{ + struct tevent_context *ev; + struct std_event_context *std_ev; + + if (fde->flags == flags) return; + + ev = fde->event_ctx; + std_ev = talloc_get_type(ev->additional_data, struct std_event_context); + + fde->flags = flags; + + epoll_check_reopen(std_ev); + + epoll_change_event(std_ev, fde); +} + +/* + event loop handling using select() +*/ +static int std_event_loop_select(struct std_event_context *std_ev, struct timeval *tvalp) +{ + fd_set r_fds, w_fds; + struct tevent_fd *fde; + int selrtn; + + /* we maybe need to recalculate the maxfd */ + if (std_ev->maxfd == EVENT_INVALID_MAXFD) { + calc_maxfd(std_ev); + } + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + + /* setup any fd events */ + for (fde = std_ev->ev->fd_events; fde; fde = fde->next) { + if (fde->flags & TEVENT_FD_READ) { + FD_SET(fde->fd, &r_fds); + } + if (fde->flags & TEVENT_FD_WRITE) { + FD_SET(fde->fd, &w_fds); + } + } + + if (std_ev->ev->signal_events && + tevent_common_check_signal(std_ev->ev)) { + return 0; + } + + selrtn = select(std_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); + + if (selrtn == -1 && errno == EINTR && + std_ev->ev->signal_events) { + tevent_common_check_signal(std_ev->ev); + return 0; + } + + if (selrtn == -1 && errno == EBADF) { + /* the socket is dead! this should never + happen as the socket should have first been + made readable and that should have removed + the event, so this must be a bug. This is a + fatal error. */ + tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, + "ERROR: EBADF on std_event_loop_once\n"); + std_ev->exit_code = EBADF; + return -1; + } + + if (selrtn == 0 && tvalp) { + /* we don't care about a possible delay here */ + tevent_common_loop_timer_delay(std_ev->ev); + return 0; + } + + if (selrtn > 0) { + /* at least one file descriptor is ready - check + which ones and call the handler, being careful to allow + the handler to remove itself when called */ + for (fde = std_ev->ev->fd_events; fde; fde = fde->next) { + uint16_t flags = 0; + + if (FD_ISSET(fde->fd, &r_fds)) flags |= TEVENT_FD_READ; + if (FD_ISSET(fde->fd, &w_fds)) flags |= TEVENT_FD_WRITE; + if (flags & fde->flags) { + fde->handler(std_ev->ev, fde, flags, fde->private_data); + break; + } + } + } + + return 0; +} + +/* + do a single event loop using the events defined in ev +*/ +static int std_event_loop_once(struct tevent_context *ev, const char *location) +{ + struct std_event_context *std_ev = talloc_get_type(ev->additional_data, + struct std_event_context); + struct timeval tval; + + if (ev->signal_events && + tevent_common_check_signal(ev)) { + return 0; + } + + if (ev->immediate_events && + tevent_common_loop_immediate(ev)) { + return 0; + } + + tval = tevent_common_loop_timer_delay(ev); + if (tevent_timeval_is_zero(&tval)) { + return 0; + } + + epoll_check_reopen(std_ev); + + if (epoll_event_loop(std_ev, &tval) == 0) { + return 0; + } + + return std_event_loop_select(std_ev, &tval); +} + +static const struct tevent_ops std_event_ops = { + .context_init = std_event_context_init, + .add_fd = std_event_add_fd, + .set_fd_close_fn = tevent_common_fd_set_close_fn, + .get_fd_flags = tevent_common_fd_get_flags, + .set_fd_flags = std_event_set_fd_flags, + .add_timer = tevent_common_add_timer, + .schedule_immediate = tevent_common_schedule_immediate, + .add_signal = tevent_common_add_signal, + .loop_once = std_event_loop_once, + .loop_wait = tevent_common_loop_wait, +}; + + +_PRIVATE_ bool tevent_standard_init(void) +{ + return tevent_register_backend("standard", &std_event_ops); +} + diff --git a/ccan/tevent/tevent_timed.c b/ccan/tevent/tevent_timed.c new file mode 100644 index 0000000..ec3135d --- /dev/null +++ b/ccan/tevent/tevent_timed.c @@ -0,0 +1,267 @@ +/* + Unix SMB/CIFS implementation. + + common events code for timed events + + Copyright (C) Andrew Tridgell 2003-2006 + Copyright (C) Stefan Metzmacher 2005-2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include +#include + +/** + compare two timeval structures. + Return -1 if tv1 < tv2 + Return 0 if tv1 == tv2 + Return 1 if tv1 > tv2 +*/ +int tevent_timeval_compare(const struct timeval *tv1, const struct timeval *tv2) +{ + if (tv1->tv_sec > tv2->tv_sec) return 1; + if (tv1->tv_sec < tv2->tv_sec) return -1; + if (tv1->tv_usec > tv2->tv_usec) return 1; + if (tv1->tv_usec < tv2->tv_usec) return -1; + return 0; +} + +/** + return a zero timeval +*/ +struct timeval tevent_timeval_zero(void) +{ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + return tv; +} + +/** + return a timeval for the current time +*/ +struct timeval tevent_timeval_current(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv; +} + +/** + return a timeval struct with the given elements +*/ +struct timeval tevent_timeval_set(uint32_t secs, uint32_t usecs) +{ + struct timeval tv; + tv.tv_sec = secs; + tv.tv_usec = usecs; + return tv; +} + +/** + return the difference between two timevals as a timeval + if tv1 comes after tv2, then return a zero timeval + (this is *tv2 - *tv1) +*/ +struct timeval tevent_timeval_until(const struct timeval *tv1, + const struct timeval *tv2) +{ + struct timeval t; + if (tevent_timeval_compare(tv1, tv2) >= 0) { + return tevent_timeval_zero(); + } + t.tv_sec = tv2->tv_sec - tv1->tv_sec; + if (tv1->tv_usec > tv2->tv_usec) { + t.tv_sec--; + t.tv_usec = 1000000 - (tv1->tv_usec - tv2->tv_usec); + } else { + t.tv_usec = tv2->tv_usec - tv1->tv_usec; + } + return t; +} + +/** + return true if a timeval is zero +*/ +bool tevent_timeval_is_zero(const struct timeval *tv) +{ + return tv->tv_sec == 0 && tv->tv_usec == 0; +} + +struct timeval tevent_timeval_add(const struct timeval *tv, uint32_t secs, + uint32_t usecs) +{ + struct timeval tv2 = *tv; + tv2.tv_sec += secs; + tv2.tv_usec += usecs; + tv2.tv_sec += tv2.tv_usec / 1000000; + tv2.tv_usec = tv2.tv_usec % 1000000; + + return tv2; +} + +/** + return a timeval in the future with a specified offset +*/ +struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs) +{ + struct timeval tv = tevent_timeval_current(); + return tevent_timeval_add(&tv, secs, usecs); +} + +/* + destroy a timed event +*/ +static int tevent_common_timed_destructor(struct tevent_timer *te) +{ + tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE, + "Destroying timer event %p \"%s\"\n", + te, te->handler_name); + + if (te->event_ctx) { + DLIST_REMOVE(te->event_ctx->timer_events, te); + } + + return 0; +} + +static int tevent_common_timed_deny_destructor(struct tevent_timer *te) +{ + return -1; +} + +/* + add a timed event + return NULL on failure (memory allocation error) +*/ +struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_CTX *mem_ctx, + struct timeval next_event, + tevent_timer_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + struct tevent_timer *te, *last_te, *cur_te; + + te = talloc(mem_ctx?mem_ctx:ev, struct tevent_timer); + if (te == NULL) return NULL; + + te->event_ctx = ev; + te->next_event = next_event; + te->handler = handler; + te->private_data = private_data; + te->handler_name = handler_name; + te->location = location; + te->additional_data = NULL; + + /* keep the list ordered */ + last_te = NULL; + for (cur_te = ev->timer_events; cur_te; cur_te = cur_te->next) { + /* if the new event comes before the current one break */ + if (tevent_timeval_compare(&te->next_event, &cur_te->next_event) < 0) { + break; + } + + last_te = cur_te; + } + + DLIST_ADD_AFTER(ev->timer_events, te, last_te); + + talloc_set_destructor(te, tevent_common_timed_destructor); + + tevent_debug(ev, TEVENT_DEBUG_TRACE, + "Added timed event \"%s\": %p\n", + handler_name, te); + return te; +} + +/* + do a single event loop using the events defined in ev + + return the delay untill the next timed event, + or zero if a timed event was triggered +*/ +struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev) +{ + struct timeval current_time = tevent_timeval_zero(); + struct tevent_timer *te = ev->timer_events; + + if (!te) { + /* have a default tick time of 30 seconds. This guarantees + that code that uses its own timeout checking will be + able to proceeed eventually */ + return tevent_timeval_set(30, 0); + } + + /* + * work out the right timeout for the next timed event + * + * avoid the syscall to gettimeofday() if the timed event should + * be triggered directly + * + * if there's a delay till the next timed event, we're done + * with just returning the delay + */ + if (!tevent_timeval_is_zero(&te->next_event)) { + struct timeval delay; + + current_time = tevent_timeval_current(); + + delay = tevent_timeval_until(¤t_time, &te->next_event); + if (!tevent_timeval_is_zero(&delay)) { + return delay; + } + } + + /* + * ok, we have a timed event that we'll process ... + */ + + /* deny the handler to free the event */ + talloc_set_destructor(te, tevent_common_timed_deny_destructor); + + /* We need to remove the timer from the list before calling the + * handler because in a semi-async inner event loop called from the + * handler we don't want to come across this event again -- vl */ + DLIST_REMOVE(ev->timer_events, te); + + /* + * If the timed event was registered for a zero current_time, + * then we pass a zero timeval here too! To avoid the + * overhead of gettimeofday() calls. + * + * otherwise we pass the current time + */ + te->handler(ev, te, current_time, te->private_data); + + /* The destructor isn't necessary anymore, we've already removed the + * event from the list. */ + talloc_set_destructor(te, NULL); + + tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE, + "Ending timer event %p \"%s\"\n", + te, te->handler_name); + + talloc_free(te); + + return tevent_timeval_zero(); +} + diff --git a/ccan/tevent/tevent_util.c b/ccan/tevent/tevent_util.c new file mode 100644 index 0000000..f02c482 --- /dev/null +++ b/ccan/tevent/tevent_util.c @@ -0,0 +1,88 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Jelmer Vernooij 2005 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include +#include + +/** + return the number of elements in a string list +*/ +size_t ev_str_list_length(const char **list) +{ + size_t ret; + for (ret=0;list && list[ret];ret++) /* noop */ ; + return ret; +} + +/** + add an entry to a string list +*/ +const char **ev_str_list_add(const char **list, const char *s) +{ + size_t len = ev_str_list_length(list); + const char **ret; + + ret = talloc_realloc(NULL, list, const char *, len+2); + if (ret == NULL) return NULL; + + ret[len] = talloc_strdup(ret, s); + if (ret[len] == NULL) return NULL; + + ret[len+1] = NULL; + + return ret; +} + + +/** + Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, + else + if SYSV use O_NDELAY + if BSD use FNDELAY +**/ + +int ev_set_blocking(int fd, bool set) +{ + int val; +#ifdef O_NONBLOCK +#define FLAG_TO_SET O_NONBLOCK +#else +#ifdef SYSV +#define FLAG_TO_SET O_NDELAY +#else /* BSD */ +#define FLAG_TO_SET FNDELAY +#endif +#endif + + if((val = fcntl(fd, F_GETFL, 0)) == -1) + return -1; + if(set) /* Turn blocking on - ie. clear nonblock flag */ + val &= ~FLAG_TO_SET; + else + val |= FLAG_TO_SET; + return fcntl( fd, F_SETFL, val); +#undef FLAG_TO_SET +} diff --git a/ccan/tevent/tevent_util.h b/ccan/tevent/tevent_util.h new file mode 100644 index 0000000..9e1bd4c --- /dev/null +++ b/ccan/tevent/tevent_util.h @@ -0,0 +1,198 @@ +#ifndef CCAN_TEVENT_TEVENT_UTIL_H +#define CCAN_TEVENT_TEVENT_UTIL_H +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 1998-2010 + Copyright (C) Jelmer Vernooij 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* To use these macros you must have a structure containing a next and + prev pointer */ + +#ifndef _DLINKLIST_H +#define _DLINKLIST_H + +/* + February 2010 - changed list format to have a prev pointer from the + list head. This makes DLIST_ADD_END() O(1) even though we only have + one list pointer. + + The scheme is as follows: + + 1) with no entries in the list: + list_head == NULL + + 2) with 1 entry in the list: + list_head->next == NULL + list_head->prev == list_head + + 3) with 2 entries in the list: + list_head->next == element2 + list_head->prev == element2 + element2->prev == list_head + element2->next == NULL + + 4) with N entries in the list: + list_head->next == element2 + list_head->prev == elementN + elementN->prev == element{N-1} + elementN->next == NULL + + This allows us to find the tail of the list by using + list_head->prev, which means we can add to the end of the list in + O(1) time + + + Note that the 'type' arguments below are no longer needed, but + are kept for now to prevent an incompatible argument change + */ + + +/* + add an element at the front of a list +*/ +#define DLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (p)->prev = (list) = (p); \ + (p)->next = NULL; \ + } else { \ + (p)->prev = (list)->prev; \ + (list)->prev = (p); \ + (p)->next = (list); \ + (list) = (p); \ + } \ +} while (0) + +/* + remove an element from a list + Note that the element doesn't have to be in the list. If it + isn't then this is a no-op +*/ +#define DLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + (list) = (p)->next; \ + } else if ((list) && (p) == (list)->prev) { \ + (p)->prev->next = NULL; \ + (list)->prev = (p)->prev; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) != (list)) (p)->next = (p)->prev = NULL; \ +} while (0) + +/* + find the head of the list given any element in it. + Note that this costs O(N), so you should avoid this macro + if at all possible! +*/ +#define DLIST_HEAD(p, result_head) \ +do { \ + (result_head) = (p); \ + while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \ +} while(0) + +/* return the last element in the list */ +#define DLIST_TAIL(list) ((list)?(list)->prev:NULL) + +/* return the previous element in the list. */ +#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL) + +/* insert 'p' after the given element 'el' in a list. If el is NULL then + this is the same as a DLIST_ADD() */ +#define DLIST_ADD_AFTER(list, p, el) \ +do { \ + if (!(list) || !(el)) { \ + DLIST_ADD(list, p); \ + } else { \ + (p)->prev = (el); \ + (p)->next = (el)->next; \ + (el)->next = (p); \ + if ((p)->next) (p)->next->prev = (p); \ + if ((list)->prev == (el)) (list)->prev = (p); \ + }\ +} while (0) + + +/* + add to the end of a list. + Note that 'type' is ignored +*/ +#define DLIST_ADD_END(list, p, type) \ +do { \ + if (!(list)) { \ + DLIST_ADD(list, p); \ + } else { \ + DLIST_ADD_AFTER(list, p, (list)->prev); \ + } \ +} while (0) + +/* promote an element to the from of a list */ +#define DLIST_PROMOTE(list, p) \ +do { \ + DLIST_REMOVE(list, p); \ + DLIST_ADD(list, p); \ +} while (0) + +/* + demote an element to the end of a list. + Note that 'type' is ignored +*/ +#define DLIST_DEMOTE(list, p, type) \ +do { \ + DLIST_REMOVE(list, p); \ + DLIST_ADD_END(list, p, NULL); \ +} while (0) + +/* + concatenate two lists - putting all elements of the 2nd list at the + end of the first list. + Note that 'type' is ignored +*/ +#define DLIST_CONCATENATE(list1, list2, type) \ +do { \ + if (!(list1)) { \ + (list1) = (list2); \ + } else { \ + (list1)->prev->next = (list2); \ + if (list2) { \ + void *_tmplist = (void *)(list1)->prev; \ + (list1)->prev = (list2)->prev; \ + (list2)->prev = _tmplist; \ + } \ + } \ +} while (0) + +#endif /* _DLINKLIST_H */ + +const char **ev_str_list_add(const char **list, const char *s); +int ev_set_blocking(int fd, bool set); +size_t ev_str_list_length(const char **list); + +/* Defined here so we can build against older talloc versions that don't + * have this define yet. */ +#ifndef TALLOC_FREE +#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) +#endif + +#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) + +#define _PRIVATE_ +#endif /* CCAN_TEVENT_TEVENT_UTIL_H */ -- 2.39.2