]> arthur.barton.de Git - netatalk.git/commitdiff
Add lib tevent from Samba
authorFrank Lahm <franklahm@googlemail.com>
Tue, 18 Jan 2011 07:19:31 +0000 (08:19 +0100)
committerFrank Lahm <franklahm@googlemail.com>
Tue, 18 Jan 2011 07:19:31 +0000 (08:19 +0100)
24 files changed:
configure.in
include/atalk/Makefile.am
include/atalk/tevent.h [new file with mode: 0644]
include/atalk/util.h
libatalk/Makefile.am
libatalk/talloc/.gitignore [new file with mode: 0644]
libatalk/tevent/.gitignore [new file with mode: 0644]
libatalk/tevent/Makefile.am [new file with mode: 0644]
libatalk/tevent/tevent.c [new file with mode: 0644]
libatalk/tevent/tevent_debug.c [new file with mode: 0644]
libatalk/tevent/tevent_epoll.c [new file with mode: 0644]
libatalk/tevent/tevent_fd.c [new file with mode: 0644]
libatalk/tevent/tevent_immediate.c [new file with mode: 0644]
libatalk/tevent/tevent_internal.h [new file with mode: 0644]
libatalk/tevent/tevent_liboop.loT [new file with mode: 0644]
libatalk/tevent/tevent_queue.c [new file with mode: 0644]
libatalk/tevent/tevent_req.c [new file with mode: 0644]
libatalk/tevent/tevent_select.c [new file with mode: 0644]
libatalk/tevent/tevent_signal.c [new file with mode: 0644]
libatalk/tevent/tevent_standard.c [new file with mode: 0644]
libatalk/tevent/tevent_timed.c [new file with mode: 0644]
libatalk/tevent/tevent_util.c [new file with mode: 0644]
libatalk/tevent/tevent_util.h [new file with mode: 0644]
libatalk/tevent/tevent_wakeup.c [new file with mode: 0644]

