]> git.ozlabs.org Git - ccan-lca-2011.git/commitdiff
lca2011: hacky import of tevent.
authorRusty Russell <rusty@rustcorp.com.au>
Sat, 22 Jan 2011 03:39:59 +0000 (14:09 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Sat, 22 Jan 2011 03:39:59 +0000 (14:09 +1030)
13 files changed:
ccan/tevent/_info [new file with mode: 0644]
ccan/tevent/tevent.c [new file with mode: 0644]
ccan/tevent/tevent.h [new file with mode: 0644]
ccan/tevent/tevent_debug.c [new file with mode: 0644]
ccan/tevent/tevent_fd.c [new file with mode: 0644]
ccan/tevent/tevent_immediate.c [new file with mode: 0644]
ccan/tevent/tevent_internal.h [new file with mode: 0644]
ccan/tevent/tevent_select.c [new file with mode: 0644]
ccan/tevent/tevent_signal.c [new file with mode: 0644]
ccan/tevent/tevent_standard.c [new file with mode: 0644]
ccan/tevent/tevent_timed.c [new file with mode: 0644]
ccan/tevent/tevent_util.c [new file with mode: 0644]
ccan/tevent/tevent_util.h [new file with mode: 0644]

diff --git a/ccan/tevent/_info b/ccan/tevent/_info
new file mode 100644 (file)
index 0000000..1d50b73
--- /dev/null
@@ -0,0 +1,23 @@
+#include <string.h>
+#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 (file)
index 0000000..a1825a7
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+  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 <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+#include <ccan/tevent/tevent_util.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+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 (file)
index 0000000..284cf6a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __TEVENT_H__
+#define __TEVENT_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/compiler/compiler.h>
+
+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 (file)
index 0000000..8df5c75
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+
+/********************************************************************
+ * 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 (file)
index 0000000..67e7e9c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+#include <ccan/tevent/tevent_util.h>
+
+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 (file)
index 0000000..3dd14df
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+#include <ccan/tevent/tevent_util.h>
+
+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 (file)
index 0000000..682dc66
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+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 (file)
index 0000000..dce40b4
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+#include <ccan/tevent/tevent_util.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+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 (file)
index 0000000..f2efe76
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+#include <ccan/tevent/tevent_util.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#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 <petr@vandrovec.name>
+  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;i<TEVENT_NUM_SIGNALS+1;i++) {
+               struct tevent_common_signal_list *sl, *next;
+               struct tevent_sigcounter counter = sig_state->signal_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;j<count;j++) {
+                                       /* sig_state->signal_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;j<count;j++) {
+                               uint32_t ofs = (counter.seen + j)
+                                       % TEVENT_SA_INFO_QUEUE_COUNT;
+                               memset((void*)&sig_state->sig_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 (file)
index 0000000..cd9823c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+  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 <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+#include <ccan/tevent/tevent_util.h>
+#include <errno.h>
+
+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;i<ret;i++) {
+               struct tevent_fd *fde = talloc_get_type(events[i].data.ptr, 
+                                                      struct tevent_fd);
+               uint16_t flags = 0;
+
+               if (fde == NULL) {
+                       epoll_fallback_to_select(std_ev, "epoll_wait() gave bad data");
+                       return -1;
+               }
+               if (events[i].events & (EPOLLHUP|EPOLLERR)) {
+                       fde->additional_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 (file)
index 0000000..ec3135d
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+#include <ccan/tevent/tevent_util.h>
+#include <sys/time.h>
+
+/**
+  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(&current_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 (file)
index 0000000..f02c482
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <ccan/tevent/tevent.h>
+#include <ccan/tevent/tevent_internal.h>
+#include <ccan/tevent/tevent_util.h>
+#include <fcntl.h>
+
+/**
+  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 (file)
index 0000000..9e1bd4c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/* 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 */