index 293202d300b95efae4412d6db22d5ef521d81d46..1cb90d16eecc6eeb3e6d5c509adaed3499fa1a0d 100644 (file)
@@ -35,6 +35,9 @@ AC_CHECK_HEADERS(netdb.h sgtty.h mount.h statfs.h dlfcn.h errno.h langinfo.h loc
 AC_CHECK_HEADERS(sys/param.h sys/fcntl.h sys/file.h sys/ioctl.h sys/time.h)
 AC_CHECK_HEADERS(sys/mnttab.h sys/statvfs.h sys/stat.h sys/vfs.h)
 AC_CHECK_HEADERS(sys/termios.h sys/types.h sys/errno.h sys/uio.h sys/filio.h)
+dnl Checks for header files, conformed to be required as of 2011
+AC_CHECK_HEADERS(sys/epoll.h)
+
 AC_CHECK_HEADER(sys/cdefs.h,,
        AC_MSG_RESULT([enabling generic cdefs.h from tree])
        CFLAGS="-I\$(top_srcdir)/sys/generic $CFLAGS"
@@ -1349,6 +1352,7 @@ AC_OUTPUT([Makefile
        libatalk/netddp/Makefile
        libatalk/util/Makefile
        libatalk/talloc/Makefile
+       libatalk/tevent/Makefile
        libatalk/tdb/Makefile
        libatalk/unicode/Makefile
        libatalk/unicode/charsets/Makefile
index 2bf09187b4c6cb996e3a729d0e9c776cabde0311..e816978034dbb117d4e4da7ea4dc226b33438512 100644 (file)
@@ -1,11 +1,50 @@
 # Makefile.am for include/atalk/
 
 atalkincludedir = $(includedir)/atalk
+
 atalkinclude_HEADERS = \
-       adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h \
-       cnid.h compat.h ddp.h dsi.h ldapconfig.h list.h logger.h \
-       nbp.h netddp.h pap.h paths.h queue.h rtmp.h server_child.h \
-       server_ipc.h tdb.h uam.h unicode.h util.h uuid.h volinfo.h \
-       zip.h ea.h acl.h unix.h directory.h hash.h volume.h
+       adouble.h \
+       afp.h \
+       vfs.h \
+       cnid.h \
+       logger.h \
+       paths.h \
+       unicode.h \
+       util.h \
+       volinfo.h \
+       ea.h \
+       acl.h \
+       unix.h \
+       volume.h
 
-noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstradd.h bstrlib.h errchk.h ftw.h
+noinst_HEADERS = \
+       directory.h \
+       hash.h \
+       zip.h \
+       uuid.h \
+       queue.h \
+       rtmp.h \
+       server_child.h \
+       server_ipc.h \
+       tdb.h \
+       uam.h \
+       nbp.h \
+       netddp.h \
+       pap.h \
+       cnid_dbd_private.h \
+       cnid_private.h \
+       bstradd.h \
+       bstrlib.h \
+       errchk.h \
+       ftw.h \
+       talloc.h \
+       tevent.h \
+       aep.h \
+       asp.h \
+       atp.h \
+       boolean.h \
+       compat.h \
+       ddp.h \
+       dsi.h \
+       ldapconfig.h \
+       list.h
diff --git a/include/atalk/tevent.h b/include/atalk/tevent.h
new file mode 100644 (file)
index 0000000..242603a
--- /dev/null
@@ -0,0 +1,518 @@
+/* 
+   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 "config.h"
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#ifdef HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
+
+#include <atalk/talloc.h>
+
+struct tevent_context;
+struct tevent_ops;
+struct tevent_fd;
+struct tevent_timer;
+struct tevent_immediate;
+struct tevent_signal;
+
+/* event handler types */
+typedef void (*tevent_fd_handler_t)(struct tevent_context *ev,
+                                   struct tevent_fd *fde,
+                                   uint16_t flags,
+                                   void *private_data);
+typedef void (*tevent_fd_close_fn_t)(struct tevent_context *ev,
+                                    struct tevent_fd *fde,
+                                    int fd,
+                                    void *private_data);
+typedef void (*tevent_timer_handler_t)(struct tevent_context *ev,
+                                      struct tevent_timer *te,
+                                      struct timeval current_time,
+                                      void *private_data);
+typedef void (*tevent_immediate_handler_t)(struct tevent_context *ctx,
+                                          struct tevent_immediate *im,
+                                          void *private_data);
+typedef void (*tevent_signal_handler_t)(struct tevent_context *ev,
+                                       struct tevent_signal *se,
+                                       int signum,
+                                       int count,
+                                       void *siginfo,
+                                       void *private_data);
+
+struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx);
+struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx, const char *name);
+const char **tevent_backend_list(TALLOC_CTX *mem_ctx);
+void tevent_set_default_backend(const char *backend);
+
+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__)
+
+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__)
+
+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__)
+
+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__);
+
+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__)
+
+int _tevent_loop_once(struct tevent_context *ev, const char *location);
+#define tevent_loop_once(ev) \
+       _tevent_loop_once(ev, __location__) \
+
+int _tevent_loop_wait(struct tevent_context *ev, const char *location);
+#define tevent_loop_wait(ev) \
+       _tevent_loop_wait(ev, __location__) \
+
+void tevent_fd_set_close_fn(struct tevent_fd *fde,
+                           tevent_fd_close_fn_t close_fn);
+void tevent_fd_set_auto_close(struct tevent_fd *fde);
+uint16_t tevent_fd_get_flags(struct tevent_fd *fde);
+void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags);
+
+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 */
+#define TEVENT_FD_READ 1
+#define TEVENT_FD_WRITE 2
+
+#define TEVENT_FD_WRITEABLE(fde) \
+       tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) | TEVENT_FD_WRITE)
+#define TEVENT_FD_READABLE(fde) \
+       tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) | TEVENT_FD_READ)
+
+#define TEVENT_FD_NOT_WRITEABLE(fde) \
+       tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) & ~TEVENT_FD_WRITE)
+#define TEVENT_FD_NOT_READABLE(fde) \
+       tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) & ~TEVENT_FD_READ)
+
+/* DEBUG */
+enum tevent_debug_level {
+       TEVENT_DEBUG_FATAL,
+       TEVENT_DEBUG_ERROR,
+       TEVENT_DEBUG_WARNING,
+       TEVENT_DEBUG_TRACE
+};
+
+int tevent_set_debug(struct tevent_context *ev,
+                    void (*debug)(void *context,
+                                  enum tevent_debug_level level,
+                                  const char *fmt,
+                                  va_list ap) PRINTF_ATTRIBUTE(3,0),
+                    void *context);
+int tevent_set_debug_stderr(struct tevent_context *ev);
+
+/**
+ * An async request moves between the following 4 states:
+ */
+enum tevent_req_state {
+       /**
+        * we are creating the request
+        */
+       TEVENT_REQ_INIT,
+       /**
+        * we are waiting the request to complete
+        */
+       TEVENT_REQ_IN_PROGRESS,
+       /**
+        * the request is finished
+        */
+       TEVENT_REQ_DONE,
+       /**
+        * A user error has occured
+        */
+       TEVENT_REQ_USER_ERROR,
+       /**
+        * Request timed out
+        */
+       TEVENT_REQ_TIMED_OUT,
+       /**
+        * No memory in between
+        */
+       TEVENT_REQ_NO_MEMORY,
+       /**
+        * the request is already received by the caller
+        */
+       TEVENT_REQ_RECEIVED
+};
+
+/**
+ * @brief An async request
+ *
+ * This represents an async request being processed by callbacks via an event
+ * context. A user can issue for example a write request to a socket, giving
+ * an implementation function the fd, the buffer and the number of bytes to
+ * transfer. The function issuing the request will immediately return without
+ * blocking most likely without having sent anything. The API user then fills
+ * in req->async.fn and req->async.private_data, functions that are called
+ * when the request is finished.
+ *
+ * It is up to the user of the async request to talloc_free it after it has
+ * finished. This can happen while the completion function is called.
+ */
+
+struct tevent_req;
+
+typedef void (*tevent_req_fn)(struct tevent_req *);
+
+void tevent_req_set_callback(struct tevent_req *req, tevent_req_fn fn, void *pvt);
+void *_tevent_req_callback_data(struct tevent_req *req);
+void *_tevent_req_data(struct tevent_req *req);
+
+#define tevent_req_callback_data(_req, _type) \
+       talloc_get_type_abort(_tevent_req_callback_data(_req), _type)
+#define tevent_req_callback_data_void(_req) \
+       _tevent_req_callback_data(_req)
+#define tevent_req_data(_req, _type) \
+       talloc_get_type_abort(_tevent_req_data(_req), _type)
+
+typedef char *(*tevent_req_print_fn)(struct tevent_req *, TALLOC_CTX *);
+
+void tevent_req_set_print_fn(struct tevent_req *req, tevent_req_print_fn fn);
+
+char *tevent_req_default_print(struct tevent_req *req, TALLOC_CTX *mem_ctx);
+
+char *tevent_req_print(TALLOC_CTX *mem_ctx, struct tevent_req *req);
+
+typedef bool (*tevent_req_cancel_fn)(struct tevent_req *);
+
+void tevent_req_set_cancel_fn(struct tevent_req *req, tevent_req_cancel_fn fn);
+
+bool _tevent_req_cancel(struct tevent_req *req, const char *location);
+#define tevent_req_cancel(req) \
+       _tevent_req_cancel(req, __location__)
+
+struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx,
+                                     void *pstate,
+                                     size_t state_size,
+                                     const char *type,
+                                     const char *location);
+
+#define tevent_req_create(_mem_ctx, _pstate, _type) \
+       _tevent_req_create((_mem_ctx), (_pstate), sizeof(_type), \
+                          #_type, __location__)
+
+bool tevent_req_set_endtime(struct tevent_req *req,
+                           struct tevent_context *ev,
+                           struct timeval endtime);
+
+void _tevent_req_notify_callback(struct tevent_req *req, const char *location);
+#define tevent_req_notify_callback(req)                \
+       _tevent_req_notify_callback(req, __location__)
+
+void _tevent_req_done(struct tevent_req *req,
+                     const char *location);
+#define tevent_req_done(req) \
+       _tevent_req_done(req, __location__)
+
+bool _tevent_req_error(struct tevent_req *req,
+                      uint64_t error,
+                      const char *location);
+#define tevent_req_error(req, error) \
+       _tevent_req_error(req, error, __location__)
+
+bool _tevent_req_nomem(const void *p,
+                      struct tevent_req *req,
+                      const char *location);
+#define tevent_req_nomem(p, req) \
+       _tevent_req_nomem(p, req, __location__)
+
+struct tevent_req *tevent_req_post(struct tevent_req *req,
+                                  struct tevent_context *ev);
+
+bool tevent_req_is_in_progress(struct tevent_req *req);
+
+bool tevent_req_poll(struct tevent_req *req,
+                    struct tevent_context *ev);
+
+bool tevent_req_is_error(struct tevent_req *req,
+                        enum tevent_req_state *state,
+                        uint64_t *error);
+
+void tevent_req_received(struct tevent_req *req);
+
+struct tevent_req *tevent_wakeup_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct timeval wakeup_time);
+bool tevent_wakeup_recv(struct tevent_req *req);
+
+int tevent_timeval_compare(const struct timeval *tv1,
+                          const struct timeval *tv2);
+
+struct timeval tevent_timeval_zero(void);
+
+struct timeval tevent_timeval_current(void);
+
+struct timeval tevent_timeval_set(uint32_t secs, uint32_t usecs);
+
+struct timeval tevent_timeval_until(const struct timeval *tv1,
+                                   const struct timeval *tv2);
+
+bool tevent_timeval_is_zero(const struct timeval *tv);
+
+struct timeval tevent_timeval_add(const struct timeval *tv, uint32_t secs,
+                                 uint32_t usecs);
+
+struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs);
+
+struct tevent_queue;
+
+struct tevent_queue *_tevent_queue_create(TALLOC_CTX *mem_ctx,
+                                         const char *name,
+                                         const char *location);
+
+#define tevent_queue_create(_mem_ctx, _name) \
+       _tevent_queue_create((_mem_ctx), (_name), __location__)
+
+typedef void (*tevent_queue_trigger_fn_t)(struct tevent_req *req,
+                                         void *private_data);
+bool tevent_queue_add(struct tevent_queue *queue,
+                     struct tevent_context *ev,
+                     struct tevent_req *req,
+                     tevent_queue_trigger_fn_t trigger,
+                     void *private_data);
+void tevent_queue_start(struct tevent_queue *queue);
+void tevent_queue_stop(struct tevent_queue *queue);
+
+size_t tevent_queue_length(struct tevent_queue *queue);
+
+typedef int (*tevent_nesting_hook)(struct tevent_context *ev,
+                                  void *private_data,
+                                  uint32_t level,
+                                  bool begin,
+                                  void *stack_ptr,
+                                  const char *location);
+#ifdef TEVENT_DEPRECATED
+#ifndef _DEPRECATED_
+#if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1 )
+#define _DEPRECATED_ __attribute__ ((deprecated))
+#else
+#define _DEPRECATED_
+#endif
+#endif
+void tevent_loop_allow_nesting(struct tevent_context *ev) _DEPRECATED_;
+void tevent_loop_set_nesting_hook(struct tevent_context *ev,
+                                 tevent_nesting_hook hook,
+                                 void *private_data) _DEPRECATED_;
+int _tevent_loop_until(struct tevent_context *ev,
+                      bool (*finished)(void *private_data),
+                      void *private_data,
+                      const char *location) _DEPRECATED_;
+#define tevent_loop_until(ev, finished, private_data) \
+       _tevent_loop_until(ev, finished, private_data, __location__)
+#endif
+
+
+/**
+ * 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);
+
+
+/**
+ * The following definitions are usueful only for compatibility with the
+ * implementation originally developed within the samba4 code and will be
+ * soon removed. Please NEVER use in new code.
+ */
+
+#ifdef TEVENT_COMPAT_DEFINES
+
+#define event_context  tevent_context
+#define event_ops      tevent_ops
+#define fd_event       tevent_fd
+#define timed_event    tevent_timer
+#define signal_event   tevent_signal
+
+#define event_fd_handler_t     tevent_fd_handler_t
+#define event_timed_handler_t  tevent_timer_handler_t
+#define event_signal_handler_t tevent_signal_handler_t
+
+#define event_context_init(mem_ctx) \
+       tevent_context_init(mem_ctx)
+
+#define event_context_init_byname(mem_ctx, name) \
+       tevent_context_init_byname(mem_ctx, name)
+
+#define event_backend_list(mem_ctx) \
+       tevent_backend_list(mem_ctx)
+
+#define event_set_default_backend(backend) \
+       tevent_set_default_backend(backend)
+
+#define event_add_fd(ev, mem_ctx, fd, flags, handler, private_data) \
+       tevent_add_fd(ev, mem_ctx, fd, flags, handler, private_data)
+
+#define event_add_timed(ev, mem_ctx, next_event, handler, private_data) \
+       tevent_add_timer(ev, mem_ctx, next_event, handler, private_data)
+
+#define event_add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data) \
+       tevent_add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data)
+
+#define event_loop_once(ev) \
+       tevent_loop_once(ev)
+
+#define event_loop_wait(ev) \
+       tevent_loop_wait(ev)
+
+#define event_get_fd_flags(fde) \
+       tevent_fd_get_flags(fde)
+
+#define event_set_fd_flags(fde, flags) \
+       tevent_fd_set_flags(fde, flags)
+
+#define EVENT_FD_READ          TEVENT_FD_READ
+#define EVENT_FD_WRITE         TEVENT_FD_WRITE
+
+#define EVENT_FD_WRITEABLE(fde) \
+       TEVENT_FD_WRITEABLE(fde)
+
+#define EVENT_FD_READABLE(fde) \
+       TEVENT_FD_READABLE(fde)
+
+#define EVENT_FD_NOT_WRITEABLE(fde) \
+       TEVENT_FD_NOT_WRITEABLE(fde)
+
+#define EVENT_FD_NOT_READABLE(fde) \
+       TEVENT_FD_NOT_READABLE(fde)
+
+#define ev_debug_level         tevent_debug_level
+
+#define EV_DEBUG_FATAL         TEVENT_DEBUG_FATAL
+#define EV_DEBUG_ERROR         TEVENT_DEBUG_ERROR
+#define EV_DEBUG_WARNING       TEVENT_DEBUG_WARNING
+#define EV_DEBUG_TRACE         TEVENT_DEBUG_TRACE
+
+#define ev_set_debug(ev, debug, context) \
+       tevent_set_debug(ev, debug, context)
+
+#define ev_set_debug_stderr(_ev) tevent_set_debug_stderr(ev)
+
+#endif /* TEVENT_COMPAT_DEFINES */
+
+#endif /* __TEVENT_H__ */
index c94306d23febdc7f19d2f632b543919cd68c0ff6..d60485e1614d3d0d54a546dedf455529f9221494 100644 (file)
@@ -54,6 +54,7 @@
 #endif
 
 #define STRCMP(a,b,c) (strcmp(a,c) b 0)
+#define ZERO_STRUCT(a) memset(&(a), 0, sizeof(a))
 
 #ifdef WITH_SENDFILE
 extern ssize_t sys_sendfile (int __out_fd, int __in_fd, off_t *__offset,size_t __count);
index ab44b3d6ef39437b79ae049892b3209198f37f20..17311efbddab500e29cc68d1282a09a53cc4876b 100644 (file)
@@ -1,7 +1,7 @@
 
 # Makefile.am for libatalk/
 
-SUBDIRS = acl adouble bstring compat cnid dsi talloc tdb util unicode vfs
+SUBDIRS = acl adouble bstring compat cnid dsi talloc tdb tevent util unicode vfs
 
 lib_LTLIBRARIES = libatalk.la
 
diff --git a/libatalk/talloc/.gitignore b/libatalk/talloc/.gitignore
new file mode 100644 (file)
index 0000000..c38bb9e
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+*.lo
+*.la
+.deps
+.libs
+*.o
diff --git a/libatalk/tevent/.gitignore b/libatalk/tevent/.gitignore
new file mode 100644 (file)
index 0000000..c38bb9e
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+*.lo
+*.la
+.deps
+.libs
+*.o
diff --git a/libatalk/tevent/Makefile.am b/libatalk/tevent/Makefile.am
new file mode 100644 (file)
index 0000000..48f7afd
--- /dev/null
@@ -0,0 +1,24 @@
+# Makefile.am for libatalk/tevent/
+
+noinst_LTLIBRARIES = libtevent.la
+
+AM_CFLAGS = 
+
+libtevent_la_SOURCES = \
+       tevent.c \
+       tevent_debug.c \
+       tevent_epoll.c \
+       tevent_fd.c \
+       tevent_immediate.c \
+       tevent_queue.c \
+       tevent_req.c \
+       tevent_select.c \
+       tevent_signal.c \
+       tevent_standard.c \
+       tevent_timed.c \
+       tevent_util.c \
+       tevent_wakeup.c
+
+noinst_HEADERS = \
+       tevent_internal.h \
+       tevent_util.h
\ No newline at end of file
diff --git a/libatalk/tevent/tevent.c b/libatalk/tevent/tevent.c
new file mode 100644 (file)
index 0000000..3720f9e
--- /dev/null
@@ -0,0 +1,618 @@
+/* 
+   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.
+
+*/
+#define TEVENT_DEPRECATED 1
+
+#include <atalk/tevent.h>
+
+#include "tevent_internal.h"
+#include "tevent_util.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(talloc_autofree_context(), 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(talloc_autofree_context(),
+                                              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)
+
+  if flags contains TEVENT_FD_AUTOCLOSE then the fd will be closed when
+  the returned fd_event context is freed
+*/
+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;
+}
+
+static void tevent_abort(struct tevent_context *ev, const char *reason)
+{
+       tevent_debug(ev, TEVENT_DEBUG_FATAL,
+                    "abort: %s\n", reason);
+
+       if (!tevent_abort_fn) {
+               abort();
+       }
+
+       tevent_abort_fn(reason);
+}
+
+/*
+  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);
+}
+
+void tevent_loop_allow_nesting(struct tevent_context *ev)
+{
+       ev->nesting.allowed = true;
+}
+
+void tevent_loop_set_nesting_hook(struct tevent_context *ev,
+                                 tevent_nesting_hook hook,
+                                 void *private_data)
+{
+       if (ev->nesting.hook_fn && 
+           (ev->nesting.hook_fn != hook ||
+            ev->nesting.hook_private != private_data)) {
+               /* the way the nesting hook code is currently written
+                  we cannot support two different nesting hooks at the
+                  same time. */
+               tevent_abort(ev, "tevent: Violation of nesting hook rules\n");
+       }
+       ev->nesting.hook_fn = hook;
+       ev->nesting.hook_private = private_data;
+}
+
+static void tevent_abort_nesting(struct tevent_context *ev, const char *location)
+{
+       const char *reason;
+
+       reason = talloc_asprintf(NULL, "tevent_loop_once() nesting at %s",
+                                location);
+       if (!reason) {
+               reason = "tevent_loop_once() nesting";
+       }
+
+       tevent_abort(ev, reason);
+}
+
+/*
+  do a single event loop using the events defined in ev 
+*/
+int _tevent_loop_once(struct tevent_context *ev, const char *location)
+{
+       int ret;
+       void *nesting_stack_ptr = NULL;
+
+       ev->nesting.level++;
+
+       if (ev->nesting.level > 1) {
+               if (!ev->nesting.allowed) {
+                       tevent_abort_nesting(ev, location);
+                       errno = ELOOP;
+                       return -1;
+               }
+       }
+       if (ev->nesting.level > 0) {
+               if (ev->nesting.hook_fn) {
+                       int ret2;
+                       ret2 = ev->nesting.hook_fn(ev,
+                                                  ev->nesting.hook_private,
+                                                  ev->nesting.level,
+                                                  true,
+                                                  (void *)&nesting_stack_ptr,
+                                                  location);
+                       if (ret2 != 0) {
+                               ret = ret2;
+                               goto done;
+                       }
+               }
+       }
+
+       ret = ev->ops->loop_once(ev, location);
+
+       if (ev->nesting.level > 0) {
+               if (ev->nesting.hook_fn) {
+                       int ret2;
+                       ret2 = ev->nesting.hook_fn(ev,
+                                                  ev->nesting.hook_private,
+                                                  ev->nesting.level,
+                                                  false,
+                                                  (void *)&nesting_stack_ptr,
+                                                  location);
+                       if (ret2 != 0) {
+                               ret = ret2;
+                               goto done;
+                       }
+               }
+       }
+
+done:
+       ev->nesting.level--;
+       return ret;
+}
+
+/*
+  this is a performance optimization for the samba4 nested event loop problems
+*/
+int _tevent_loop_until(struct tevent_context *ev,
+                      bool (*finished)(void *private_data),
+                      void *private_data,
+                      const char *location)
+{
+       int ret = 0;
+       void *nesting_stack_ptr = NULL;
+
+       ev->nesting.level++;
+
+       if (ev->nesting.level > 1) {
+               if (!ev->nesting.allowed) {
+                       tevent_abort_nesting(ev, location);
+                       errno = ELOOP;
+                       return -1;
+               }
+       }
+       if (ev->nesting.level > 0) {
+               if (ev->nesting.hook_fn) {
+                       int ret2;
+                       ret2 = ev->nesting.hook_fn(ev,
+                                                  ev->nesting.hook_private,
+                                                  ev->nesting.level,
+                                                  true,
+                                                  (void *)&nesting_stack_ptr,
+                                                  location);
+                       if (ret2 != 0) {
+                               ret = ret2;
+                               goto done;
+                       }
+               }
+       }
+
+       while (!finished(private_data)) {
+               ret = ev->ops->loop_once(ev, location);
+               if (ret != 0) {
+                       break;
+               }
+       }
+
+       if (ev->nesting.level > 0) {
+               if (ev->nesting.hook_fn) {
+                       int ret2;
+                       ret2 = ev->nesting.hook_fn(ev,
+                                                  ev->nesting.hook_private,
+                                                  ev->nesting.level,
+                                                  false,
+                                                  (void *)&nesting_stack_ptr,
+                                                  location);
+                       if (ret2 != 0) {
+                               ret = ret2;
+                               goto done;
+                       }
+               }
+       }
+
+done:
+       ev->nesting.level--;
+       return ret;
+}
+
+/*
+  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/libatalk/tevent/tevent_debug.c b/libatalk/tevent/tevent_debug.c
new file mode 100644 (file)
index 0000000..76d8c4e
--- /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 <atalk/tevent.h>
+#include "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_ATTRIBUTE(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_ATTRIBUTE(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/libatalk/tevent/tevent_epoll.c b/libatalk/tevent/tevent_epoll.c
new file mode 100644 (file)
index 0000000..17f61a7
--- /dev/null
@@ -0,0 +1,442 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   main select loop and event handling - epoll implementation
+
+   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 <atalk/tevent.h>
+#include <atalk/util.h>
+
+#include "tevent_internal.h"
+#include "tevent_util.h"
+
+struct epoll_event_context {
+       /* a pointer back to the generic event_context */
+       struct tevent_context *ev;
+
+       /* when using epoll this is the handle from epoll_create */
+       int epoll_fd;
+
+       pid_t pid;
+};
+
+/*
+  called when a epoll call fails, and we should fallback
+  to using select
+*/
+static void epoll_panic(struct epoll_event_context *epoll_ev, const char *reason)
+{
+       tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
+                "%s (%s) - calling abort()\n", reason, strerror(errno));
+       abort();
+}
+
+/*
+  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 epoll_event_context *epoll_ev)
+{
+       close(epoll_ev->epoll_fd);
+       epoll_ev->epoll_fd = -1;
+       return 0;
+}
+
+/*
+ init the epoll fd
+*/
+static int epoll_init_ctx(struct epoll_event_context *epoll_ev)
+{
+       epoll_ev->epoll_fd = epoll_create(64);
+       epoll_ev->pid = getpid();
+       talloc_set_destructor(epoll_ev, epoll_ctx_destructor);
+       if (epoll_ev->epoll_fd == -1) {
+               return -1;
+       }
+       return 0;
+}
+
+static void epoll_add_event(struct epoll_event_context *epoll_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 epoll_event_context *epoll_ev)
+{
+       struct tevent_fd *fde;
+
+       if (epoll_ev->pid == getpid()) {
+               return;
+       }
+
+       close(epoll_ev->epoll_fd);
+       epoll_ev->epoll_fd = epoll_create(64);
+       if (epoll_ev->epoll_fd == -1) {
+               tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
+                            "Failed to recreate epoll handle after fork\n");
+               return;
+       }
+       epoll_ev->pid = getpid();
+       for (fde=epoll_ev->ev->fd_events;fde;fde=fde->next) {
+               epoll_add_event(epoll_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 epoll_event_context *epoll_ev, struct tevent_fd *fde)
+{
+       struct epoll_event event;
+
+       if (epoll_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(epoll_ev->epoll_fd, EPOLL_CTL_ADD, fde->fd, &event) != 0) {
+               epoll_panic(epoll_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 epoll_event_context *epoll_ev, struct tevent_fd *fde)
+{
+       struct epoll_event event;
+
+       if (epoll_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;
+       if (epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_DEL, fde->fd, &event) != 0) {
+               tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
+                            "epoll_del_event failed! probable early close bug (%s)\n",
+                            strerror(errno));
+       }
+       fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT;
+}
+
+/*
+ change the epoll event to the given fd_event
+*/
+static void epoll_mod_event(struct epoll_event_context *epoll_ev, struct tevent_fd *fde)
+{
+       struct epoll_event event;
+       if (epoll_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(epoll_ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) {
+               epoll_panic(epoll_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 epoll_event_context *epoll_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 (epoll_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(epoll_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(epoll_ev, fde);
+               return;
+       }
+
+       /* there's no epoll_event attached to the fde */
+       if (want_read || (want_write && !got_error)) {
+               epoll_add_event(epoll_ev, fde);
+               return;
+       }
+}
+
+/*
+  event loop handling using epoll
+*/
+static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval *tvalp)
+{
+       int ret, i;
+#define MAXEVENTS 1
+       struct epoll_event events[MAXEVENTS];
+       int timeout = -1;
+
+       if (epoll_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 (epoll_ev->ev->signal_events &&
+           tevent_common_check_signal(epoll_ev->ev)) {
+               return 0;
+       }
+
+       ret = epoll_wait(epoll_ev->epoll_fd, events, MAXEVENTS, timeout);
+
+       if (ret == -1 && errno == EINTR && epoll_ev->ev->signal_events) {
+               if (tevent_common_check_signal(epoll_ev->ev)) {
+                       return 0;
+               }
+       }
+
+       if (ret == -1 && errno != EINTR) {
+               epoll_panic(epoll_ev, "epoll_wait() failed");
+               return -1;
+       }
+
+       if (ret == 0 && tvalp) {
+               /* we don't care about a possible delay here */
+               tevent_common_loop_timer_delay(epoll_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_panic(epoll_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(epoll_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(epoll_ev->ev, fde, flags, fde->private_data);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+/*
+  create a epoll_event_context structure.
+*/
+static int epoll_event_context_init(struct tevent_context *ev)
+{
+       int ret;
+       struct epoll_event_context *epoll_ev;
+
+       epoll_ev = talloc_zero(ev, struct epoll_event_context);
+       if (!epoll_ev) return -1;
+       epoll_ev->ev = ev;
+       epoll_ev->epoll_fd = -1;
+
+       ret = epoll_init_ctx(epoll_ev);
+       if (ret != 0) {
+               talloc_free(epoll_ev);
+               return ret;
+       }
+
+       ev->additional_data = epoll_ev;
+       return 0;
+}
+
+/*
+  destroy an fd_event
+*/
+static int epoll_event_fd_destructor(struct tevent_fd *fde)
+{
+       struct tevent_context *ev = fde->event_ctx;
+       struct epoll_event_context *epoll_ev = NULL;
+
+       if (ev) {
+               epoll_ev = talloc_get_type(ev->additional_data,
+                                          struct epoll_event_context);
+
+               epoll_check_reopen(epoll_ev);
+
+               epoll_del_event(epoll_ev, fde);
+       }
+
+       return tevent_common_fd_destructor(fde);
+}
+
+/*
+  add a fd based event
+  return NULL on failure (memory allocation error)
+*/
+static struct tevent_fd *epoll_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 epoll_event_context *epoll_ev = talloc_get_type(ev->additional_data,
+                                                          struct epoll_event_context);
+       struct tevent_fd *fde;
+
+       epoll_check_reopen(epoll_ev);
+
+       fde = tevent_common_add_fd(ev, mem_ctx, fd, flags,
+                                  handler, private_data,
+                                  handler_name, location);
+       if (!fde) return NULL;
+
+       talloc_set_destructor(fde, epoll_event_fd_destructor);
+
+       epoll_add_event(epoll_ev, fde);
+
+       return fde;
+}
+
+/*
+  set the fd event flags
+*/
+static void epoll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
+{
+       struct tevent_context *ev;
+       struct epoll_event_context *epoll_ev;
+
+       if (fde->flags == flags) return;
+
+       ev = fde->event_ctx;
+       epoll_ev = talloc_get_type(ev->additional_data, struct epoll_event_context);
+
+       fde->flags = flags;
+
+       epoll_check_reopen(epoll_ev);
+
+       epoll_change_event(epoll_ev, fde);
+}
+
+/*
+  do a single event loop using the events defined in ev 
+*/
+static int epoll_event_loop_once(struct tevent_context *ev, const char *location)
+{
+       struct epoll_event_context *epoll_ev = talloc_get_type(ev->additional_data,
+                                                          struct epoll_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(epoll_ev);
+
+       return epoll_event_loop(epoll_ev, &tval);
+}
+
+static const struct tevent_ops epoll_event_ops = {
+       .context_init           = epoll_event_context_init,
+       .add_fd                 = epoll_event_add_fd,
+       .set_fd_close_fn        = tevent_common_fd_set_close_fn,
+       .get_fd_flags           = tevent_common_fd_get_flags,
+       .set_fd_flags           = epoll_event_set_fd_flags,
+       .add_timer              = tevent_common_add_timer,
+       .schedule_immediate     = tevent_common_schedule_immediate,
+       .add_signal             = tevent_common_add_signal,
+       .loop_once              = epoll_event_loop_once,
+       .loop_wait              = tevent_common_loop_wait,
+};
+
+bool tevent_epoll_init(void)
+{
+       return tevent_register_backend("epoll", &epoll_event_ops);
+}
diff --git a/libatalk/tevent/tevent_fd.c b/libatalk/tevent/tevent_fd.c
new file mode 100644 (file)
index 0000000..0ead012
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+   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 <atalk/tevent.h>
+
+#include "tevent_internal.h"
+#include "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/libatalk/tevent/tevent_immediate.c b/libatalk/tevent/tevent_immediate.c
new file mode 100644 (file)
index 0000000..5e65224
--- /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 <atalk/tevent.h>
+#include "tevent_internal.h"
+#include "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/libatalk/tevent/tevent_internal.h b/libatalk/tevent/tevent_internal.h
new file mode 100644 (file)
index 0000000..7f5fd64
--- /dev/null
@@ -0,0 +1,308 @@
+/* 
+   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_req {
+       /**
+        * @brief What to do on completion
+        *
+        * This is used for the user of an async request, fn is called when
+        * the request completes, either successfully or with an error.
+        */
+       struct {
+               /**
+                * @brief Completion function
+                * Completion function, to be filled by the API user
+                */
+               tevent_req_fn fn;
+               /**
+                * @brief Private data for the completion function
+                */
+               void *private_data;
+       } async;
+
+       /**
+        * @brief Private state pointer for the actual implementation
+        *
+        * The implementation doing the work for the async request needs to
+        * keep around current data like for example a fd event. The user of
+        * an async request should not touch this.
+        */
+       void *data;
+
+       /**
+        * @brief A function to overwrite the default print function
+        *
+        * The implementation doing the work may want to implement a
+        * custom function to print the text representation of the async
+        * request.
+        */
+       tevent_req_print_fn private_print;
+
+       /**
+        * @brief A function to cancel the request
+        *
+        * The implementation might want to set a function
+        * that is called when the tevent_req_cancel() function
+        * was called.
+        */
+       tevent_req_cancel_fn private_cancel;
+
+       /**
+        * @brief Internal state of the request
+        *
+        * Callers should only access this via functions and never directly.
+        */
+       struct {
+               /**
+                * @brief The talloc type of the data pointer
+                *
+                * This is filled by the tevent_req_create() macro.
+                *
+                * This for debugging only.
+                */
+               const char *private_type;
+
+               /**
+                * @brief The location where the request was created
+                *
+                * This uses the __location__ macro via the tevent_req_create()
+                * macro.
+                *
+                * This for debugging only.
+                */
+               const char *create_location;
+
+               /**
+                * @brief The location where the request was finished
+                *
+                * This uses the __location__ macro via the tevent_req_done(),
+                * tevent_req_error() or tevent_req_nomem() macro.
+                *
+                * This for debugging only.
+                */
+               const char *finish_location;
+
+               /**
+                * @brief The location where the request was canceled
+                *
+                * This uses the __location__ macro via the
+                * tevent_req_cancel() macro.
+                *
+                * This for debugging only.
+                */
+               const char *cancel_location;
+
+               /**
+                * @brief The external state - will be queried by the caller
+                *
+                * While the async request is being processed, state will remain in
+                * TEVENT_REQ_IN_PROGRESS. A request is finished if
+                * req->state>=TEVENT_REQ_DONE.
+                */
+               enum tevent_req_state state;
+
+               /**
+                * @brief status code when finished
+                *
+                * This status can be queried in the async completion function. It
+                * will be set to 0 when everything went fine.
+                */
+               uint64_t error;
+
+               /**
+                * @brief the immediate event used by tevent_req_post
+                *
+                */
+               struct tevent_immediate *trigger;
+
+               /**
+                * @brief the timer event if tevent_req_set_endtime was used
+                *
+                */
+               struct tevent_timer *timer;
+       } internal;
+};
+
+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_ATTRIBUTE(3,0);
+       void *context;
+};
+
+void tevent_debug(struct tevent_context *ev, enum tevent_debug_level level,
+                 const char *fmt, ...) PRINTF_ATTRIBUTE(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;
+
+       /* info about the nesting status */
+       struct {
+               bool allowed;
+               uint32_t level;
+               tevent_nesting_hook hook_fn;
+               void *hook_private;
+       } nesting;
+};
+
+
+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
diff --git a/libatalk/tevent/tevent_liboop.loT b/libatalk/tevent/tevent_liboop.loT
new file mode 100644 (file)
index 0000000..75fdac3
--- /dev/null
@@ -0,0 +1,7 @@
+# tevent_liboop.lo - a libtool object file
+# Generated by ltmain.sh - GNU libtool 1.5.26 Debian 1.5.26-1ubuntu1 (1.1220.2.493 2008/02/01 16:58:18)
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
diff --git a/libatalk/tevent/tevent_queue.c b/libatalk/tevent/tevent_queue.c
new file mode 100644 (file)
index 0000000..9a59100
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+   Unix SMB/CIFS implementation.
+   Infrastructure for async requests
+   Copyright (C) Volker Lendecke 2008
+   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 <atalk/tevent.h>
+#include "tevent_internal.h"
+#include "tevent_util.h"
+
+struct tevent_queue_entry {
+       struct tevent_queue_entry *prev, *next;
+       struct tevent_queue *queue;
+
+       bool triggered;
+
+       struct tevent_req *req;
+       struct tevent_context *ev;
+
+       tevent_queue_trigger_fn_t trigger;
+       void *private_data;
+};
+
+struct tevent_queue {
+       const char *name;
+       const char *location;
+
+       bool running;
+       struct tevent_immediate *immediate;
+
+       size_t length;
+       struct tevent_queue_entry *list;
+};
+
+static void tevent_queue_immediate_trigger(struct tevent_context *ev,
+                                          struct tevent_immediate *im,
+                                          void *private_data);
+
+static int tevent_queue_entry_destructor(struct tevent_queue_entry *e)
+{
+       struct tevent_queue *q = e->queue;
+
+       if (!q) {
+               return 0;
+       }
+
+       DLIST_REMOVE(q->list, e);
+       q->length--;
+
+       if (!q->running) {
+               return 0;
+       }
+
+       if (!q->list) {
+               return 0;
+       }
+
+       if (q->list->triggered) {
+               return 0;
+       }
+
+       tevent_schedule_immediate(q->immediate,
+                                 q->list->ev,
+                                 tevent_queue_immediate_trigger,
+                                 q);
+
+       return 0;
+}
+
+static int tevent_queue_destructor(struct tevent_queue *q)
+{
+       q->running = false;
+
+       while (q->list) {
+               struct tevent_queue_entry *e = q->list;
+               talloc_free(e);
+       }
+
+       return 0;
+}
+
+struct tevent_queue *_tevent_queue_create(TALLOC_CTX *mem_ctx,
+                                         const char *name,
+                                         const char *location)
+{
+       struct tevent_queue *queue;
+
+       queue = talloc_zero(mem_ctx, struct tevent_queue);
+       if (!queue) {
+               return NULL;
+       }
+
+       queue->name = talloc_strdup(queue, name);
+       if (!queue->name) {
+               talloc_free(queue);
+               return NULL;
+       }
+       queue->immediate = tevent_create_immediate(queue);
+       if (!queue->immediate) {
+               talloc_free(queue);
+               return NULL;
+       }
+
+       queue->location = location;
+
+       /* queue is running by default */
+       queue->running = true;
+
+       talloc_set_destructor(queue, tevent_queue_destructor);
+       return queue;
+}
+
+static void tevent_queue_immediate_trigger(struct tevent_context *ev,
+                                          struct tevent_immediate *im,
+                                          void *private_data)
+{
+       struct tevent_queue *q = talloc_get_type(private_data,
+                                 struct tevent_queue);
+
+       if (!q->running) {
+               return;
+       }
+
+       q->list->triggered = true;
+       q->list->trigger(q->list->req, q->list->private_data);
+}
+
+bool tevent_queue_add(struct tevent_queue *queue,
+                     struct tevent_context *ev,
+                     struct tevent_req *req,
+                     tevent_queue_trigger_fn_t trigger,
+                     void *private_data)
+{
+       struct tevent_queue_entry *e;
+
+       e = talloc_zero(req, struct tevent_queue_entry);
+       if (e == NULL) {
+               return false;
+       }
+
+       e->queue = queue;
+       e->req = req;
+       e->ev = ev;
+       e->trigger = trigger;
+       e->private_data = private_data;
+
+       DLIST_ADD_END(queue->list, e, struct tevent_queue_entry *);
+       queue->length++;
+       talloc_set_destructor(e, tevent_queue_entry_destructor);
+
+       if (!queue->running) {
+               return true;
+       }
+
+       if (queue->list->triggered) {
+               return true;
+       }
+
+       tevent_schedule_immediate(queue->immediate,
+                                 queue->list->ev,
+                                 tevent_queue_immediate_trigger,
+                                 queue);
+
+       return true;
+}
+
+void tevent_queue_start(struct tevent_queue *queue)
+{
+       if (queue->running) {
+               /* already started */
+               return;
+       }
+
+       queue->running = true;
+
+       if (!queue->list) {
+               return;
+       }
+
+       if (queue->list->triggered) {
+               return;
+       }
+
+       tevent_schedule_immediate(queue->immediate,
+                                 queue->list->ev,
+                                 tevent_queue_immediate_trigger,
+                                 queue);
+}
+
+void tevent_queue_stop(struct tevent_queue *queue)
+{
+       queue->running = false;
+}
+
+size_t tevent_queue_length(struct tevent_queue *queue)
+{
+       return queue->length;
+}
diff --git a/libatalk/tevent/tevent_req.c b/libatalk/tevent/tevent_req.c
new file mode 100644 (file)
index 0000000..f87a433
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+   Unix SMB/CIFS implementation.
+   Infrastructure for async requests
+   Copyright (C) Volker Lendecke 2008
+   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 <atalk/tevent.h>
+
+#include "tevent_internal.h"
+#include "tevent_util.h"
+
+/**
+ * @brief The default print function for creating debug messages
+ * @param[in] req      The request to be printed
+ * @param[in] mem_ctx  The memory context for the result
+ * @retval             Text representation of req
+ *
+ * The function should not be used by users of the asynx API,
+ * but custom print function can use it and append custom text
+ * to the string.
+ */
+
+char *tevent_req_default_print(struct tevent_req *req, TALLOC_CTX *mem_ctx)
+{
+       return talloc_asprintf(mem_ctx,
+                              "tevent_req[%p/%s]: state[%d] error[%lld (0x%llX)] "
+                              " state[%s (%p)] timer[%p]",
+                              req, req->internal.create_location,
+                              req->internal.state,
+                              (unsigned long long)req->internal.error,
+                              (unsigned long long)req->internal.error,
+                              talloc_get_name(req->data),
+                              req->data,
+                              req->internal.timer
+                              );
+}
+
+/**
+ * @brief Print an tevent_req structure in debug messages
+ * @param[in] mem_ctx  The memory context for the result
+ * @param[in] req      The request to be printed
+ * @retval             Text representation of req
+ *
+ * This function should be used by callers of the async API
+ */
+
+char *tevent_req_print(TALLOC_CTX *mem_ctx, struct tevent_req *req)
+{
+       if (!req->private_print) {
+               return tevent_req_default_print(req, mem_ctx);
+       }
+
+       return req->private_print(req, mem_ctx);
+}
+
+/**
+ * @brief Create an async request
+ * @param[in] mem_ctx  The memory context for the result
+ * @param[in] ev       The event context this async request will be driven by
+ * @retval             A new async request
+ *
+ * The new async request will be initialized in state ASYNC_REQ_IN_PROGRESS
+ */
+
+struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx,
+                                   void *pdata,
+                                   size_t data_size,
+                                   const char *type,
+                                   const char *location)
+{
+       struct tevent_req *req;
+       void **ppdata = (void **)pdata;
+       void *data;
+
+       req = talloc_zero(mem_ctx, struct tevent_req);
+       if (req == NULL) {
+               return NULL;
+       }
+       req->internal.private_type      = type;
+       req->internal.create_location   = location;
+       req->internal.finish_location   = NULL;
+       req->internal.state             = TEVENT_REQ_IN_PROGRESS;
+       req->internal.trigger           = tevent_create_immediate(req);
+       if (!req->internal.trigger) {
+               talloc_free(req);
+               return NULL;
+       }
+
+       data = talloc_zero_size(req, data_size);
+       if (data == NULL) {
+               talloc_free(req);
+               return NULL;
+       }
+       talloc_set_name_const(data, type);
+
+       req->data = data;
+
+       *ppdata = data;
+       return req;
+}
+
+void _tevent_req_notify_callback(struct tevent_req *req, const char *location)
+{
+       req->internal.finish_location = location;
+       if (req->async.fn != NULL) {
+               req->async.fn(req);
+       }
+}
+
+static void tevent_req_finish(struct tevent_req *req,
+                             enum tevent_req_state state,
+                             const char *location)
+{
+       req->internal.state = state;
+       _tevent_req_notify_callback(req, location);
+}
+
+/**
+ * @brief An async request has successfully finished
+ * @param[in] req      The finished request
+ *
+ * tevent_req_done is to be used by implementors of async requests. When a
+ * request is successfully finished, this function calls the user's completion
+ * function.
+ */
+
+void _tevent_req_done(struct tevent_req *req,
+                     const char *location)
+{
+       tevent_req_finish(req, TEVENT_REQ_DONE, location);
+}
+
+/**
+ * @brief An async request has seen an error
+ * @param[in] req      The request with an error
+ * @param[in] error    The error code
+ *
+ * tevent_req_done is to be used by implementors of async requests. When a
+ * request can not successfully completed, the implementation should call this
+ * function with the appropriate status code.
+ *
+ * If error is 0 the function returns false and does nothing more.
+ *
+ * Call pattern would be
+ * \code
+ * int error = first_function();
+ * if (tevent_req_error(req, error)) {
+ *     return;
+ * }
+ *
+ * error = second_function();
+ * if (tevent_req_error(req, error)) {
+ *     return;
+ * }
+ *
+ * tevent_req_done(req);
+ * return;
+ * \endcode
+ */
+
+bool _tevent_req_error(struct tevent_req *req,
+                      uint64_t error,
+                      const char *location)
+{
+       if (error == 0) {
+               return false;
+       }
+
+       req->internal.error = error;
+       tevent_req_finish(req, TEVENT_REQ_USER_ERROR, location);
+       return true;
+}
+
+/**
+ * @brief Helper function for nomem check
+ * @param[in] p                The pointer to be checked
+ * @param[in] req      The request being processed
+ *
+ * Convenience helper to easily check alloc failure within a callback
+ * implementing the next step of an async request.
+ *
+ * Call pattern would be
+ * \code
+ * p = talloc(mem_ctx, bla);
+ * if (tevent_req_nomem(p, req)) {
+ *     return;
+ * }
+ * \endcode
+ */
+
+bool _tevent_req_nomem(const void *p,
+                      struct tevent_req *req,
+                      const char *location)
+{
+       if (p != NULL) {
+               return false;
+       }
+       tevent_req_finish(req, TEVENT_REQ_NO_MEMORY, location);
+       return true;
+}
+
+/**
+ * @brief Immediate event callback
+ * @param[in] ev       Event context
+ * @param[in] im       The immediate event
+ * @param[in] priv     The async request to be finished
+ */
+static void tevent_req_trigger(struct tevent_context *ev,
+                              struct tevent_immediate *im,
+                              void *private_data)
+{
+       struct tevent_req *req = talloc_get_type(private_data,
+                                struct tevent_req);
+
+       tevent_req_finish(req, req->internal.state,
+                         req->internal.finish_location);
+}
+
+/**
+ * @brief Finish a request before the caller had the change to set the callback
+ * @param[in] req      The finished request
+ * @param[in] ev       The tevent_context for the timed event
+ * @retval             req will be returned
+ *
+ * An implementation of an async request might find that it can either finish
+ * the request without waiting for an external event, or it can't even start
+ * the engine. To present the illusion of a callback to the user of the API,
+ * the implementation can call this helper function which triggers an
+ * immediate timed event. This way the caller can use the same calling
+ * conventions, independent of whether the request was actually deferred.
+ */
+
+struct tevent_req *tevent_req_post(struct tevent_req *req,
+                                  struct tevent_context *ev)
+{
+       tevent_schedule_immediate(req->internal.trigger,
+                                 ev, tevent_req_trigger, req);
+       return req;
+}
+
+/**
+ * @brief This function destroys the attached private data
+ * @param[in] req      The request to poll
+ * @retval             The boolean form of "is in progress".
+ *
+ * This function can be used to ask if the given request
+ * is still in progress.
+ *
+ * This function is typically used by sync wrapper functions.
+ */
+bool tevent_req_is_in_progress(struct tevent_req *req)
+{
+       if (req->internal.state == TEVENT_REQ_IN_PROGRESS) {
+               return true;
+       }
+
+       return false;
+}
+
+/**
+ * @brief This function destroys the attached private data
+ * @param[in] req      The finished request
+ *
+ * This function can be called as last action of a _recv()
+ * function, it destroys the data attached to the tevent_req.
+ */
+void tevent_req_received(struct tevent_req *req)
+{
+       TALLOC_FREE(req->data);
+       req->private_print = NULL;
+
+       TALLOC_FREE(req->internal.trigger);
+       TALLOC_FREE(req->internal.timer);
+
+       req->internal.state = TEVENT_REQ_RECEIVED;
+}
+
+/**
+ * @brief This function destroys the attached private data
+ * @param[in] req      The request to poll
+ * @param[in] ev       The tevent_context to be used
+ * @retval             If a critical error has happened in the
+ *                     tevent loop layer false is returned.
+ *                     Otherwise true is returned.
+ *                     This is not the return value of the given request!
+ *
+ * This function can be used to actively poll for the
+ * given request to finish.
+ *
+ * Note: this should only be used if the given tevent context
+ *       was created by the caller, to avoid event loop nesting.
+ *
+ * This function is typically used by sync wrapper functions.
+ */
+bool tevent_req_poll(struct tevent_req *req,
+                    struct tevent_context *ev)
+{
+       while (tevent_req_is_in_progress(req)) {
+               int ret;
+
+               ret = tevent_loop_once(ev);
+               if (ret != 0) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+bool tevent_req_is_error(struct tevent_req *req, enum tevent_req_state *state,
+                       uint64_t *error)
+{
+       if (req->internal.state == TEVENT_REQ_DONE) {
+               return false;
+       }
+       if (req->internal.state == TEVENT_REQ_USER_ERROR) {
+               *error = req->internal.error;
+       }
+       *state = req->internal.state;
+       return true;
+}
+
+static void tevent_req_timedout(struct tevent_context *ev,
+                              struct tevent_timer *te,
+                              struct timeval now,
+                              void *private_data)
+{
+       struct tevent_req *req = talloc_get_type(private_data,
+                                struct tevent_req);
+
+       TALLOC_FREE(req->internal.timer);
+
+       tevent_req_finish(req, TEVENT_REQ_TIMED_OUT, __FUNCTION__);
+}
+
+bool tevent_req_set_endtime(struct tevent_req *req,
+                           struct tevent_context *ev,
+                           struct timeval endtime)
+{
+       TALLOC_FREE(req->internal.timer);
+
+       req->internal.timer = tevent_add_timer(ev, req, endtime,
+                                              tevent_req_timedout,
+                                              req);
+       if (tevent_req_nomem(req->internal.timer, req)) {
+               return false;
+       }
+
+       return true;
+}
+
+void tevent_req_set_callback(struct tevent_req *req, tevent_req_fn fn, void *pvt)
+{
+       req->async.fn = fn;
+       req->async.private_data = pvt;
+}
+
+void *_tevent_req_callback_data(struct tevent_req *req)
+{
+       return req->async.private_data;
+}
+
+void *_tevent_req_data(struct tevent_req *req)
+{
+       return req->data;
+}
+
+/**
+ * @brief This function sets a print function for the given request
+ * @param[in] req      The given request
+ * @param[in] fn       A pointer to the print function
+ *
+ * This function can be used to setup a print function for the given request.
+ * This will be triggered if the tevent_req_print() function was
+ * called on the given request.
+ *
+ * Note: this function should only be used for debugging.
+ */
+void tevent_req_set_print_fn(struct tevent_req *req, tevent_req_print_fn fn)
+{
+       req->private_print = fn;
+}
+
+/**
+ * @brief This function sets a cancel function for the given request
+ * @param[in] req      The given request
+ * @param[in] fn       A pointer to the cancel function
+ *
+ * This function can be used to setup a cancel function for the given request.
+ * This will be triggered if the tevent_req_cancel() function was
+ * called on the given request.
+ *
+ */
+void tevent_req_set_cancel_fn(struct tevent_req *req, tevent_req_cancel_fn fn)
+{
+       req->private_cancel = fn;
+}
+
+/**
+ * @brief This function tries to cancel the given request
+ * @param[in] req      The given request
+ * @param[in] location Automaticly filled with the __location__ macro
+ *                     via the tevent_req_cancel() macro. This is for debugging
+ *                     only!
+ * @retval             This function returns true is the request is cancelable.
+ *                     Otherwise false is returned.
+ *
+ * This function can be used to cancel the given request.
+ *
+ * It is only possible to cancel a request when the implementation
+ * has registered a cancel function via the tevent_req_set_cancel_fn().
+ *
+ * Note: Even if the function returns true, the caller need to wait
+ *       for the function to complete normally.
+ *       Only the _recv() function of the given request indicates
+ *       if the request was really canceled.
+ */
+bool _tevent_req_cancel(struct tevent_req *req, const char *location)
+{
+       if (req->private_cancel == NULL) {
+               return false;
+       }
+
+       return req->private_cancel(req);
+}
diff --git a/libatalk/tevent/tevent_select.c b/libatalk/tevent/tevent_select.c
new file mode 100644 (file)
index 0000000..28dbdfd
--- /dev/null
@@ -0,0 +1,245 @@
+/* 
+   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 <atalk/tevent.h>
+
+#include "tevent_util.h"
+#include "tevent_internal.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 (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,
+};
+
+bool tevent_select_init(void)
+{
+       return tevent_register_backend("select", &select_event_ops);
+}
diff --git a/libatalk/tevent/tevent_signal.c b/libatalk/tevent/tevent_signal.c
new file mode 100644 (file)
index 0000000..ad75481
--- /dev/null
@@ -0,0 +1,421 @@
+/* 
+   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 <atalk/tevent.h>
+
+#include "tevent_internal.h"
+#include "tevent_util.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(talloc_autofree_context(), 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/libatalk/tevent/tevent_standard.c b/libatalk/tevent/tevent_standard.c
new file mode 100644 (file)
index 0000000..67b904c
--- /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 <atalk/tevent.h>
+
+#include "tevent_util.h"
+#include "tevent_internal.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->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,
+};
+
+
+bool tevent_standard_init(void)
+{
+       return tevent_register_backend("standard", &std_event_ops);
+}
+
diff --git a/libatalk/tevent/tevent_timed.c b/libatalk/tevent/tevent_timed.c
new file mode 100644 (file)
index 0000000..f65b460
--- /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 <atalk/tevent.h>
+
+#include "tevent_internal.h"
+#include "tevent_util.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/libatalk/tevent/tevent_util.c b/libatalk/tevent/tevent_util.c
new file mode 100644 (file)
index 0000000..2906319
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+   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 <atalk/tevent.h>
+#include <atalk/talloc.h>
+
+#include "tevent_internal.h"
+#include "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/libatalk/tevent/tevent_util.h b/libatalk/tevent/tevent_util.h
new file mode 100644 (file)
index 0000000..829cbc2
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Andrew Tridgell 1998-2005
+   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
+
+
+/* hook into the front of the list */
+#define DLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+               (list) = (p); \
+               (p)->next = (p)->prev = NULL; \
+       } else { \
+               (list)->prev = (p); \
+               (p)->next = (list); \
+               (p)->prev = NULL; \
+               (list) = (p); \
+       }\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define DLIST_REMOVE(list, p) \
+do { \
+       if ((p) == (list)) { \
+               (list) = (p)->next; \
+               if (list) (list)->prev = NULL; \
+       } 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)
+
+/* promote an element to the top of the list */
+#define DLIST_PROMOTE(list, p) \
+do { \
+          DLIST_REMOVE(list, p); \
+          DLIST_ADD(list, p); \
+} while (0)
+
+/* hook into the end of the list - needs a tmp pointer */
+#define DLIST_ADD_END(list, p, type) \
+do { \
+               if (!(list)) { \
+                       (list) = (p); \
+                       (p)->next = (p)->prev = NULL; \
+               } else { \
+                       type tmp; \
+                       for (tmp = (list); tmp->next; tmp = tmp->next) ; \
+                       tmp->next = (p); \
+                       (p)->next = NULL; \
+                       (p)->prev = tmp; \
+               } \
+} while (0)
+
+/* 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; \
+       }\
+} while (0)
+
+/* demote an element to the end of the list, needs a tmp pointer */
+#define DLIST_DEMOTE(list, p, tmp) \
+do { \
+               DLIST_REMOVE(list, p); \
+               DLIST_ADD_END(list, p, tmp); \
+} while (0)
+
+/* concatenate two lists - putting all elements of the 2nd list at the
+   end of the first list */
+#define DLIST_CONCATENATE(list1, list2, type) \
+do { \
+               if (!(list1)) { \
+                       (list1) = (list2); \
+               } else { \
+                       type tmp; \
+                       for (tmp = (list1); tmp->next; tmp = tmp->next) ; \
+                       tmp->next = (list2); \
+                       if (list2) { \
+                               (list2)->prev = tmp;    \
+                       } \
+               } \
+} 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
diff --git a/libatalk/tevent/tevent_wakeup.c b/libatalk/tevent/tevent_wakeup.c
new file mode 100644 (file)
index 0000000..f3a94de
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+   Unix SMB/CIFS implementation.
+   Infrastructure for async requests
+   Copyright (C) Volker Lendecke 2008
+   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 <atalk/tevent.h>
+
+#include "tevent_internal.h"
+#include "tevent_util.h"
+
+struct tevent_wakeup_state {
+       struct timeval wakeup_time;
+};
+
+struct tevent_req *tevent_wakeup_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct timeval wakeup_time)
+{
+       struct tevent_req *req;
+       struct tevent_wakeup_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct tevent_wakeup_state);
+       if (!req) {
+               return NULL;
+       }
+       state->wakeup_time = wakeup_time;
+
+       if (!tevent_req_set_endtime(req, ev, wakeup_time)) {
+               goto post;
+       }
+
+       return req;
+post:
+       return tevent_req_post(req, ev);
+}
+
+bool tevent_wakeup_recv(struct tevent_req *req)
+{
+       enum tevent_req_state state;
+       uint64_t error;
+
+       if (tevent_req_is_error(req, &state, &error)) {
+               if (state == TEVENT_REQ_TIMED_OUT) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+