From 266060b44e6d836eaac252f80a95f3ecd8506d0b Mon Sep 17 00:00:00 2001 From: des Date: Mon, 3 Apr 2006 11:59:08 +0000 Subject: [PATCH] Update from libevent CVS trunk. git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@98 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- varnish-cache/contrib/libevent/Makefile.am | 9 +- varnish-cache/contrib/libevent/README | 7 + .../contrib/libevent/WIN32-Code/win32.c | 27 +- .../contrib/libevent/WIN32-Prj/libevent.dsp | 2 +- varnish-cache/contrib/libevent/buffer.c | 87 +- varnish-cache/contrib/libevent/configure.in | 5 +- varnish-cache/contrib/libevent/devpoll.c | 22 +- varnish-cache/contrib/libevent/epoll.c | 30 +- varnish-cache/contrib/libevent/evbuffer.c | 32 +- varnish-cache/contrib/libevent/event.3 | 175 +- varnish-cache/contrib/libevent/event.c | 82 +- varnish-cache/contrib/libevent/event.h | 124 +- .../contrib/libevent/event_rpcgen.py | 1378 +++++++++++++++ .../contrib/libevent/event_tagging.c | 360 ++++ varnish-cache/contrib/libevent/http.c | 1492 +++++++++++++++++ varnish-cache/contrib/libevent/http.h | 125 ++ varnish-cache/contrib/libevent/kqueue.c | 23 +- varnish-cache/contrib/libevent/log.h | 4 +- varnish-cache/contrib/libevent/poll.c | 30 +- varnish-cache/contrib/libevent/rtsig.c | 1108 +++++++++--- varnish-cache/contrib/libevent/select.c | 30 +- varnish-cache/contrib/libevent/signal.c | 26 +- varnish-cache/contrib/libevent/strlcpy.c | 74 + .../contrib/libevent/test/Makefile.am | 22 +- varnish-cache/contrib/libevent/test/regress.c | 278 ++- varnish-cache/contrib/libevent/test/regress.h | 41 + .../contrib/libevent/test/regress.rpc | 17 + .../contrib/libevent/test/regress_http.c | 439 +++++ .../contrib/libevent/test/test-weof.c | 2 +- varnish-cache/contrib/libevent/test/test.sh | 22 +- 30 files changed, 5613 insertions(+), 460 deletions(-) create mode 100755 varnish-cache/contrib/libevent/event_rpcgen.py create mode 100644 varnish-cache/contrib/libevent/event_tagging.c create mode 100644 varnish-cache/contrib/libevent/http.c create mode 100644 varnish-cache/contrib/libevent/http.h create mode 100644 varnish-cache/contrib/libevent/strlcpy.c create mode 100644 varnish-cache/contrib/libevent/test/regress.h create mode 100644 varnish-cache/contrib/libevent/test/regress.rpc create mode 100644 varnish-cache/contrib/libevent/test/regress_http.c diff --git a/varnish-cache/contrib/libevent/Makefile.am b/varnish-cache/contrib/libevent/Makefile.am index e08bc03f..c9ec0481 100644 --- a/varnish-cache/contrib/libevent/Makefile.am +++ b/varnish-cache/contrib/libevent/Makefile.am @@ -2,9 +2,11 @@ AUTOMAKE_OPTIONS = foreign no-dependencies SUBDIRS = . sample test +bin_SCRIPTS = event_rpcgen.py + EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h event.3 \ kqueue.c epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c \ - devpoll.c \ + devpoll.c event_rpcgen.py \ sample/Makefile.am sample/Makefile.in sample/event-test.c \ sample/signal-test.c sample/time-test.c \ test/Makefile.am test/Makefile.in test/bench.c test/regress.c \ @@ -21,9 +23,10 @@ EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h event.3 \ lib_LTLIBRARIES = libevent.la -libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c +libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c event_tagging.c \ + http.c http.h libevent_la_LIBADD = @LTLIBOBJS@ -libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:2:0 +libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:3:0 include_HEADERS = event.h diff --git a/varnish-cache/contrib/libevent/README b/varnish-cache/contrib/libevent/README index b978fe98..bd4a84ca 100644 --- a/varnish-cache/contrib/libevent/README +++ b/varnish-cache/contrib/libevent/README @@ -12,6 +12,10 @@ $ make verify Before, reporting any problems, please run the regression tests. +To enable the low-level tracing build the library as: + +CFLAGS=-DUSE_DEBUG ./configure [...] + Acknowledgements: ----------------- @@ -24,5 +28,8 @@ fixing bugs: Mike Davis William Ahern Alexander von Gernler + Artur Grabowski + Stas Bekman + Tassilo von Parseval If I have forgotten your name, please contact me. diff --git a/varnish-cache/contrib/libevent/WIN32-Code/win32.c b/varnish-cache/contrib/libevent/WIN32-Code/win32.c index f4cc2d8a..1978bcae 100644 --- a/varnish-cache/contrib/libevent/WIN32-Code/win32.c +++ b/varnish-cache/contrib/libevent/WIN32-Code/win32.c @@ -81,6 +81,7 @@ int win32_insert (void *, struct event *); int win32_del (void *, struct event *); int win32_recalc (struct event_base *base, void *, int); int win32_dispatch (struct event_base *base, void *, struct timeval *); +void win32_dealloc (void *); struct eventop win32ops = { "win32", @@ -88,7 +89,8 @@ struct eventop win32ops = { win32_insert, win32_del, win32_recalc, - win32_dispatch + win32_dispatch, + win32_dealloc }; #define FD_SET_ALLOC_SIZE(n) ((sizeof(struct win_fd_set) + ((n)-1)*sizeof(SOCKET))) @@ -135,6 +137,8 @@ do_fd_set(struct win32op *op, SOCKET s, int read) if (set->fd_count == op->fd_setsz) { if (realloc_fd_sets(op, op->fd_setsz*2)) return (-1); + /* set pointer will have changed and needs reiniting! */ + set = read ? op->readset_in : op->writeset_in; } set->fd_array[set->fd_count] = s; return (set->fd_count++); @@ -363,6 +367,27 @@ win32_dispatch(struct event_base *base, struct win32op *win32op, return (0); } +void +win32_dealloc(void *arg) +{ + struct win32op *win32op = arg; + + if (win32op->readset_in) + free(win32op->readset_in); + if (win32op->writeset_in) + free(win32op->writeset_in); + if (win32op->readset_out) + free(win32op->readset_out); + if (win32op->writeset_out) + free(win32op->writeset_out); + if (win32op->exset_out) + free(win32op->exset_out); + if (win32op->events) + free(win32op->events); + + memset(win32op, 0, sizeof(win32op)); + free(win32op); +} static int signal_handler(int sig) diff --git a/varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp b/varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp index c7bfd60e..e9848f7f 100644 --- a/varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp +++ b/varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp @@ -85,7 +85,7 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=..\err.c +SOURCE=..\log.c # End Source File # Begin Source File diff --git a/varnish-cache/contrib/libevent/buffer.c b/varnish-cache/contrib/libevent/buffer.c index 1dc16ae3..e641e91f 100644 --- a/varnish-cache/contrib/libevent/buffer.c +++ b/varnish-cache/contrib/libevent/buffer.c @@ -117,45 +117,55 @@ evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) } res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off); - if (res == 0) + if (res == 0) { + /* We drain the input buffer on success */ evbuffer_drain(inbuf, inbuf->off); + } return (res); } int -evbuffer_add_printf(struct evbuffer *buf, char *fmt, ...) +evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) { - int res = -1; - char *msg; -#ifndef HAVE_VASPRINTF - static char buffer[4096]; -#endif - va_list ap; + char *buffer; + size_t space; + size_t oldoff = buf->off; + int sz; - va_start(ap, fmt); + for (;;) { + buffer = buf->buffer + buf->off; + space = buf->totallen - buf->misalign - buf->off; -#ifdef HAVE_VASPRINTF - if (vasprintf(&msg, fmt, ap) == -1) - goto end; +#ifdef WIN32 + sz = vsnprintf(buffer, space - 1, fmt, ap); + buffer[space - 1] = '\0'; #else -# ifdef WIN32 - _vsnprintf(buffer, sizeof(buffer) - 1, fmt, ap); - buffer[sizeof(buffer)-1] = '\0'; -# else /* ! WIN32 */ - vsnprintf(buffer, sizeof(buffer), fmt, ap); -# endif - msg = buffer; + sz = vsnprintf(buffer, space, fmt, ap); #endif - - res = strlen(msg); - if (evbuffer_add(buf, msg, res) == -1) - res = -1; -#ifdef HAVE_VASPRINTF - free(msg); + if (sz == -1) + return (-1); + if (sz < space) { + buf->off += sz; + if (buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + return (sz); + } + if (evbuffer_expand(buf, sz + 1) == -1) + return (-1); -end: -#endif + } + /* NOTREACHED */ +} + +int +evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) +{ + int res = -1; + va_list ap; + + va_start(ap, fmt); + res = evbuffer_add_vprintf(buf, fmt, ap); va_end(ap); return (res); @@ -184,16 +194,16 @@ evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen) char * evbuffer_readline(struct evbuffer *buffer) { - char *data = EVBUFFER_DATA(buffer); + u_char *data = EVBUFFER_DATA(buffer); size_t len = EVBUFFER_LENGTH(buffer); char *line; - int i; + u_int i; for (i = 0; i < len; i++) { if (data[i] == '\r' || data[i] == '\n') break; } - + if (i == len) return (NULL); @@ -332,8 +342,21 @@ evbuffer_read(struct evbuffer *buf, int fd, int howmuch) #endif #ifdef FIONREAD - if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) + if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) { n = EVBUFFER_MAX_READ; + } else if (n > EVBUFFER_MAX_READ && n > howmuch) { + /* + * It's possible that a lot of data is available for + * reading. We do not want to exhaust resources + * before the reader has a chance to do something + * about it. If the reader does not tell us how much + * data we should read, we artifically limit it. + */ + if (n > buf->totallen << 2) + n = buf->totallen << 2; + if (n < EVBUFFER_MAX_READ) + n = EVBUFFER_MAX_READ; + } #endif if (howmuch < 0 || howmuch > n) howmuch = n; @@ -397,7 +420,7 @@ evbuffer_write(struct evbuffer *buffer, int fd) } u_char * -evbuffer_find(struct evbuffer *buffer, u_char *what, size_t len) +evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len) { size_t remain = buffer->off; u_char *search = buffer->buffer; diff --git a/varnish-cache/contrib/libevent/configure.in b/varnish-cache/contrib/libevent/configure.in index b75d2d66..42e17a73 100644 --- a/varnish-cache/contrib/libevent/configure.in +++ b/varnish-cache/contrib/libevent/configure.in @@ -2,7 +2,7 @@ dnl configure.in for libevent dnl Dug Song AC_INIT(event.c) -AM_INIT_AUTOMAKE(libevent,1.1a) +AM_INIT_AUTOMAKE(libevent,1.2) AM_CONFIG_HEADER(config.h) AM_MAINTAINER_MODE @@ -117,7 +117,8 @@ AC_C_INLINE AC_HEADER_TIME dnl Checks for library functions. -AC_CHECK_FUNCS(gettimeofday vasprintf fcntl) +AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime) +AC_REPLACE_FUNCS(strlcpy) AC_MSG_CHECKING(for F_SETFD in fcntl.h) AC_EGREP_CPP(yes, diff --git a/varnish-cache/contrib/libevent/devpoll.c b/varnish-cache/contrib/libevent/devpoll.c index 68def058..b4cdae27 100644 --- a/varnish-cache/contrib/libevent/devpoll.c +++ b/varnish-cache/contrib/libevent/devpoll.c @@ -76,6 +76,7 @@ int devpoll_add (void *, struct event *); int devpoll_del (void *, struct event *); int devpoll_recalc (struct event_base *, void *, int); int devpoll_dispatch (struct event_base *, void *, struct timeval *); +void devpoll_dealloc (void *); struct eventop devpollops = { "devpoll", @@ -83,7 +84,8 @@ struct eventop devpollops = { devpoll_add, devpoll_del, devpoll_recalc, - devpoll_dispatch + devpoll_dispatch, + devpoll_dealloc }; #define NEVENT 32000 @@ -401,3 +403,21 @@ devpoll_del(void *arg, struct event *ev) return (0); } + +void +devpoll_dealloc(void *arg) +{ + struct devpollop *devpollop = arg; + + if (devpollop->fds) + free(devpollop->fds); + if (devpollop->events) + free(devpollop->events); + if (devpollop->changes) + free(devpollop->changes); + if (devpollop->dpfd >= 0) + close(devpollop->dpfd); + + memset(devpollop, 0, sizeof(struct devpollop)); + free(devpollop); +} diff --git a/varnish-cache/contrib/libevent/epoll.c b/varnish-cache/contrib/libevent/epoll.c index 9f1066d8..19a88a1f 100644 --- a/varnish-cache/contrib/libevent/epoll.c +++ b/varnish-cache/contrib/libevent/epoll.c @@ -76,6 +76,7 @@ int epoll_add (void *, struct event *); int epoll_del (void *, struct event *); int epoll_recalc (struct event_base *, void *, int); int epoll_dispatch (struct event_base *, void *, struct timeval *); +void epoll_dealloc (void *); struct eventop epollops = { "epoll", @@ -83,7 +84,8 @@ struct eventop epollops = { epoll_add, epoll_del, epoll_recalc, - epoll_dispatch + epoll_dispatch, + epoll_dealloc }; #ifdef HAVE_SETFD @@ -109,8 +111,14 @@ epoll_init(void) return (NULL); if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && - rl.rlim_cur != RLIM_INFINITY) - nfiles = rl.rlim_cur; + rl.rlim_cur != RLIM_INFINITY) { + /* + * Solaris is somewhat retarded - it's important to drop + * backwards compatibility when making changes. So, don't + * dare to put rl.rlim_cur here. + */ + nfiles = rl.rlim_cur - 1; + } /* Initalize the kernel queue */ @@ -343,3 +351,19 @@ epoll_del(void *arg, struct event *ev) return (0); } + +void +epoll_dealloc(void *arg) +{ + struct epollop *epollop = arg; + + if (epollop->fds) + free(epollop->fds); + if (epollop->events) + free(epollop->events); + if (epollop->epfd >= 0) + close(epollop->epfd); + + memset(epollop, 0, sizeof(struct epollop)); + free(epollop); +} diff --git a/varnish-cache/contrib/libevent/evbuffer.c b/varnish-cache/contrib/libevent/evbuffer.c index e9f7e43a..7bd8d49a 100644 --- a/varnish-cache/contrib/libevent/evbuffer.c +++ b/varnish-cache/contrib/libevent/evbuffer.c @@ -74,7 +74,7 @@ bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now, void *arg) { struct bufferevent *bufev = arg; /* - * If we are below the watermak then reschedule reading if it's + * If we are below the watermark then reschedule reading if it's * still enabled. */ if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) { @@ -92,13 +92,21 @@ bufferevent_readcb(int fd, short event, void *arg) int res = 0; short what = EVBUFFER_READ; size_t len; + int howmuch = -1; if (event == EV_TIMEOUT) { what |= EVBUFFER_TIMEOUT; goto error; } - res = evbuffer_read(bufev->input, fd, -1); + /* + * If we have a high watermark configured then we don't want to + * read more data than would make us reach the watermark. + */ + if (bufev->wm_read.high != 0) + howmuch = bufev->wm_read.high; + + res = evbuffer_read(bufev->input, fd, howmuch); if (res == -1) { if (errno == EAGAIN || errno == EINTR) goto reschedule; @@ -226,7 +234,12 @@ bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, bufev->cbarg = cbarg; - bufev->enabled = EV_READ | EV_WRITE; + /* + * Set to EV_WRITE so that using bufferevent_write is going to + * trigger a callback. Reading needs to be explicitly enabled + * because otherwise no data will be available. + */ + bufev->enabled = EV_WRITE; return (bufev); } @@ -372,3 +385,16 @@ bufferevent_setwatermark(struct bufferevent *bufev, short events, bufferevent_read_pressure_cb(bufev->input, 0, EVBUFFER_LENGTH(bufev->input), bufev); } + +int +bufferevent_base_set(struct event_base *base, struct bufferevent *bufev) +{ + int res; + + res = event_base_set(base, &bufev->ev_read); + if (res == -1) + return (res); + + res = event_base_set(base, &bufev->ev_write); + return (res); +} diff --git a/varnish-cache/contrib/libevent/event.3 b/varnish-cache/contrib/libevent/event.3 index eb674b42..2e594c4a 100644 --- a/varnish-cache/contrib/libevent/event.3 +++ b/varnish-cache/contrib/libevent/event.3 @@ -34,9 +34,11 @@ .Nm event_dispatch , .Nm event_loop , .Nm event_loopexit , +.Nm event_set , +.Nm event_base_dispatch , .Nm event_base_loop , .Nm event_base_loopexit , -.Nm event_set , +.Nm event_base_set , .Nm event_add , .Nm event_del , .Nm event_once , @@ -46,12 +48,12 @@ .Nm event_priority_set , .Nm evtimer_set , .Nm evtimer_add , -.Nm evtimer_del +.Nm evtimer_del , .Nm evtimer_pending , .Nm evtimer_initialized , .Nm signal_set , .Nm signal_add , -.Nm signal_del +.Nm signal_del , .Nm signal_pending , .Nm signal_initialized , .Nm bufferevent_new , @@ -62,16 +64,20 @@ .Nm bufferevent_enable , .Nm bufferevent_disable , .Nm bufferevent_settimeout , +.Nm bufferevent_base_set , .Nm evbuffer_new , .Nm evbuffer_free , .Nm evbuffer_add , .Nm evbuffer_add_buffer , .Nm evbuffer_add_printf , +.Nm evbuffer_add_vprintf , .Nm evbuffer_drain , .Nm evbuffer_write , .Nm evbuffer_read , .Nm evbuffer_find , -.Nm evbuffer_readline +.Nm evbuffer_readline , +.Nm evhttp_start , +.Nm evhttp_free .Nd execute a function when a specific event occurs .Sh SYNOPSIS .Fd #include @@ -84,13 +90,17 @@ .Fn "event_loop" "int flags" .Ft int .Fn "event_loopexit" "struct timeval *tv" -.Ft int -.Fn "event_base_loop" "struct event_base *" "int flags" -.Ft int -.Fn "event_base_loopexit" "struct event_base *" "struct timeval *tv" .Ft void .Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" .Ft int +.Fn "event_base_dispatch" "struct event_base *base" +.Ft int +.Fn "event_base_loop" "struct event_base *base" "int flags" +.Ft int +.Fn "event_base_loopexit" "struct event_base *base" "struct timeval *tv" +.Ft int +.Fn "event_base_set" "struct event_base *base" "struct event *" +.Ft int .Fn "event_add" "struct event *ev" "struct timeval *tv" .Ft int .Fn "event_del" "struct event *ev" @@ -140,6 +150,8 @@ .Fn "bufferevent_disable" "struct bufferevent *bufev" "short event" .Ft void .Fn "bufferevent_settimeout" "struct bufferevent *bufev" "int timeout_read" "int timeout_write" +.Ft int +.Fn "bufferevent_base_set" "struct event_base *base" "struct bufferevent *bufev" .Ft "struct evbuffer *" .Fn "evbuffer_new" "void" .Ft void @@ -150,6 +162,8 @@ .Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src" .Ft int .Fn "evbuffer_add_printf" "struct evbuffer *buf" "char *fmt" "..." +.Ft int +.Fn "evbuffer_add_vprintf" "struct evbuffer *buf" "const char *fmt" "va_list ap" .Ft void .Fn "evbuffer_drain" "struct evbuffer *buf" "size_t size" .Ft int @@ -160,6 +174,10 @@ .Fn "evbuffer_find" "struct evbuffer *buf" "u_char *data" "size_t size" .Ft "char *" .Fn "evbuffer_readline" "struct evbuffer *buf" +.Ft "struct evhttp *" +.Fn "evhttp_start" "const char *address" "u_short port" +.Ft "void" +.Fn "evhttp_free" "struct evhttp* http" .Ft int .Fa (*event_sigcb)(void) ; .Ft int @@ -193,24 +211,26 @@ sets to indicate that a signal has been received. The application sets .Va event_sigcb -to a callback function. After the signal handler sets +to a callback function. +After the signal handler sets .Va event_gotsig , .Nm event_dispatch -will execute the callback function to process received signals. The -callback returns 1 when no events are registered any more. It can -return -1 to indicate an error to the +will execute the callback function to process received signals. +The callback returns 1 when no events are registered any more. +It can return -1 to indicate an error to the .Nm event library, causing .Fn event_dispatch to terminate with .Va errno set to -.Er EINTR. +.Er EINTR . .Pp The .Nm event_loop function provides an interface for single pass execution of pending -events. The flags +events. +The flags .Va EVLOOP_ONCE and .Va EVLOOP_NONBLOCK @@ -243,14 +263,14 @@ argument indicating the type of event, and a argument given in the .Fa arg argument. -The +The .Fa fd indicates the file descriptor that should be monitored for events. The events can be either .Va EV_READ , .Va EV_WRITE , -or both. -Indicating that an application can read or write from the file descriptor +or both, +indicating that an application can read or write from the file descriptor respectively without blocking. .Pp The function @@ -278,18 +298,18 @@ and .Fn event_del and does not need to be reinitialized unless the function called and/or the argument to it are to be changed. -However, when an +However, when an .Fa ev -structure has been added to libevent using +structure has been added to libevent using .Fn event_add -the structure must persist until the event occurs (assuming -.Fa EV_PERSIST -is not set) or is removed -using -.Fn event_del . +the structure must persist until the event occurs (assuming +.Fa EV_PERSIST +is not set) or is removed +using +.Fn event_del . You may not reuse the same -.Fa ev -structure for multiple monitored descriptors; each descriptor +.Fa ev +structure for multiple monitored descriptors; each descriptor needs its own .Fa ev . .Pp @@ -297,13 +317,15 @@ The function .Fn event_add schedules the execution of the .Fa ev -event when the event specified in +event when the event specified in .Fn event_set occurs or in at least the time specified in the .Fa tv . If .Fa tv -is NULL, no timeout occurs and the function will only be called +is +.Dv NULL , +no timeout occurs and the function will only be called if a matching event occurs on the file descriptor. The event in the .Fa ev @@ -335,7 +357,7 @@ require the caller to prepare an structure. This function supports .Fa EV_TIMEOUT , -.Fa EV_READ +.Fa EV_READ , and .Fa EV_WRITE . .Pp @@ -346,10 +368,10 @@ function can be used to check if the event specified by is pending to run. If .Va EV_TIMEOUT -was specified and +was specified and .Fa tv is not -.Va NULL , +.Dv NULL , the expiration time of the event will be returned in .Fa tv . .Pp @@ -365,10 +387,9 @@ The functions and .Fn evtimer_pending are abbreviations for common situations where only a timeout is required. -The file descriptor passed will be -1, and the event type will be +The file descriptor passed will be \-1, and the event type will be .Va EV_TIMEOUT . .Pp -.Pp The functions .Fn signal_set , .Fn signal_add , @@ -385,23 +406,23 @@ adds .Va EV_PERSIST . .Pp It is possible to disable support for -.Va epoll , kqueue , devpoll, poll +.Va epoll , kqueue , devpoll , poll or .Va select by setting the environment variable -.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NODEVPOLL, EVENT_NOPOLL +.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NODEVPOLL , EVENT_NOPOLL or -.Va EVENT_NOSELECT . +.Va EVENT_NOSELECT , +respectively. By setting the environment variable .Va EVENT_SHOW_METHOD , .Nm libevent displays the kernel notification method that it uses. -.Pp .Sh EVENT PRIORITIES By default .Nm libevent schedules all active events with the same priority. -However, sometime it is desirable to process some events with a higher +However, sometimes it is desirable to process some events with a higher priority than others. For that reason, .Nm libevent @@ -421,7 +442,6 @@ By default, .Nm libevent assigns the middle priority to all events unless their priority is explicitly set. -.Pp .Sh THREAD SAFE EVENTS .Nm Libevent has experimental support for thread-safe events. @@ -429,24 +449,27 @@ When initializing the library via .Fn event_init , an event base is returned. This event base can be used in conjunction with calls to -.Fn event_base_set +.Fn event_base_set , .Fn event_base_dispatch , .Fn event_base_loop , +.Fn event_base_loopexit , and -.Fn event_base_loopexit . -.Fn event_base_set +.Fn bufferevent_base_set . +.Fn event_base_set should be called after preparing an event with -.Fn event_set , +.Fn event_set , as -.Fn event_set +.Fn event_set assigns the provided event to the most recently created event base. -.Pp +.Fn bufferevent_base_set +should be called after preparing a bufferevent with +.Fn bufferevent_new . .Sh BUFFERED EVENTS .Nm libevent provides an abstraction on top of the regular event callbacks. This abstraction is called a .Va "buffered event" . -A buffered event provides input and output buffer that get filled +A buffered event provides input and output buffers that get filled and drained automatically. The user of a buffered event no longer deals directly with the IO, but instead is reading from input and writing to output buffers. @@ -454,21 +477,33 @@ but instead is reading from input and writing to output buffers. A new bufferevent is created by .Fn bufferevent_new . The parameter -.Fa "fd" +.Fa fd specifies the file descriptor from which data is read and written to. -This file descriptor is not allowed to be a +This file descriptor is not allowed to be a .Xr pipe 2 . The next three parameters are callbacks. -The read and write callback have the following form +The read and write callback have the following form: +.Ft void +.Fn "(*cb)" "struct bufferevent *bufev" "void *arg" . +The error callback has the following form: .Ft void -.Fn "(*cb)" "struct bufferevent *bufev" "void *arg" +.Fn "(*cb)" "struct bufferevent *bufev" "short what" "void *arg" . The argument is specified by the fourth parameter .Fa "cbarg" . +A +.Fa bufferevent struct +pointer is returned on success, NULL on error. .Pp -By default the buffered event is read enabled and will try to read -from the file descriptor. -The write callback is executed whenever the output buffer is drained -below the write low watermark which is +Once initialized, the bufferevent structure can be used repeatedly with +bufferevent_enable() and bufferevent_disable(). The flags parameter can +be a combination of +.Va EV_READ +and +.Va EV_WRITE . +When read enabled the bufferevent will try to read from the file +descriptor and call the read callback. The write callback is executed +whenever the output buffer is drained below the write low watermark, +which is .Va 0 by default. .Pp @@ -482,18 +517,43 @@ The function is used to read data from the input buffer. Both functions return the amount of data written or read. .Pp +If multiple bases are in use, bufferevent_base_set() must be called before +enabling the bufferevent for the first time. +.Sh NON-BLOCKING HTTP SUPPORT +.Nm libevent +provides a very thin HTTP layer that can be used both to host an HTTP +server and also to make HTTP requests. +An HTTP server can be created by calling +.Fn evhttp_start . +When the HTTP server is no longer used, it can be freed via +.Fn evhttp_free . +.Pp +To be notified of HTTP requests, a user needs to register callbacks with the +HTTP server. +This can be done by calling +.Fn evhttp_set_cb . +The second argument is the URI for which a callback is being registered. +The corresponding callback will receive an +.Va struct evhttp_request +object that contains all information about the request. +.Pp +This section does not document all the possible function calls, please +check +.Va event.h +for the public interfaces. .Sh RETURN VALUES Upon successful completion .Fn event_add and .Fn event_del return 0. -Otherwise, -1 is returned and the global variable errno is +Otherwise, \-1 is returned and the global variable errno is set to indicate the error. .Sh SEE ALSO -.Xr timeout 9 , +.Xr kqueue 2 , +.Xr poll 2 , .Xr select 2 , -.Xr kqueue 2 +.Xr timeout 9 .Sh HISTORY The .Nm event @@ -508,7 +568,6 @@ Support for real-time signals is due to Taral. The .Nm event library was written by Niels Provos. -.Pp .Sh BUGS This documentation is neither complete nor authoritative. If you are in doubt about the usage of this API then diff --git a/varnish-cache/contrib/libevent/event.c b/varnish-cache/contrib/libevent/event.c index 2a2150ac..8621f6c2 100644 --- a/varnish-cache/contrib/libevent/event.c +++ b/varnish-cache/contrib/libevent/event.c @@ -24,7 +24,9 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifdef HAVE_CONFIG_H #include "config.h" +#endif #ifdef WIN32 #define WIN32_LEAN_AND_MEAN @@ -46,6 +48,7 @@ #include #endif #include +#include #include #include @@ -107,8 +110,8 @@ struct event_list signalqueue; struct event_base *current_base = NULL; /* Handle signals - This is a deprecated interface */ -int (*event_sigcb)(void); /* Signal callback when gotsig is set */ -volatile int event_gotsig; /* Set in signal handler */ +int (*event_sigcb)(void); /* Signal callback when gotsig is set */ +volatile sig_atomic_t event_gotsig; /* Set in signal handler */ /* Prototypes */ static void event_queue_insert(struct event_base *, struct event *, int); @@ -135,6 +138,23 @@ compare(struct event *a, struct event *b) return (0); } +static int +gettime(struct timeval *tp) +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) + return (-1); + tp->tv_sec = ts.tv_sec; + tp->tv_usec = ts.tv_nsec / 1000; +#else + gettimeofday(tp, NULL); +#endif + + return (0); +} + RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare); RB_GENERATE(event_tree, event, ev_timeout_node, compare); @@ -150,7 +170,7 @@ event_init(void) event_sigcb = NULL; event_gotsig = 0; - gettimeofday(¤t_base->event_tv, NULL); + gettime(¤t_base->event_tv); RB_INIT(¤t_base->timetree); TAILQ_INIT(¤t_base->eventqueue); @@ -176,6 +196,33 @@ event_init(void) return (current_base); } +void +event_base_free(struct event_base *base) +{ + int i; + + if (base == NULL && current_base) + base = current_base; + if (base == current_base) + current_base = NULL; + + assert(base); + assert(TAILQ_EMPTY(&base->eventqueue)); + for (i=0; i < base->nactivequeues; ++i) + assert(TAILQ_EMPTY(base->activequeues[i])); + + assert(RB_EMPTY(&base->timetree)); + + for (i = 0; i < base->nactivequeues; ++i) + free(base->activequeues[i]); + free(base->activequeues); + + if (base->evsel->dealloc != NULL) + base->evsel->dealloc(base->evbase); + + free(base); +} + int event_priority_init(int npriorities) { @@ -313,12 +360,12 @@ event_base_loop(struct event_base *base, int flags) struct timeval tv; int res, done; - /* Calculate the initial events that we are waiting for */ - if (evsel->recalc(base, evbase, 0) == -1) - return (-1); - done = 0; while (!done) { + /* Calculate the initial events that we are waiting for */ + if (evsel->recalc(base, evbase, 0) == -1) + return (-1); + /* Terminate the loop if we have been asked to */ if (base->event_gotterm) { base->event_gotterm = 0; @@ -338,7 +385,7 @@ event_base_loop(struct event_base *base, int flags) } /* Check if time is running backwards */ - gettimeofday(&tv, NULL); + gettime(&tv); if (timercmp(&tv, &base->event_tv, <)) { struct timeval off; event_debug(("%s: time is running backwards, corrected", @@ -372,9 +419,6 @@ event_base_loop(struct event_base *base, int flags) done = 1; } else if (flags & EVLOOP_NONBLOCK) done = 1; - - if (evsel->recalc(base, evbase, 0) == -1) - return (-1); } event_debug(("%s: asked to terminate loop.", __func__)); @@ -499,6 +543,7 @@ event_priority_set(struct event *ev, int pri) int event_pending(struct event *ev, short event, struct timeval *tv) { + struct timeval now, res; int flags = 0; if (ev->ev_flags & EVLIST_INSERTED) @@ -513,8 +558,13 @@ event_pending(struct event *ev, short event, struct timeval *tv) event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL); /* See if there is a timeout that we should report */ - if (tv != NULL && (flags & event & EV_TIMEOUT)) - *tv = ev->ev_timeout; + if (tv != NULL && (flags & event & EV_TIMEOUT)) { + gettime(&now); + timersub(&ev->ev_timeout, &now, &res); + /* correctly remap to real time */ + gettimeofday(&now, NULL); + timeradd(&now, &res, tv); + } return (flags & event); } @@ -558,7 +608,7 @@ event_add(struct event *ev, struct timeval *tv) event_queue_remove(base, ev, EVLIST_ACTIVE); } - gettimeofday(&now, NULL); + gettime(&now); timeradd(&now, tv, &ev->ev_timeout); event_debug(( @@ -654,7 +704,7 @@ timeout_next(struct event_base *base, struct timeval *tv) return (0); } - if (gettimeofday(&now, NULL) == -1) + if (gettime(&now) == -1) return (-1); if (timercmp(&ev->ev_timeout, &now, <=)) { @@ -690,7 +740,7 @@ timeout_process(struct event_base *base) struct timeval now; struct event *ev, *next; - gettimeofday(&now, NULL); + gettime(&now); for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) { if (timercmp(&ev->ev_timeout, &now, >)) diff --git a/varnish-cache/contrib/libevent/event.h b/varnish-cache/contrib/libevent/event.h index 857dd376..4c1b38f0 100644 --- a/varnish-cache/contrib/libevent/event.h +++ b/varnish-cache/contrib/libevent/event.h @@ -31,6 +31,8 @@ extern "C" { #endif +#include + #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include @@ -101,11 +103,25 @@ struct event { #define EVENT_SIGNAL(ev) (int)ev->ev_fd #define EVENT_FD(ev) (int)ev->ev_fd +/* + * Key-Value pairs. Can be used for HTTP headers but also for + * query argument parsing. + */ +struct evkeyval { + TAILQ_ENTRY(evkeyval) next; + + char *key; + char *value; +}; + #ifdef _EVENT_DEFINED_TQENTRY #undef TAILQ_ENTRY +struct event_list; +struct evkeyvalq; #undef _EVENT_DEFINED_TQENTRY #else TAILQ_HEAD (event_list, event); +TAILQ_HEAD (evkeyvalq, evkeyval); #endif /* _EVENT_DEFINED_TQENTRY */ #ifdef _EVENT_DEFINED_RBENTRY #undef RB_ENTRY @@ -119,6 +135,7 @@ struct eventop { int (*del)(void *, struct event *); int (*recalc)(struct event_base *, void *, int); int (*dispatch)(struct event_base *, void *, struct timeval *); + void (*dealloc)(void *); }; #define TIMEOUT_DEFAULT {5, 0} @@ -126,6 +143,7 @@ struct eventop { void *event_init(void); int event_dispatch(void); int event_base_dispatch(struct event_base *); +void event_base_free(struct event_base *); #define _EVENT_LOG_DEBUG 0 #define _EVENT_LOG_MSG 1 @@ -263,13 +281,115 @@ int evbuffer_add(struct evbuffer *, void *, size_t); int evbuffer_remove(struct evbuffer *, void *, size_t); char *evbuffer_readline(struct evbuffer *); int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *); -int evbuffer_add_printf(struct evbuffer *, char *fmt, ...); +int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...); +int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap); void evbuffer_drain(struct evbuffer *, size_t); int evbuffer_write(struct evbuffer *, int); int evbuffer_read(struct evbuffer *, int, int); -u_char *evbuffer_find(struct evbuffer *, u_char *, size_t); +u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t); void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *); +/* + * Marshaling tagged data - We assume that all tags are inserted in their + * numeric order - so that unknown tags will always be higher than the + * known ones - and we can just ignore the end of an event buffer. + */ + +void evtag_init(void); + +void evtag_marshal(struct evbuffer *evbuf, u_int8_t tag, const void *data, + u_int16_t len); + +void encode_int(struct evbuffer *evbuf, u_int32_t number); + +void evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag, + u_int32_t integer); + +void evtag_marshal_string(struct evbuffer *buf, u_int8_t tag, + const char *string); + +void evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag, + struct timeval *tv); + +void evtag_test(void); + +int evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag, + struct evbuffer *dst); +int evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag); +int evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength); +int evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength); +int evtag_consume(struct evbuffer *evbuf); + +int evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag, + u_int32_t *pinteger); + +int evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data, + size_t len); + +int evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag, + char **pstring); + +int evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag, + struct timeval *ptv); + +/* + * Basic support for HTTP serving. + * + * As libevent is a library for dealing with event notification and most + * interesting applications are networked today, I have often found the + * need to write HTTP code. The following prototypes and definitions provide + * an application with a minimal interface for making HTTP requests and for + * creating a very simple HTTP server. + */ + +/* Response codes */ +#define HTTP_OK 200 +#define HTTP_MOVEPERM 301 +#define HTTP_MOVETEMP 302 +#define HTTP_NOTFOUND 404 + +struct evhttp; +struct evhttp_request; + +/* Start an HTTP server on the specified address and port */ +struct evhttp *evhttp_start(const char *address, u_short port); + +/* + * Free the previously create HTTP server. Works only if no requests are + * currently being served. + */ +void evhttp_free(struct evhttp* http); + +/* Set a callback for a specified URI */ +void evhttp_set_cb(struct evhttp *, const char *, + void (*)(struct evhttp_request *, void *), void *); + +/* Set a callback for all requests that are not caught by specific callbacks */ +void evhttp_set_gencb(struct evhttp *, + void (*)(struct evhttp_request *, void *), void *); + +void evhttp_send_error(struct evhttp_request *, int, const char *); +void evhttp_send_reply(struct evhttp_request *, int, const char *, + struct evbuffer *); + +/* Interfaces for making requests */ +enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD }; + +struct evhttp_request *evhttp_request_new( + void (*cb)(struct evhttp_request *, void *), void *arg); +void evhttp_request_free(struct evhttp_request *req); +const char *evhttp_request_uri(struct evhttp_request *req); + +/* Interfaces for dealing with HTTP headers */ + +const char *evhttp_find_header(struct evkeyvalq *, const char *); +int evhttp_remove_header(struct evkeyvalq *, const char *); +int evhttp_add_header(struct evkeyvalq *, const char *, const char *); +void evhttp_clear_headers(struct evkeyvalq *); + +/* Miscellaneous utility functions */ +void evhttp_parse_query(const char *uri, struct evkeyvalq *); +char *evhttp_htmlescape(const char *html); #ifdef __cplusplus } #endif diff --git a/varnish-cache/contrib/libevent/event_rpcgen.py b/varnish-cache/contrib/libevent/event_rpcgen.py new file mode 100755 index 00000000..d6a579d1 --- /dev/null +++ b/varnish-cache/contrib/libevent/event_rpcgen.py @@ -0,0 +1,1378 @@ +#!/usr/bin/env python +# +# Copyright (c) 2005 Niels Provos +# All rights reserved. +# +# Generates marshalling code based on libevent. + +import sys +import re + +# +_NAME = "event_rpcgen.py" +_VERSION = "0.1" +_STRUCT_RE = '[a-z][a-z_0-9]*' + +# Globals +line_count = 0 + +leading = re.compile(r'^\s+') +trailing = re.compile(r'\s+$') +white = re.compile(r'^\s+') +cppcomment = re.compile(r'\/\/.*$') +cppdirect = [] + +# Holds everything that makes a struct +class Struct: + def __init__(self, name): + self._name = name + self._entries = [] + self._tags = {} + print >>sys.stderr, ' Created struct: %s' % name + + def AddEntry(self, entry): + if self._tags.has_key(entry.Tag()): + print >>sys.stderr, ( 'Entry "%s" duplicates tag number ' + '%d from "%s" around line %d' ) % ( + entry.Name(), entry.Tag(), + self._tags[entry.Tag()], line_count) + sys.exit(1) + self._entries.append(entry) + self._tags[entry.Tag()] = entry.Name() + print >>sys.stderr, ' Added entry: %s' % entry.Name() + + def Name(self): + return self._name + + def EntryTagName(self, entry): + """Creates the name inside an enumeration for distinguishing data + types.""" + name = "%s_%s" % (self._name, entry.Name()) + return name.upper() + + def PrintIdented(self, file, ident, code): + """Takes an array, add indentation to each entry and prints it.""" + for entry in code: + print >>file, '%s%s' % (ident, entry) + + def PrintTags(self, file): + """Prints the tag definitions for a structure.""" + print >>file, '/* Tag definition for %s */' % self._name + print >>file, 'enum %s_ {' % self._name.lower() + for entry in self._entries: + print >>file, ' %s=%d,' % (self.EntryTagName(entry), + entry.Tag()) + print >>file, ' %s_MAX_TAGS' % (self._name.upper()) + print >>file, '} %s_tags;\n' % (self._name.lower()) + + def PrintForwardDeclaration(self, file): + print >>file, 'struct %s;' % self._name + + def PrintDeclaration(self, file): + print >>file, '/* Structure declaration for %s */' % self._name + print >>file, 'struct %s {' % self._name + for entry in self._entries: + dcl = entry.Declaration() + dcl.extend( + entry.AssignDeclaration('(*%s_assign)' % entry.Name())) + dcl.extend( + entry.GetDeclaration('(*%s_get)' % entry.Name())) + if entry.Array(): + dcl.extend( + entry.AddDeclaration('(*%s_add)' % entry.Name())) + self.PrintIdented(file, ' ', dcl) + print >>file, '' + for entry in self._entries: + print >>file, ' u_int8_t %s_set;' % entry.Name() + print >>file, '};\n' + + print >>file, ( + 'struct %s *%s_new();\n' % (self._name, self._name) + + 'void %s_free(struct %s *);\n' % (self._name, self._name) + + 'void %s_clear(struct %s *);\n' % (self._name, self._name) + + 'void %s_marshal(struct evbuffer *, const struct %s *);\n' % ( + self._name, self._name) + + 'int %s_unmarshal(struct %s *, struct evbuffer *);\n' % ( + self._name, self._name) + + 'int %s_complete(struct %s *);' % (self._name, self._name) + ) + print >>file, ('void evtag_marshal_%s(struct evbuffer *, u_int8_t, ' + 'const struct %s *);') % ( self._name, self._name) + print >>file, ('int evtag_unmarshal_%s(struct evbuffer *, u_int8_t, ' + 'struct %s *);') % ( self._name, self._name) + + # Write a setting function of every variable + for entry in self._entries: + self.PrintIdented(file, '', entry.AssignDeclaration( + entry.AssignFuncName())) + self.PrintIdented(file, '', entry.GetDeclaration( + entry.GetFuncName())) + if entry.Array(): + self.PrintIdented(file, '', entry.AddDeclaration( + entry.AddFuncName())) + + print >>file, '/* --- %s done --- */\n' % self._name + + def PrintCode(self, file): + print >>file, ('/*\n' + ' * Implementation of %s\n' + ' */\n') % self._name + + # Creation + print >>file, ( 'struct %s *\n' % self._name + + '%s_new()\n' % self._name + + '{\n' + ' struct %s *tmp;\n' % self._name + + ' if ((tmp = malloc(sizeof(struct %s))) == NULL) {\n' + ' event_warn("%%s: malloc", __func__);\n' + ' return (NULL);\n' % self._name + + ' }' + ) + for entry in self._entries: + self.PrintIdented(file, ' ', entry.CodeNew('tmp')) + print >>file, ' tmp->%s_set = 0;\n' % entry.Name() + + print >>file, (' return (tmp);\n' + '}\n') + + # Adding + for entry in self._entries: + if entry.Array(): + self.PrintIdented(file, '', entry.CodeAdd()) + print >>file, '' + + # Assigning + for entry in self._entries: + self.PrintIdented(file, '', entry.CodeAssign()) + print >>file, '' + + # Getting + for entry in self._entries: + self.PrintIdented(file, '', entry.CodeGet()) + print >>file, '' + + # Clearing + print >>file, ( 'void\n' + '%s_clear(struct %s *tmp)\n' % ( + self._name, self._name)+ + '{' + ) + for entry in self._entries: + self.PrintIdented(file, ' ', entry.CodeClear('tmp')) + + print >>file, '}\n' + + # Freeing + print >>file, ( 'void\n' + '%s_free(struct %s *tmp)\n' % ( + self._name, self._name)+ + '{' + ) + for entry in self._entries: + self.PrintIdented(file, ' ', entry.CodeFree('tmp')) + + print >>file, (' free(tmp);\n' + '}\n') + + # Marshaling + print >>file, ('void\n' + '%s_marshal(struct evbuffer *evbuf, ' + 'const struct %s *tmp)' % (self._name, self._name) + + '{') + for entry in self._entries: + indent = ' ' + # Optional entries do not have to be set + if entry.Optional(): + indent += ' ' + print >>file, ' if (tmp->%s_set) {' % entry.Name() + self.PrintIdented( + file, indent, + entry.CodeMarshal('evbuf', self.EntryTagName(entry), 'tmp')) + if entry.Optional(): + print >>file, ' }' + + print >>file, '}\n' + + # Unmarshaling + print >>file, ('int\n' + '%s_unmarshal(struct %s *tmp, ' + ' struct evbuffer *evbuf)\n' % ( + self._name, self._name) + + '{\n' + ' u_int8_t tag;\n' + ' while (EVBUFFER_LENGTH(evbuf) > 0) {\n' + ' if (evtag_peek(evbuf, &tag) == -1)\n' + ' return (-1);\n' + ' switch (tag) {\n' + ) + for entry in self._entries: + print >>file, ' case %s:\n' % self.EntryTagName(entry) + if not entry.Array(): + print >>file, ( + ' if (tmp->%s_set)\n' + ' return (-1);' + ) % (entry.Name()) + + self.PrintIdented( + file, ' ', + entry.CodeUnmarshal('evbuf', + self.EntryTagName(entry), 'tmp')) + + print >>file, ( ' tmp->%s_set = 1;\n' % entry.Name() + + ' break;\n' ) + print >>file, ( ' default:\n' + ' return -1;\n' + ' }\n' + ' }\n' ) + # Check if it was decoded completely + print >>file, ( ' if (%s_complete(tmp) == -1)\n' % self._name + + ' return (-1);') + + # Successfully decoded + print >>file, ( ' return (0);\n' + '}\n') + + # Checking if a structure has all the required data + print >>file, ( + 'int\n' + '%s_complete(struct %s *msg)\n' % (self._name, self._name) + + '{' ) + for entry in self._entries: + self.PrintIdented( + file, ' ', + entry.CodeComplete('msg')) + print >>file, ( + ' return (0);\n' + '}\n' ) + + # Complete message unmarshaling + print >>file, ( + 'int\n' + 'evtag_unmarshal_%s(struct evbuffer *evbuf, u_int8_t need_tag, ' + ' struct %s *msg)' + ) % (self._name, self._name) + print >>file, ( + '{\n' + ' u_int8_t tag;\n' + ' int res = -1;\n' + '\n' + ' struct evbuffer *tmp = evbuffer_new();\n' + '\n' + ' if (evtag_unmarshal(evbuf, &tag, tmp) == -1' + ' || tag != need_tag)\n' + ' goto error;\n' + '\n' + ' if (%s_unmarshal(msg, tmp) == -1)\n' + ' goto error;\n' + '\n' + ' res = 0;\n' + '\n' + ' error:\n' + ' evbuffer_free(tmp);\n' + ' return (res);\n' + '}\n' ) % self._name + + # Complete message marshaling + print >>file, ( + 'void\n' + 'evtag_marshal_%s(struct evbuffer *evbuf, u_int8_t tag, ' + 'const struct %s *msg)\n' % (self._name, self._name) + + '{\n' + ' struct evbuffer *_buf = evbuffer_new();\n' + ' assert(_buf != NULL);\n' + ' evbuffer_drain(_buf, -1);\n' + ' %s_marshal(_buf, msg);\n' % self._name + + ' evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), ' + 'EVBUFFER_LENGTH(_buf));\n' + ' evbuffer_free(_buf);\n' + '}\n' ) + +class Entry: + def __init__(self, type, name, tag): + self._type = type + self._name = name + self._tag = int(tag) + self._ctype = type + self._optional = 0 + self._can_be_array = 0 + self._array = 0 + self._line_count = -1 + self._struct = None + + def SetStruct(self, struct): + self._struct = struct + + def LineCount(self): + assert self._line_count != -1 + return self._line_count + + def SetLineCount(self, number): + self._line_count = number + + def Array(self): + return self._array + + def Optional(self): + return self._optional + + def Tag(self): + return self._tag + + def Name(self): + return self._name + + def Type(self): + return self._type + + def MakeArray(self, yes=1): + self._array = yes + + def MakeOptional(self): + self._optional = 1 + + def GetFuncName(self): + return '%s_%s_get' % (self._struct.Name(), self._name) + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeGet(self): + code = [ 'int', + '%s_%s_get(struct %s *msg, %s *value)' % ( + self._struct.Name(), self._name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1)' % self._name, + ' return (-1);', + ' *value = msg->%s_data;' % self._name, + ' return (0);', + '}' ] + return code + + def AssignFuncName(self): + return '%s_%s_assign' % (self._struct.Name(), self._name) + + def AddFuncName(self): + return '%s_%s_add' % (self._struct.Name(), self._name) + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeAssign(self): + code = [ 'int', + '%s_%s_assign(struct %s *msg, const %s value)' % ( + self._struct.Name(), self._name, + self._struct.Name(), self._ctype), + '{', + ' msg->%s_set = 1;' % self._name, + ' msg->%s_data = value;' % self._name, + ' return (0);', + '}' ] + return code + + def CodeClear(self, structname): + code = [ '%s->%s_set = 0;' % (structname, self.Name()) ] + + return code + + def CodeComplete(self, structname): + if self.Optional(): + return [] + + code = [ 'if (!%s->%s_set)' % (structname, self.Name()), + ' return (-1);' ] + + return code + + def CodeFree(self, name): + return [] + + def CodeNew(self, name): + code = [ '%s->%s_assign = %s_%s_assign;' % ( + name, self._name, self._struct.Name(), self._name ), + '%s->%s_get = %s_%s_get;' % ( + name, self._name, self._struct.Name(), self._name ), + ] + if self.Array(): + code.append( + '%s->%s_add = %s_%s_add;' % ( + name, self._name, self._struct.Name(), self._name ) ) + return code + + def Verify(self): + if self.Array() and not self._can_be_array: + print >>sys.stderr, ( + 'Entry "%s" cannot be created as an array ' + 'around line %d' ) % (self._name, self.LineCount()) + sys.exit(1) + if not self._struct: + print >>sys.stderr, ( + 'Entry "%s" does not know which struct it belongs to ' + 'around line %d' ) % (self._name, self.LineCount()) + sys.exit(1) + if self._optional and self._array: + print >>sys.stderr, ( 'Entry "%s" has illegal combination of ' + 'optional and array around line %d' ) % ( + self._name, self.LineCount() ) + sys.exit(1) + +class EntryBytes(Entry): + def __init__(self, type, name, tag, length): + # Init base class + Entry.__init__(self, type, name, tag) + + self._length = length + self._ctype = 'u_int8_t' + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s **);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def Declaration(self): + dcl = ['u_int8_t %s_data[%s];' % (self._name, self._length)] + + return dcl + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s **value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1)' % name, + ' return (-1);', + ' *value = msg->%s_data;' % name, + ' return (0);', + '}' ] + return code + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, const %s *value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' msg->%s_set = 1;' % name, + ' memcpy(msg->%s_data, value, %s);' % ( + name, self._length), + ' return (0);', + '}' ] + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = [ 'if (evtag_unmarshal_fixed(%s, %s, ' % (buf, tag_name) + + '%s->%s_data, ' % (var_name, self._name) + + 'sizeof(%s->%s_data)) == -1) {' % ( + var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal(%s, %s, %s->%s_data, sizeof(%s->%s_data));' % ( + buf, tag_name, var_name, self._name, var_name, self._name )] + return code + + def CodeClear(self, structname): + code = [ '%s->%s_set = 0;' % (structname, self.Name()), + 'memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( + structname, self._name, structname, self._name)] + + return code + + def CodeNew(self, name): + code = ['memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( + name, self._name, name, self._name)] + code.extend(Entry.CodeNew(self, name)) + return code + + def Verify(self): + if not self._length: + print >>sys.stderr, 'Entry "%s" needs a length around line %d' % ( + self._name, self.LineCount() ) + sys.exit(1) + + Entry.Verify(self) + +class EntryInt(Entry): + def __init__(self, type, name, tag): + # Init base class + Entry.__init__(self, type, name, tag) + + self._ctype = 'u_int32_t' + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['if (evtag_unmarshal_int(%s, %s, &%s->%s_data) == -1) {' % ( + buf, tag_name, var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal_int(%s, %s, %s->%s_data);' % ( + buf, tag_name, var_name, self._name)] + return code + + def Declaration(self): + dcl = ['u_int32_t %s_data;' % self._name] + + return dcl + +class EntryString(Entry): + def __init__(self, type, name, tag): + # Init base class + Entry.__init__(self, type, name, tag) + + self._ctype = 'char *' + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, const %s value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_data != NULL)' % name, + ' free(msg->%s_data);' % name, + ' if ((msg->%s_data = strdup(value)) == NULL)' % name, + ' return (-1);', + ' msg->%s_set = 1;' % name, + ' return (0);', + '}' ] + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['if (evtag_unmarshal_string(%s, %s, &%s->%s_data) == -1) {' % ( + buf, tag_name, var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal_string(%s, %s, %s->%s_data);' % ( + buf, tag_name, var_name, self._name)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' free (%s->%s_data);' % (structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeNew(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name)] + code.extend(Entry.CodeNew(self, name)) + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' free (%s->%s_data); ' % (name, self._name)] + + return code + + def Declaration(self): + dcl = ['char *%s_data;' % self._name] + + return dcl + +class EntryStruct(Entry): + def __init__(self, type, name, tag, refname): + # Init base class + Entry.__init__(self, type, name, tag) + + self._can_be_array = 1 + self._refname = refname + self._ctype = 'struct %s' % refname + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s **);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s **value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1) {' % name, + ' msg->%s_data = %s_new();' % (name, self._refname), + ' if (msg->%s_data == NULL)' % name, + ' return (-1);', + ' msg->%s_set = 1;' % name, + ' }', + ' *value = msg->%s_data;' % name, + ' return (0);', + '}' ] + return code + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, const %s *value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' struct evbuffer *tmp = NULL;', + ' if (msg->%s_set) {' % name, + ' %s_clear(msg->%s_data);' % (self._refname, name), + ' msg->%s_set = 0;' % name, + ' } else {', + ' msg->%s_data = %s_new();' % (name, self._refname), + ' if (msg->%s_data == NULL) {' % name, + ' event_warn("%%s: %s_new()", __func__);' % ( + self._refname), + ' goto error;', + ' }', + ' }', + ' if ((tmp = evbuffer_new()) == NULL) {', + ' event_warn("%s: evbuffer_new()", __func__);', + ' goto error;', + ' }', + ' %s_marshal(tmp, value); ' % self._refname, + ' if (%s_unmarshal(msg->%s_data, tmp) == -1) {' % ( + self._refname, name ), + ' event_warnx("%%s: %s_unmarshal", __func__);' % ( + self._refname), + ' goto error;', + ' }', + ' msg->%s_set = 1;' % name, + ' evbuffer_free(tmp);', + ' return (0);', + ' error:', + ' if (tmp != NULL)', + ' evbuffer_free(tmp);', + ' if (msg->%s_data != NULL) {' % name, + ' %s_free(msg->%s_data);' % (self._refname, name), + ' msg->%s_data = NULL;' % name, + ' }', + ' return (-1);', + '}' ] + return code + + def CodeComplete(self, structname): + if self.Optional(): + code = [ 'if (%s->%s_set && %s_complete(%s->%s_data) == -1)' % ( + structname, self.Name(), + self._refname, structname, self.Name()), + ' return (-1);' ] + else: + code = [ 'if (%s_complete(%s->%s_data) == -1)' % ( + self._refname, structname, self.Name()), + ' return (-1);' ] + + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['%s->%s_data = %s_new();' % ( + var_name, self._name, self._refname), + 'if (%s->%s_data == NULL)' % (var_name, self._name), + ' return (-1);', + 'if (evtag_unmarshal_%s(%s, %s, %s->%s_data) == -1) {' % ( + self._refname, buf, tag_name, var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal_%s(%s, %s, %s->%s_data);' % ( + self._refname, buf, tag_name, var_name, self._name)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' %s_free(%s->%s_data);' % ( + self._refname, structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeNew(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name)] + code.extend(Entry.CodeNew(self, name)) + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' %s_free(%s->%s_data); ' % ( + self._refname, name, self._name)] + + return code + + def Declaration(self): + dcl = ['struct %s *%s_data;' % (self._refname, self._name)] + + return dcl + +class EntryVarBytes(Entry): + def __init__(self, type, name, tag): + # Init base class + Entry.__init__(self, type, name, tag) + + self._ctype = 'u_int8_t *' + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s *, u_int32_t *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s, u_int32_t);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, ' + 'const %s value, u_int32_t len)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_data != NULL)' % name, + ' free (msg->%s_data);' % name, + ' msg->%s_data = malloc(len);' % name, + ' if (msg->%s_data == NULL)' % name, + ' return (-1);', + ' msg->%s_set = 1;' % name, + ' msg->%s_length = len;' % name, + ' memcpy(msg->%s_data, value, len);' % name, + ' return (0);', + '}' ] + return code + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s *value, u_int32_t *plen)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1)' % name, + ' return (-1);', + ' *value = msg->%s_data;' % name, + ' *plen = msg->%s_length;' % name, + ' return (0);', + '}' ] + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['if (evtag_payload_length(%s, &%s->%s_length) == -1)' % ( + buf, var_name, self._name), + ' return (-1);', + # We do not want DoS opportunities + 'if (%s->%s_length > EVBUFFER_LENGTH(%s))' % ( + var_name, self._name, buf), + ' return (-1);', + 'if ((%s->%s_data = malloc(%s->%s_length)) == NULL)' % ( + var_name, self._name, var_name, self._name), + ' return (-1);', + 'if (evtag_unmarshal_fixed(%s, %s, %s->%s_data, ' + '%s->%s_length) == -1) {' % ( + buf, tag_name, var_name, self._name, var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal(%s, %s, %s->%s_data, %s->%s_length);' % ( + buf, tag_name, var_name, self._name, var_name, self._name)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' free (%s->%s_data);' % (structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_length = 0;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeNew(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name), + '%s->%s_length = 0;' % (name, self._name) ] + code.extend(Entry.CodeNew(self, name)) + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' free (%s->%s_data); ' % (name, self._name)] + + return code + + def Declaration(self): + dcl = ['u_int8_t *%s_data;' % self._name, + 'u_int32_t %s_length;' % self._name] + + return dcl + +class EntryArray(Entry): + def __init__(self, entry): + # Init base class + Entry.__init__(self, entry._type, entry._name, entry._tag) + + self._entry = entry + self._refname = entry._refname + self._ctype = 'struct %s' % self._refname + + def GetDeclaration(self, funcname): + """Allows direct access to elements of the array.""" + code = [ 'int %s(struct %s *, int, %s **);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, int, const %s *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AddDeclaration(self, funcname): + code = [ '%s *%s(struct %s *);' % ( + self._ctype, funcname, self._struct.Name() ) ] + return code + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, int offset, %s **value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1)' % name, + ' return (-1);', + ' if (offset >= msg->%s_length)' % name, + ' return (-1);', + ' *value = msg->%s_data[offset];' % name, + ' return (0);', + '}' ] + return code + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, int off, const %s *value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' struct evbuffer *tmp = NULL;', + ' if (msg->%s_set != 1)' % name, + ' return (-1);', + ' if (off >= msg->%s_length)' % name, + ' return (-1);', + '', + ' %s_clear(msg->%s_data[off]);' % (self._refname, name), + ' if ((tmp = evbuffer_new()) == NULL) {', + ' event_warn("%s: evbuffer_new()", __func__);', + ' goto error;', + ' }', + ' %s_marshal(tmp, value); ' % self._refname, + ' if (%s_unmarshal(msg->%s_data[off], tmp) == -1) {' % ( + self._refname, name ), + ' event_warnx("%%s: %s_unmarshal", __func__);' % ( + self._refname), + ' goto error;', + ' }', + ' evbuffer_free(tmp);', + ' return (0);', + ' error:', + ' if (tmp != NULL)', + ' evbuffer_free(tmp);', + ' %s_clear(msg->%s_data[off]);' % (self._refname, name), + ' return (-1);', + '}' ] + return code + + def CodeAdd(self): + name = self._name + code = [ + '%s *' % self._ctype, + '%s_%s_add(struct %s *msg)' % ( + self._struct.Name(), name, self._struct.Name()), + '{', + ' msg->%s_length++;' % name, + ' msg->%s_data = (struct %s**)realloc(msg->%s_data, ' + ' msg->%s_length * sizeof(struct %s*));' % ( + name, self._refname, name, name, self._refname ), + ' if (msg->%s_data == NULL)' % name, + ' return (NULL);', + ' msg->%s_data[msg->%s_length - 1] = %s_new();' % ( + name, name, self._refname), + ' if (msg->%s_data[msg->%s_length - 1] == NULL) {' % (name, name), + ' msg->%s_length--; ' % name, + ' return (NULL);', + ' }', + ' msg->%s_set = 1;' % name, + ' return (msg->%s_data[msg->%s_length - 1]);' % (name, name), + '}' + ] + return code + + def CodeComplete(self, structname): + code = [] + if self.Optional(): + code.append( 'if (%s->%s_set)' % (structname, self.Name())) + + code.extend(['{', + ' int i;', + ' for (i = 0; i < %s->%s_length; ++i) {' % ( + structname, self.Name()), + ' if (%s_complete(%s->%s_data[i]) == -1)' % ( + self._refname, structname, self.Name()), + ' return (-1);', + ' }', + '}' + ]) + + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['if (%s_%s_add(%s) == NULL)' % ( + self._struct.Name(), self._name, var_name), + ' return (-1);', + 'if (evtag_unmarshal_%s(%s, %s, ' + '%s->%s_data[%s->%s_length - 1]) == -1) {' % ( + self._refname, buf, tag_name, var_name, self._name, + var_name, self._name), + ' %s->%s_length--; ' % (var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['{', + ' int i;', + ' for (i = 0; i < %s->%s_length; ++i) {' % ( + var_name, self._name), + ' evtag_marshal_%s(%s, %s, %s->%s_data[i]);' % ( + self._refname, buf, tag_name, var_name, self._name), + ' }', + '}' + ] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' int i;', + ' for (i = 0; i < %s->%s_length; ++i) {' % ( + structname, self.Name()), + ' %s_free(%s->%s_data[i]);' % ( + self._refname, structname, self.Name()), + ' }', + ' free(%s->%s_data);' % (structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + ' %s->%s_length = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeNew(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name), + '%s->%s_length = 0;' % (name, self._name)] + code.extend(Entry.CodeNew(self, name)) + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL) {' % (name, self._name), + ' int i;', + ' for (i = 0; i < %s->%s_length; ++i) {' % ( + name, self._name), + ' %s_free(%s->%s_data[i]); ' % ( + self._refname, name, self._name), + ' %s->%s_data[i] = NULL;' % (name, self._name), + ' }', + ' free(%s->%s_data);' % (name, self._name), + ' %s->%s_data = NULL;' % (name, self._name), + ' %s->%s_length = 0;' % (name, self._name), + '}' + ] + + return code + + def Declaration(self): + dcl = ['struct %s **%s_data;' % (self._refname, self._name), + 'int %s_length;' % self._name] + + return dcl + +def NormalizeLine(line): + global leading + global trailing + global white + global cppcomment + + line = cppcomment.sub('', line) + line = leading.sub('', line) + line = trailing.sub('', line) + line = white.sub(' ', line) + + return line + +def ProcessOneEntry(newstruct, entry): + optional = 0 + array = 0 + type = '' + name = '' + tag = '' + tag_set = None + separator = '' + fixed_length = '' + + tokens = entry.split(' ') + while tokens: + token = tokens[0] + tokens = tokens[1:] + + if not type: + if not optional and token == 'optional': + optional = 1 + continue + + if not array and token == 'array': + array = 1 + continue + + if not type: + type = token + continue + + if not name: + res = re.match(r'^([^\[\]]+)(\[.*\])?$', token) + if not res: + print >>sys.stderr, 'Cannot parse name: \"%s\" around %d' % ( + entry, line_count) + sys.exit(1) + name = res.group(1) + fixed_length = res.group(2) + if fixed_length: + fixed_length = fixed_length[1:-1] + continue + + if not separator: + separator = token + if separator != '=': + print >>sys.stderr, 'Expected "=" after name \"%s\" got %s' % ( + name, token) + sys.exit(1) + continue + + if not tag_set: + tag_set = 1 + if not re.match(r'^[0-9]+$', token): + print >>sys.stderr, 'Expected tag number: \"%s\"' % entry + sys.exit(1) + tag = int(token) + continue + + print >>sys.stderr, 'Cannot parse \"%s\"' % entry + sys.exit(1) + + if not tag_set: + print >>sys.stderr, 'Need tag number: \"%s\"' % entry + sys.exit(1) + + # Create the right entry + if type == 'bytes': + if fixed_length: + newentry = EntryBytes(type, name, tag, fixed_length) + else: + newentry = EntryVarBytes(type, name, tag) + elif type == 'int' and not fixed_length: + newentry = EntryInt(type, name, tag) + elif type == 'string' and not fixed_length: + newentry = EntryString(type, name, tag) + else: + res = re.match(r'^struct\[(%s)\]$' % _STRUCT_RE, type, re.IGNORECASE) + if res: + # References another struct defined in our file + newentry = EntryStruct(type, name, tag, res.group(1)) + else: + print >>sys.stderr, 'Bad type: "%s" in "%s"' % (type, entry) + sys.exit(1) + + structs = [] + + if optional: + newentry.MakeOptional() + if array: + newentry.MakeArray() + + newentry.SetStruct(newstruct) + newentry.SetLineCount(line_count) + newentry.Verify() + + if array: + # We need to encapsulate this entry into a struct + newname = newentry.Name()+ '_array' + + # Now borgify the new entry. + newentry = EntryArray(newentry) + newentry.SetStruct(newstruct) + newentry.SetLineCount(line_count) + newentry.MakeArray() + + newstruct.AddEntry(newentry) + + return structs + +def ProcessStruct(data): + tokens = data.split(' ') + + # First three tokens are: 'struct' 'name' '{' + newstruct = Struct(tokens[1]) + + inside = ' '.join(tokens[3:-1]) + + tokens = inside.split(';') + + structs = [] + + for entry in tokens: + entry = NormalizeLine(entry) + if not entry: + continue + + # It's possible that new structs get defined in here + structs.extend(ProcessOneEntry(newstruct, entry)) + + structs.append(newstruct) + return structs + +def GetNextStruct(file): + global line_count + global cppdirect + + got_struct = 0 + + processed_lines = [] + + have_c_comment = 0 + data = '' + for line in file: + line_count += 1 + line = line[:-1] + + if not have_c_comment and re.search(r'/\*', line): + if re.search(r'/\*.*\*/', line): + line = re.sub(r'/\*.*\*/', '', line) + else: + line = re.sub(r'/\*.*$', '', line) + have_c_comment = 1 + + if have_c_comment: + if not re.search(r'\*/', line): + continue + have_c_comment = 0 + line = re.sub(r'^.*\*/', '', line) + + line = NormalizeLine(line) + + if not line: + continue + + if not got_struct: + if re.match(r'#include ["<].*[>"]', line): + cppdirect.append(line) + continue + + if re.match(r'^#(if( |def)|endif)', line): + cppdirect.append(line) + continue + + if not re.match(r'^struct %s {$' % _STRUCT_RE, + line, re.IGNORECASE): + print >>sys.stderr, 'Missing struct on line %d: %s' % ( + line_count, line) + sys.exit(1) + else: + got_struct = 1 + data += line + continue + + # We are inside the struct + tokens = line.split('}') + if len(tokens) == 1: + data += ' ' + line + continue + + if len(tokens[1]): + print >>sys.stderr, 'Trailing garbage after struct on line %d' % ( + line_count ) + sys.exit(1) + + # We found the end of the struct + data += ' %s}' % tokens[0] + break + + # Remove any comments, that might be in there + data = re.sub(r'/\*.*\*/', '', data) + + return data + + +def Parse(file): + """Parses the input file and returns C code and corresponding header + file.""" + + entities = [] + + while 1: + # Just gets the whole struct nicely formatted + data = GetNextStruct(file) + + if not data: + break + + entities.extend(ProcessStruct(data)) + + return entities + +def GuardName(name): + name = '_'.join(name.split('.')) + name = '_'.join(name.split('/')) + guard = '_'+name.upper()+'_' + + return guard + +def HeaderPreamble(name): + guard = GuardName(name) + pre = ( + '/*\n' + ' * Automatically generated from %s\n' + ' */\n\n' + '#ifndef %s\n' + '#define %s\n\n' ) % ( + name, guard, guard) + pre += ( + '#define EVTAG_HAS(msg, member) ((msg)->member##_set == 1)\n' + '#define EVTAG_ASSIGN(msg, member, args...) ' + '(*(msg)->member##_assign)(msg, ## args)\n' + '#define EVTAG_GET(msg, member, args...) ' + '(*(msg)->member##_get)(msg, ## args)\n' + '#define EVTAG_ADD(msg, member) (*(msg)->member##_add)(msg)\n' + '#define EVTAG_LEN(msg, member) ((msg)->member##_length)\n' + ) + + return pre + + +def HeaderPostamble(name): + guard = GuardName(name) + return '#endif /* %s */' % guard + +def BodyPreamble(name): + global _NAME + global _VERSION + + header_file = '.'.join(name.split('.')[:-1]) + '.gen.h' + + pre = ( '/*\n' + ' * Automatically generated from %s\n' + ' * by %s/%s. DO NOT EDIT THIS FILE.\n' + ' */\n\n' ) % (name, _NAME, _VERSION) + pre += ( '#include \n' + '#include \n' + '#include \n' + '#include \n' + '#include \n' + '#include \n\n' ) + + for include in cppdirect: + pre += '%s\n' % include + + pre += '\n#include "%s"\n\n' % header_file + + pre += 'void event_err(int eval, const char *fmt, ...);\n' + pre += 'void event_warn(const char *fmt, ...);\n' + pre += 'void event_errx(int eval, const char *fmt, ...);\n' + pre += 'void event_warnx(const char *fmt, ...);\n\n' + + return pre + +def main(argv): + filename = argv[1] + + if filename.split('.')[-1] != 'rpc': + ext = filename.split('.')[-1] + print >>sys.stderr, 'Unrecognized file extension: %s' % ext + sys.exit(1) + + print >>sys.stderr, 'Reading \"%s\"' % filename + + fp = open(filename, 'r') + entities = Parse(fp) + fp.close() + + header_file = '.'.join(filename.split('.')[:-1]) + '.gen.h' + impl_file = '.'.join(filename.split('.')[:-1]) + '.gen.c' + + print >>sys.stderr, '... creating "%s"' % header_file + header_fp = open(header_file, 'w') + print >>header_fp, HeaderPreamble(filename) + + # Create forward declarations: allows other structs to reference + # each other + for entry in entities: + entry.PrintForwardDeclaration(header_fp) + print >>header_fp, '' + + for entry in entities: + entry.PrintTags(header_fp) + entry.PrintDeclaration(header_fp) + print >>header_fp, HeaderPostamble(filename) + header_fp.close() + + print >>sys.stderr, '... creating "%s"' % impl_file + impl_fp = open(impl_file, 'w') + print >>impl_fp, BodyPreamble(filename) + for entry in entities: + entry.PrintCode(impl_fp) + impl_fp.close() + +if __name__ == '__main__': + main(sys.argv) diff --git a/varnish-cache/contrib/libevent/event_tagging.c b/varnish-cache/contrib/libevent/event_tagging.c new file mode 100644 index 00000000..b923ad3c --- /dev/null +++ b/varnish-cache/contrib/libevent/event_tagging.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2003, 2004 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "event.h" + +int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf); + +static struct evbuffer *_buf; + +void +evtag_init() +{ + if ((_buf = evbuffer_new()) == NULL) + err(1, "%s: malloc", __func__); +} + +/* + * We encode integer's by nibbles; the first nibble contains the number + * of significant nibbles - 1; this allows us to encode up to 64-bit + * integers. This function is byte-order independent. + */ + +void +encode_int(struct evbuffer *evbuf, u_int32_t number) +{ + int off = 1, nibbles = 0; + u_int8_t data[5]; + + memset(data, 0, sizeof(data)); + while (number) { + if (off & 0x1) + data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f); + else + data[off/2] = (data[off/2] & 0x0f) | + ((number & 0x0f) << 4); + number >>= 4; + off++; + } + + if (off > 2) + nibbles = off - 2; + + /* Off - 1 is the number of encoded nibbles */ + data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4); + + evbuffer_add(evbuf, data, (off + 1) / 2); +} + +/* + * Marshal a data type, the general format is as follows: + * + * tag number: one byte; length: var bytes; payload: var bytes + */ + +void +evtag_marshal(struct evbuffer *evbuf, u_int8_t tag, + const void *data, u_int16_t len) +{ + evbuffer_add(evbuf, &tag, sizeof(tag)); + encode_int(evbuf, len); + evbuffer_add(evbuf, (void *)data, len); +} + +/* Marshaling for integers */ +void +evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag, u_int32_t integer) +{ + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + encode_int(_buf, integer); + + evbuffer_add(evbuf, &tag, sizeof(tag)); + encode_int(evbuf, EVBUFFER_LENGTH(_buf)); + evbuffer_add_buffer(evbuf, _buf); +} + +void +evtag_marshal_string(struct evbuffer *buf, u_int8_t tag, const char *string) +{ + evtag_marshal(buf, tag, string, strlen(string)); +} + +void +evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag, struct timeval *tv) +{ + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + + encode_int(_buf, tv->tv_sec); + encode_int(_buf, tv->tv_usec); + + evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), + EVBUFFER_LENGTH(_buf)); +} + +static int __inline +decode_int_internal(u_int32_t *pnumber, struct evbuffer *evbuf, int dodrain) +{ + u_int32_t number = 0; + u_int8_t *data = EVBUFFER_DATA(evbuf); + int len = EVBUFFER_LENGTH(evbuf); + int nibbles = 0, off; + + if (!len) + return (-1); + + nibbles = ((data[0] & 0xf0) >> 4) + 1; + if (nibbles > 8 || (nibbles >> 1) > len - 1) + return (-1); + + off = nibbles; + while (off > 0) { + number <<= 4; + if (off & 0x1) + number |= data[off >> 1] & 0x0f; + else + number |= (data[off >> 1] & 0xf0) >> 4; + off--; + } + + len = (nibbles >> 1) + 1; + if (dodrain) + evbuffer_drain(evbuf, len); + + *pnumber = number; + + return (len); +} + +int +decode_int(u_int32_t *pnumber, struct evbuffer *evbuf) +{ + return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0); +} + +int +evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag) +{ + if (EVBUFFER_LENGTH(evbuf) < 2) + return (-1); + *ptag = EVBUFFER_DATA(evbuf)[0]; + + return (0); +} + +int +evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength) +{ + struct evbuffer tmp; + int res; + + if (EVBUFFER_LENGTH(evbuf) < 2) + return (-1); + + tmp = *evbuf; + tmp.buffer += 1; + tmp.off -= 1; + + res = decode_int_internal(plength, &tmp, 0); + if (res == -1) + return (-1); + + *plength += res + 1; + + return (0); +} + +int +evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength) +{ + struct evbuffer tmp; + int res; + + if (EVBUFFER_LENGTH(evbuf) < 2) + return (-1); + + tmp = *evbuf; + tmp.buffer += 1; + tmp.off -= 1; + + res = decode_int_internal(plength, &tmp, 0); + if (res == -1) + return (-1); + + return (0); +} + +int +evtag_consume(struct evbuffer *evbuf) +{ + u_int32_t len; + evbuffer_drain(evbuf, 1); + if (decode_int(&len, evbuf) == -1) + return (-1); + evbuffer_drain(evbuf, len); + + return (0); +} + +/* Reads the data type from an event buffer */ + +int +evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag, struct evbuffer *dst) +{ + u_int8_t tag; + u_int16_t len; + u_int32_t integer; + + if (evbuffer_remove(src, &tag, sizeof(tag)) != sizeof(tag)) + return (-1); + if (decode_int(&integer, src) == -1) + return (-1); + len = integer; + + if (EVBUFFER_LENGTH(src) < len) + return (-1); + + if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1) + return (-1); + + evbuffer_drain(src, len); + + *ptag = tag; + return (len); +} + +/* Marshaling for integers */ + +int +evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag, + u_int32_t *pinteger) +{ + u_int8_t tag; + u_int16_t len; + u_int32_t integer; + + if (evbuffer_remove(evbuf, &tag, sizeof(tag)) != sizeof(tag) || + tag != need_tag) + return (-1); + if (decode_int(&integer, evbuf) == -1) + return (-1); + len = integer; + + if (EVBUFFER_LENGTH(evbuf) < len) + return (-1); + + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1) + return (-1); + + evbuffer_drain(evbuf, len); + + return (decode_int(pinteger, _buf)); +} + +/* Unmarshal a fixed length tag */ + +int +evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data, + size_t len) +{ + u_int8_t tag; + + /* Initialize this event buffer so that we can read into it */ + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + + /* Now unmarshal a tag and check that it matches the tag we want */ + if (evtag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag) + return (-1); + + if (EVBUFFER_LENGTH(_buf) != len) + return (-1); + + memcpy(data, EVBUFFER_DATA(_buf), len); + return (0); +} + +int +evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag, + char **pstring) +{ + u_int8_t tag; + + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + + if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) + return (-1); + + *pstring = calloc(EVBUFFER_LENGTH(_buf) + 1, 1); + if (*pstring == NULL) + err(1, "%s: calloc", __func__); + evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf)); + + return (0); +} + +int +evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag, + struct timeval *ptv) +{ + u_int8_t tag; + u_int32_t integer; + + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) + return (-1); + + if (decode_int(&integer, _buf) == -1) + return (-1); + ptv->tv_sec = integer; + if (decode_int(&integer, _buf) == -1) + return (-1); + ptv->tv_usec = integer; + + return (0); +} diff --git a/varnish-cache/contrib/libevent/http.c b/varnish-cache/contrib/libevent/http.c new file mode 100644 index 00000000..9ff4dc4a --- /dev/null +++ b/varnish-cache/contrib/libevent/http.c @@ -0,0 +1,1492 @@ +/* + * Copyright (c) 2002-2006 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_IOCCOM_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_TIME_H +#include +#endif +#include +#include +#include + +#undef timeout_pending +#undef timeout_initialized + +#include "event.h" +#include "log.h" +#include "http.h" + +extern int debug; + +static int make_socket_ai(int (*f)(int, const struct sockaddr *, socklen_t), + struct addrinfo *); +static int make_socket(int (*)(int, const struct sockaddr *, socklen_t), + const char *, short); +static void name_from_addr(struct sockaddr *, socklen_t, char **, char **); + +void evhttp_write(int, short, void *); + +static const char * +html_replace(char ch) +{ + static char buf[2]; + + switch (ch) { + case '<': + return "<"; + case '>': + return ">"; + case '"': + return """; + case '\'': + return "'"; + case '&': + return "&"; + default: + break; + } + + /* Echo the character back */ + buf[0] = ch; + buf[1] = '\0'; + + return buf; +} + +/* + * Replaces <, >, ", ' and & with <, >, ", + * ' and & correspondingly. + * + * The returned string needs to be freed by the caller. + */ + +char * +evhttp_htmlescape(const char *html) +{ + int i, new_size = 0; + char *escaped_html, *p; + + for (i = 0; i < strlen(html); ++i) + new_size += strlen(html_replace(html[i])); + + p = escaped_html = malloc(new_size + 1); + if (escaped_html == NULL) + event_err(1, "%s: malloc(%d)", __func__, new_size + 1); + for (i = 0; i < strlen(html); ++i) { + const char *replaced = html_replace(html[i]); + /* this is length checked */ + strcpy(p, replaced); + p += strlen(replaced); + } + + *p = '\0'; + + return (escaped_html); +} + +const char * +evhttp_method(enum evhttp_cmd_type type) +{ + const char *method; + + switch (type) { + case EVHTTP_REQ_GET: + method = "GET"; + break; + case EVHTTP_REQ_POST: + method = "POST"; + break; + case EVHTTP_REQ_HEAD: + method = "HEAD"; + break; + default: + method = NULL; + break; + } + + return (method); +} + +void +evhttp_form_response(struct evbuffer *buf, struct evhttp_request *req) +{ + /* Clean out the buffer */ + evbuffer_drain(buf, buf->off); + + /* Create the header fields */ + evhttp_make_header(buf, req); + + /* Append the response buffer */ + evbuffer_add(buf, + EVBUFFER_DATA(req->buffer), EVBUFFER_LENGTH(req->buffer)); +} + +void +evhttp_write_buffer(struct evhttp_request *req, struct evbuffer *buffer, + void (*cb)(struct evhttp_request *, void *), void *arg) +{ + struct timeval tv; + + event_debug(("%s: preparing to write buffer\n", __func__)); + + if (req->buffer != buffer) { + if (req->buffer != NULL) + evbuffer_free(req->buffer); + req->buffer = buffer; + } + + /* Set call back */ + + req->cb = cb; + req->cb_arg = arg; + + event_set(&req->ev, req->fd, EV_WRITE, evhttp_write, req); + timerclear(&tv); + tv.tv_sec = HTTP_WRITE_TIMEOUT; + event_add(&req->ev, &tv); +} + +/* + * Create the headers need for an HTTP reply + */ +static void +evhttp_make_header_request(struct evbuffer *buf, struct evhttp_request *req) +{ + static char line[1024]; + const char *method; + + evhttp_remove_header(req->output_headers, "Accept-Encoding"); + evhttp_remove_header(req->output_headers, "Proxy-Connection"); + evhttp_remove_header(req->output_headers, "Connection"); + evhttp_add_header(req->output_headers, "Connection", "close"); + req->minor = 0; + + /* Generate request line */ + method = evhttp_method(req->type); + snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n", + method, req->uri, req->major, req->minor); + evbuffer_add(buf, line, strlen(line)); + + /* Add the content length on a post request if missing */ + if (req->type == EVHTTP_REQ_POST && + evhttp_find_header(req->output_headers, "Content-Length") == NULL){ + char size[12]; + snprintf(size, sizeof(size), "%d", + EVBUFFER_LENGTH(req->buffer)); + evhttp_add_header(req->output_headers, "Content-Length", size); + } +} + +/* + * Create the headers needed for an HTTP reply + */ +static void +evhttp_make_header_response(struct evbuffer *buf, struct evhttp_request *req) +{ + static char line[1024]; + snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n", + req->major, req->minor, req->response_code, + req->response_code_line); + evbuffer_add(buf, line, strlen(line)); + + /* Potentially add headers */ + if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) { + evhttp_add_header(req->output_headers, + "Content-Type", "text/html; charset=ISO-8859-1"); + } +} + +void +evhttp_make_header(struct evbuffer *buf, struct evhttp_request *req) +{ + static char line[1024]; + struct evkeyval *header; + + /* + * Depending if this is a HTTP request or response, we might need to + * add some new headers or remove existing headers. + */ + if (req->kind == EVHTTP_REQUEST) { + evhttp_make_header_request(buf, req); + } else { + evhttp_make_header_response(buf, req); + } + + TAILQ_FOREACH(header, req->output_headers, next) { + snprintf(line, sizeof(line), "%s: %s\r\n", + header->key, header->value); + evbuffer_add(buf, line, strlen(line)); + } + evbuffer_add(buf, "\r\n", 2); + + if (req->kind == EVHTTP_REQUEST) { + int len = EVBUFFER_LENGTH(req->buffer); + + /* Add the POST data */ + if (len > 0) + evbuffer_add(buf, EVBUFFER_DATA(req->buffer), len); + } +} + +/* Separated host, port and file from URI */ + +int +evhttp_hostportfile(char *url, char **phost, u_short *pport, char **pfile) +{ + static char host[1024]; + static char file[1024]; + char *p, *p2; + int len; + u_short port; + + len = strlen(HTTP_PREFIX); + if (strncasecmp(url, HTTP_PREFIX, len)) + return (-1); + + url += len; + + /* We might overrun */ + if (strlcpy(host, url, sizeof (host)) >= sizeof(host)) + return (-1); + + p = strchr(host, '/'); + if (p != NULL) { + *p = '\0'; + p2 = p + 1; + } else + p2 = NULL; + + if (pfile != NULL) { + /* Generate request file */ + if (p2 == NULL) + p2 = ""; + snprintf(file, sizeof(file), "/%s", p2); + } + + p = strchr(host, ':'); + if (p != NULL) { + *p = '\0'; + port = atoi(p + 1); + + if (port == 0) + return (-1); + } else + port = HTTP_DEFAULTPORT; + + if (phost != NULL) + *phost = host; + if (pport != NULL) + *pport = port; + if (pfile != NULL) + *pfile = file; + + return (0); +} + +void +evhttp_fail(struct evhttp_request *req) +{ + evhttp_request_free(req); +} + +void +evhttp_write(int fd, short what, void *arg) +{ + struct evhttp_request *req = arg; + struct timeval tv; + int n; + + if (what == EV_TIMEOUT) { + evhttp_fail(req); + return; + } + + n = evbuffer_write(req->buffer, fd); + if (n == -1) { + event_warn("%s: evbuffer_write", __func__); + evhttp_fail(req); + return; + } + + if (n == 0) { + event_warnx("%s: write nothing\n", __func__); + evhttp_fail(req); + return; + } + + if (EVBUFFER_LENGTH(req->buffer) != 0) { + timerclear(&tv); + tv.tv_sec = HTTP_WRITE_TIMEOUT; + event_add(&req->ev, &tv); + return; + } + + /* Activate our call back */ + (*req->cb)(req, req->cb_arg); +} + +/* + * Reads data into a buffer structure until no more data + * can be read on the file descriptor or we have read all + * the data that we wanted to read. + * Execute callback when done. + */ + +void +evhttp_read(int fd, short what, void *arg) +{ + struct evhttp_request *req = arg; + struct timeval tv; + int n; + + if (what == EV_TIMEOUT) { + evhttp_fail(req); + return; + } + + n = evbuffer_read(req->buffer, fd, req->ntoread); + event_debug(("%s: got %d on %d\n", __func__, n, req->fd)); + + if (n == -1) { + event_warn("%s: evbuffer_read", __func__); + evhttp_fail(req); + } + + /* Adjust the amount of data that we have left to read */ + if (req->ntoread > 0) + req->ntoread -= n; + + if (n == 0 || req->ntoread == 0) { + (*req->cb)(req, req->cb_arg); + return; + } + + timerclear(&tv); + tv.tv_sec = HTTP_READ_TIMEOUT; + event_add(&req->ev, &tv); +} + +void +evhttp_write_requestcb(struct evhttp_request *req, void *arg) +{ + /* Restore the original callbacks */ + req->cb = req->save_cb; + req->cb_arg = req->save_cbarg; + + /* This is after writing the request to the server */ + + /* We are done writing our header and are now expecting the response */ + req->kind = EVHTTP_RESPONSE; + + evhttp_start_read(req); +} + +/* + * Clean up a connection object + */ + +void +evhttp_connection_free(struct evhttp_connection *evcon) +{ + event_del(&evcon->ev); + + if (evcon->fd != -1) + close(evcon->fd); + + if (evcon->address != NULL) + free(evcon->address); + + free(evcon); +} + +/* + * Call back for asynchronous connection attempt. + */ + +void +evhttp_connectioncb(int fd, short what, void *arg) +{ + struct evhttp_connection *evcon = arg; + int error; + socklen_t errsz = sizeof(error); + + if (what == EV_TIMEOUT) { + event_warnx("%s: connection timeout for \"%s:%d\" on %d\n", + __func__, evcon->address, evcon->port, evcon->fd); + goto cleanup; + } + + /* Check if the connection completed */ + if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, &error, + &errsz) == -1) { + event_warn("%s: getsockopt for \"%s:%d\" on %d", + __func__, evcon->address, evcon->port, evcon->fd); + goto cleanup; + } + + if (error) { + event_warnx("%s: connect failed for \"%s:%d\" on %d: %s\n", + __func__, evcon->address, evcon->port, evcon->fd, + strerror(error)); + goto cleanup; + } + + /* We are connected to the server now */ + event_debug(("%s: connected to \"%s:%d\" on %d\n", + __func__, evcon->address, evcon->port, evcon->fd)); + + /* We are turning the connection object over to the user */ + (*evcon->cb)(evcon, evcon->cb_arg); + return; + + cleanup: + /* Signal error to the user */ + (*evcon->cb)(NULL, evcon->cb_arg); + + evhttp_connection_free(evcon); + return; +} + +/* + * Check if we got a valid response code. + */ + +int +evhttp_valid_response_code(int code) +{ + if (code == 0) + return (0); + + return (1); +} + +/* Parses the status line of a web server */ + +int +evhttp_parse_response_line(struct evhttp_request *req, char *line) +{ + char *protocol; + char *number; + char *readable; + + protocol = strsep(&line, " "); + if (line == NULL) + return (-1); + number = strsep(&line, " "); + if (line == NULL) + return (-1); + readable = line; + + if (strcmp(protocol, "HTTP/1.0") == 0) { + req->major = 1; + req->minor = 0; + } else if (strcmp(protocol, "HTTP/1.1") == 0) { + req->major = 1; + req->minor = 1; + } else { + event_warnx("%s: bad protocol \"%s\" on %d\n", + __func__, protocol, req->fd); + return (-1); + } + + req->response_code = atoi(number); + if (!evhttp_valid_response_code(req->response_code)) { + event_warnx("%s: bad response code \"%s\" on %d\n", + __func__, number, req->fd); + return (-1); + } + + if ((req->response_code_line = strdup(readable)) == NULL) + event_err(1, "%s: strdup", __func__); + + return (0); +} + +/* Parse the first line of a HTTP request */ + +int +evhttp_parse_request_line(struct evhttp_request *req, char *line) +{ + char *method; + char *uri; + char *version; + + /* Parse the request line */ + method = strsep(&line, " "); + if (line == NULL) + return (-1); + uri = strsep(&line, " "); + if (line == NULL) + return (-1); + version = strsep(&line, " "); + if (line != NULL) + return (-1); + + /* First line */ + if (strcmp(method, "GET") == 0) { + req->type = EVHTTP_REQ_GET; + } else if (strcmp(method, "POST") == 0) { + req->type = EVHTTP_REQ_POST; + } else if (strcmp(method, "HEAD") == 0) { + req->type = EVHTTP_REQ_HEAD; + } else { + event_warnx("%s: bad method %s on fd %d\n", + __func__, method, req->fd); + return (-1); + } + + if (strcmp(version, "HTTP/1.0") == 0) { + req->major = 1; + req->minor = 0; + } else if (strcmp(version, "HTTP/1.1") == 0) { + req->major = 1; + req->minor = 1; + } else { + event_warnx("%s: bad version %s on %d\n", + __func__, version, req->fd); + return (-1); + } + + if ((req->uri = strdup(uri)) == NULL) { + event_warn("%s: strdup", __func__); + return (-1); + } + + return (0); +} + +const char * +evhttp_find_header(struct evkeyvalq *headers, const char *key) +{ + struct evkeyval *header; + + TAILQ_FOREACH(header, headers, next) { + if (strcasecmp(header->key, key) == 0) + return (header->value); + } + + return (NULL); +} + +void +evhttp_clear_headers(struct evkeyvalq *headers) +{ + struct evkeyval *header; + + for (header = TAILQ_FIRST(headers); + header != NULL; + header = TAILQ_FIRST(headers)) { + TAILQ_REMOVE(headers, header, next); + free(header->key); + free(header->value); + free(header); + } +} + +/* + * Returns 0, if the header was successfully removed. + * Returns -1, if the header could not be found. + */ + +int +evhttp_remove_header(struct evkeyvalq *headers, const char *key) +{ + struct evkeyval *header; + + TAILQ_FOREACH(header, headers, next) { + if (strcasecmp(header->key, key) == 0) + break; + } + + if (header == NULL) + return (-1); + + /* Free and remove the header that we found */ + TAILQ_REMOVE(headers, header, next); + free(header->key); + free(header->value); + free(header); + + return (0); +} + +int +evhttp_add_header(struct evkeyvalq *headers, const char *key, const char *value) +{ + struct evkeyval *header; + + header = calloc(1, sizeof(struct evkeyval)); + if (header == NULL) { + event_warn("%s: calloc", __func__); + return (-1); + } + if ((header->key = strdup(key)) == NULL) { + free(header); + event_warn("%s: strdup", __func__); + return (-1); + } + if ((header->value = strdup(value)) == NULL) { + free(header->key); + free(header); + event_warn("%s: strdup", __func__); + return (-1); + } + + TAILQ_INSERT_TAIL(headers, header, next); + + return (0); +} + +/* + * Parses header lines from a request or a response into the specified + * request object given an event buffer. + * + * Returns + * -1 on error + * 0 when we need to read more headers + * 1 when all headers have been read. + */ + +int +evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer) +{ + u_char *endp; + int done = 0; + + struct evkeyvalq* headers = req->input_headers; + while ((endp = evbuffer_find(buffer, "\r\n", 2)) != NULL) { + char *skey, *svalue; + + if (strncmp(EVBUFFER_DATA(buffer), "\r\n", 2) == 0) { + evbuffer_drain(buffer, 2); + /* Last header - Done */ + done = 1; + break; + } + + *endp = '\0'; + endp += 2; + + event_debug(("%s: Got: %s\n", __func__, EVBUFFER_DATA(buffer))); + + /* Processing of header lines */ + if (req->got_firstline == 0) { + switch (req->kind) { + case EVHTTP_REQUEST: + if (evhttp_parse_request_line(req, EVBUFFER_DATA(buffer)) == -1) + return (-1); + break; + case EVHTTP_RESPONSE: + if (evhttp_parse_response_line(req, EVBUFFER_DATA(buffer)) == -1) + return (-1); + break; + default: + return (-1); + } + req->got_firstline = 1; + } else { + /* Regular header */ + svalue = EVBUFFER_DATA(buffer); + skey = strsep(&svalue, ":"); + if (svalue == NULL) + return (-1); + + svalue += strspn(svalue, " "); + + if (evhttp_add_header(headers, skey, svalue) == -1) + return (-1); + } + + /* Move the uncompleted headers forward */ + evbuffer_drain(buffer, endp - EVBUFFER_DATA(buffer)); + } + + return (done); +} + +void +evhttp_get_body(struct evhttp_request *req) +{ + struct timeval tv; + const char *content_length; + const char *connection; + struct evkeyvalq *headers = req->input_headers; + + /* If this is a request without a body, then we are done */ + if (req->kind == EVHTTP_REQUEST && req->type != EVHTTP_REQ_POST) { + (*req->cb)(req, req->cb_arg); + return; + } + + content_length = evhttp_find_header(headers, "Content-Length"); + connection = evhttp_find_header(headers, "Connection"); + + if (content_length == NULL && connection == NULL) + req->ntoread = -1; + else if (content_length == NULL && + strcasecmp(connection, "Close") != 0) { + /* Bad combination, we don't know when it will end */ + event_warnx("%s: we got no content length, but the server" + " wants to keep the connection open: %s.\n", + __func__, connection); + evhttp_fail(req); + return; + } else if (content_length == NULL) + req->ntoread = -1; + else + req->ntoread = atoi(content_length); + + event_debug(("%s: bytes to read: %d (in buffer %d)\n", + __func__, req->ntoread, EVBUFFER_LENGTH(req->buffer))); + + if (req->ntoread > 0) + req->ntoread -= EVBUFFER_LENGTH(req->buffer); + + if (req->ntoread == 0) { + (*req->cb)(req, req->cb_arg); + return; + } + + event_set(&req->ev, req->fd, EV_READ, evhttp_read, req); + timerclear(&tv); + tv.tv_sec = HTTP_READ_TIMEOUT; + event_add(&req->ev, &tv); +} + +void +evhttp_read_header(int fd, short what, void *arg) +{ + struct timeval tv; + struct evhttp_request *req = arg; + int n, res; + + if (what == EV_TIMEOUT) { + event_warnx("%s: timeout on %d\n", __func__, fd); + evhttp_request_free(req); + return; + } + + n = evbuffer_read(req->buffer, fd, -1); + if (n == 0) { + event_warnx("%s: no more data on %d\n", __func__, fd); + evhttp_request_free(req); + return; + } + if (n == -1) { + event_warnx("%s: bad read on %d\n", __func__, fd); + evhttp_request_free(req); + return; + } + + res = evhttp_parse_lines(req, req->buffer); + if (res == -1) { + /* Error while reading, terminate */ + event_warnx("%s: bad header lines on %d\n", __func__, fd); + evhttp_request_free(req); + return; + } else if (res == 0) { + /* Need more header lines */ + timerclear(&tv); + tv.tv_sec = HTTP_READ_TIMEOUT; + event_add(&req->ev, &tv); + return; + } + + /* Done reading headers, do the real work */ + switch (req->kind) { + case EVHTTP_REQUEST: + event_debug(("%s: checking for post data on %d\n", + __func__, req->fd)); + evhttp_get_body(req); + break; + + case EVHTTP_RESPONSE: + event_debug(("%s: starting to read body for \"%s\" on %d\n", + __func__, req->remote_host, req->fd)); + evhttp_get_body(req); + break; + + default: + event_warnx("%s: bad header on %d\n", __func__, fd); + evhttp_fail(req); + break; + } +} + +/* + * Creates a TCP connection to the specified port and executes a callback + * when finished. Failure or sucess is indicate by the passed connection + * object. + * + * Although this interface accepts a hostname, it is intended to take + * only numeric hostnames so that non-blocking DNS resolution can + * happen elsewhere. + */ + +struct evhttp_connection * +evhttp_connect(const char *address, unsigned short port, + void (*cb)(struct evhttp_connection *, void *), void *cb_arg) +{ + struct evhttp_connection *evcon = NULL; + struct timeval tv; + + event_debug(("Attempting connection to %s:%d\n", address, port)); + + if ((evcon = calloc(1, sizeof(struct evhttp_connection))) == NULL) { + event_warn("%s: calloc failed", __func__); + goto error; + } + + evcon->fd = -1; + evcon->port = port; + if ((evcon->address = strdup(address)) == NULL) { + event_warn("%s: strdup failed", __func__); + goto error; + } + + /* Let the user name when something interesting happened */ + evcon->cb = cb; + evcon->cb_arg = cb_arg; + + /* Do async connection to HTTP server */ + if ((evcon->fd = make_socket(connect, address, port)) == -1) { + event_warn("%s: failed to connect to \"%s:%d\"", + __func__, address, port); + goto error; + } + + /* Set up a callback for successful connection setup */ + event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon); + timerclear(&tv); + tv.tv_sec = HTTP_CONNECT_TIMEOUT; + event_add(&evcon->ev, &tv); + + return (evcon); + + error: + if (evcon != NULL) + evhttp_connection_free(evcon); + return (NULL); +} + +/* + * Starts an HTTP request on the provided evhttp_connection object. + * + * In theory we might use this to queue requests on the connection + * object. + */ + +int +evhttp_make_request(struct evhttp_connection *evcon, + struct evhttp_request *req, + enum evhttp_cmd_type type, const char *uri) +{ + struct evbuffer *evbuf = evbuffer_new(); + + if (evbuf == NULL) + return (-1); + + /* We are making a request */ + req->fd = evcon->fd; + req->kind = EVHTTP_REQUEST; + req->type = type; + if (req->uri != NULL) + free(req->uri); + if ((req->uri = strdup(uri)) == NULL) + goto error; + + /* Set the protocol version if it is not supplied */ + if (!req->major && !req->minor) { + req->major = 1; + req->minor = 1; + } + + /* Create the header from the store arguments */ + evhttp_make_header(evbuf, req); + + /* Schedule the write */ + req->save_cb = req->cb; + req->save_cbarg = req->cb_arg; + + /* evbuf is being freed when the request finishes */ + evhttp_write_buffer(req, evbuf, evhttp_write_requestcb, NULL); + return (0); + + error: + evbuffer_free(evbuf); + return (-1); +} + +/* + * Reads data from file descriptor into request structure + * Request structure needs to be set up correctly. + */ + +void +evhttp_start_read(struct evhttp_request *req) +{ + struct timeval tv; + + /* Set up an event to read the headers */ + event_set(&req->ev, req->fd, EV_READ, evhttp_read_header, req); + + timerclear(&tv); + tv.tv_sec = HTTP_READ_TIMEOUT; + event_add(&req->ev, &tv); +} + +void +evhttp_send_done(struct evhttp_request *req, void *arg) +{ + evhttp_request_free(req); +} + +/* + * Returns an error page. + */ + +void +evhttp_send_error(struct evhttp_request *req, int error, const char *reason) +{ + char *fmt = "\n" + "%d %s\n" + "\n" + "

Method Not Implemented

\n" + "Invalid method in request

\n" + "\n"; + + struct evbuffer *buf = evbuffer_new(); + + evhttp_response_code(req, error, reason); + + evbuffer_add_printf(buf, fmt, error, reason); + + evhttp_send_page(req, buf); + + evbuffer_free(buf); +} + +/* Requires that headers and response code are already set up */ + +static __inline void +evhttp_send(struct evhttp_request *req, struct evbuffer *databuf) +{ + struct evbuffer *buf = req->buffer; + + evbuffer_drain(buf, -1); + + /* Adds headers to the response */ + evhttp_make_header(buf, req); + + evbuffer_add_buffer(buf, databuf); + + evhttp_write_buffer(req, buf, evhttp_send_done, NULL); +} + +void +evhttp_send_reply(struct evhttp_request *req, int code, const char *reason, + struct evbuffer *databuf) +{ + evhttp_response_code(req, code, reason); + + evhttp_send(req, databuf); +} + +void +evhttp_response_code(struct evhttp_request *req, int code, const char *reason) +{ + req->kind = EVHTTP_RESPONSE; + req->response_code = code; + if (req->response_code_line != NULL) + free(req->response_code_line); + req->response_code_line = strdup(reason); +} + +void +evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf) +{ + if (req->kind != EVHTTP_RESPONSE) + evhttp_response_code(req, 200, "OK"); + + evhttp_clear_headers(req->output_headers); + evhttp_add_header(req->output_headers, "Content-Type", "text/html"); + evhttp_add_header(req->output_headers, "Connection", "close"); + + evhttp_send(req, databuf); +} + +/* + * Helper function to parse out arguments in a query. + * The arguments are separated by key and value. + */ + +void +evhttp_parse_query(const char *uri, struct evkeyvalq *headers) +{ + char *line; + char *argument; + char *p; + + TAILQ_INIT(headers); + + /* No arguments - we are done */ + if (strchr(uri, '?') == NULL) + return; + + if ((line = strdup(uri)) == NULL) + event_err(1, "%s: strdup", __func__); + + + argument = line; + + /* We already know that there has to be a ? */ + strsep(&argument, "?"); + + p = argument; + while (p != NULL && *p != '\0') { + char *key, *value; + argument = strsep(&p, "&"); + + value = argument; + key = strsep(&value, "="); + if (value == NULL) + goto error; + + event_warnx("Got: %s -> %s\n", key, value); + evhttp_add_header(headers, key, value); + } + + error: + free(line); +} + +void +evhttp_handle_request(struct evhttp_request *req, void *arg) +{ + struct evhttp *http = arg; + struct evhttp_cb *cb; + + /* Test for different URLs */ + TAILQ_FOREACH(cb, &http->callbacks, next) { + int res; + char *p = strchr(req->uri, '?'); + if (p == NULL) + res = strcmp(cb->what, req->uri) == 0; + else + res = strncmp(cb->what, req->uri, + (size_t)(p - req->uri)) == 0; + if (res) { + (*cb->cb)(req, cb->cbarg); + return; + } + } + + /* Generic call back */ + if (http->gencb) { + (*http->gencb)(req, http->gencbarg); + return; + } else { + /* We need to send a 404 here */ + char *fmt = "" + "404 Not Found" + "" + "

Not Found

" + "

The requested URL %s was not found on this server.

" + "\n"; + + char *escaped_html = evhttp_htmlescape(req->uri); + struct evbuffer *buf = evbuffer_new(); + + evhttp_response_code(req, HTTP_NOTFOUND, "Not Found"); + + evbuffer_add_printf(buf, fmt, escaped_html); + + free(escaped_html); + + evhttp_send_page(req, buf); + + evbuffer_free(buf); + } +} + +static void +accept_socket(int fd, short what, void *arg) +{ + struct evhttp *http = arg; + struct sockaddr_storage ss; + socklen_t addrlen = sizeof(ss); + int nfd; + + if ((nfd = accept(fd, (struct sockaddr *)&ss, &addrlen)) == -1) { + event_warn("%s: bad accept", __func__); + return; + } + + evhttp_get_request(nfd, (struct sockaddr *)&ss, addrlen, + evhttp_handle_request, http); +} + +static int +bind_socket(struct evhttp *http, const char *address, u_short port) +{ + struct event *ev = &http->bind_ev; + int fd; + + if ((fd = make_socket(bind, address, port)) == -1) + return (-1); + + if (listen(fd, 10) == -1) { + event_warn("%s: listen", __func__); + return (-1); + } + + /* Schedule the socket for accepting */ + event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http); + event_add(ev, NULL); + + event_debug(("Bound to port %d - Awaiting connections ... ", port)); + + return (0); +} + +/* + * Start a web server on the specified address and port. + */ + +struct evhttp * +evhttp_start(const char *address, u_short port) +{ + struct evhttp *http; + + if ((http = calloc(1, sizeof(struct evhttp))) == NULL) { + event_warn("%s: calloc", __func__); + return (NULL); + } + + TAILQ_INIT(&http->callbacks); + + if (bind_socket(http, address, port) == -1) { + free(http); + return (NULL); + } + + return (http); +} + +void +evhttp_free(struct evhttp* http) +{ + struct evhttp_cb *http_cb; + int fd = http->bind_ev.ev_fd; + + /* Remove the accepting part */ + event_del(&http->bind_ev); + close(fd); + + while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) { + TAILQ_REMOVE(&http->callbacks, http_cb, next); + free(http_cb->what); + free(http_cb); + } + + free(http); +} + +void +evhttp_set_cb(struct evhttp *http, const char *uri, + void (*cb)(struct evhttp_request *, void *), void *cbarg) +{ + struct evhttp_cb *http_cb; + + if ((http_cb = calloc(1, sizeof(struct evhttp_cb))) == NULL) + event_err(1, "%s: calloc", __func__); + + http_cb->what = strdup(uri); + http_cb->cb = cb; + http_cb->cbarg = cbarg; + + TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next); +} + +void +evhttp_set_gencb(struct evhttp *http, + void (*cb)(struct evhttp_request *, void *), void *cbarg) +{ + http->gencb = cb; + http->gencbarg = cbarg; +} + +/* + * Request related functions + */ + +struct evhttp_request * +evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg) +{ + struct evhttp_request *req = NULL; + + /* Allocate request structure */ + if ((req = calloc(1, sizeof(struct evhttp_request))) == NULL) { + event_warn("%s: calloc", __func__); + goto error; + } + + req->fd = -1; + req->kind = EVHTTP_RESPONSE; + req->input_headers = calloc(1, sizeof(struct evkeyvalq)); + if (req->input_headers == NULL) { + event_warn("%s: calloc", __func__); + goto error; + } + TAILQ_INIT(req->input_headers); + + req->output_headers = calloc(1, sizeof(struct evkeyvalq)); + if (req->output_headers == NULL) { + event_warn("%s: calloc", __func__); + goto error; + } + TAILQ_INIT(req->output_headers); + + req->buffer = evbuffer_new(); + + req->cb = cb; + req->cb_arg = arg; + + return (req); + + error: + if (req != NULL) + evhttp_request_free(req); + return (NULL); +} + +void +evhttp_request_free(struct evhttp_request *req) +{ + if (req->fd != -1) + close(req->fd); + if (req->remote_host != NULL) + free(req->remote_host); + if (req->uri != NULL) + free(req->uri); + if (req->response_code_line != NULL) + free(req->response_code_line); + + if (event_initialized(&req->ev)) + event_del(&req->ev); + + evhttp_clear_headers(req->input_headers); + free(req->input_headers); + + evhttp_clear_headers(req->output_headers); + free(req->output_headers); + + if (req->buffer != NULL) + evbuffer_free(req->buffer); + free(req); +} + +/* + * Allows for inspection of the request URI + */ + +const char * +evhttp_request_uri(struct evhttp_request *req) { + if (req->uri == NULL) + event_debug(("%s: request %p has no uri\n", req)); + return (req->uri); +} + +/* + * Takes a file descriptor to read a request from. + * The callback is executed once the whole request has been read. + */ + +void +evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen, + void (*cb)(struct evhttp_request *, void *), void *arg) +{ + struct evhttp_request *req; + char *hostname, *portname; + + name_from_addr(sa, salen, &hostname, &portname); + event_debug(("%s: new request from %s:%s on %d\n", + __func__, hostname, portname, fd)); + + if ((req = evhttp_request_new(cb, arg)) == NULL) + return; + + req->fd = fd; + req->kind = EVHTTP_REQUEST; + + if ((req->remote_host = strdup(hostname)) == NULL) + event_err(1, "%s: strdup", __func__); + req->remote_port = atoi(portname); + + evhttp_start_read(req); +} + + +/* + * Network helper functions that we do not want to export to the rest of + * the world. + */ + +static struct addrinfo * +addr_from_name(char *address) +{ + struct addrinfo ai, *aitop; + + memset(&ai, 0, sizeof (ai)); + ai.ai_family = AF_INET; + ai.ai_socktype = SOCK_RAW; + ai.ai_flags = 0; + if (getaddrinfo(address, NULL, &ai, &aitop) != 0) { + event_warn("getaddrinfo"); + return (NULL); + } + + return (aitop); +} + +static void +name_from_addr(struct sockaddr *sa, socklen_t salen, + char **phost, char **pport) +{ + static char ntop[NI_MAXHOST]; + static char strport[NI_MAXSERV]; + + if (getnameinfo(sa, salen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) + event_err(1, "getnameinfo failed"); + + *phost = ntop; + *pport = strport; +} + +/* Either connect or bind */ + +static int +make_socket_ai(int (*f)(int, const struct sockaddr *, socklen_t), + struct addrinfo *ai) +{ + struct linger linger; + int fd, on = 1; + int serrno; + + /* Create listen socket */ + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + event_warn("socket"); + return (-1); + } + + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + event_warn("fcntl(O_NONBLOCK)"); + goto out; + } + + if (fcntl(fd, F_SETFD, 1) == -1) { + event_warn("fcntl(F_SETFD)"); + goto out; + } + + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); + + if ((f)(fd, ai->ai_addr, ai->ai_addrlen) == -1) { + if (errno != EINPROGRESS) { + goto out; + } + } + + return (fd); + + out: + serrno = errno; + close(fd); + errno = serrno; + return (-1); +} + +static int +make_socket(int (*f)(int, const struct sockaddr *, socklen_t), + const char *address, short port) +{ + struct addrinfo ai, *aitop; + char strport[NI_MAXSERV]; + int fd; + + memset(&ai, 0, sizeof (ai)); + ai.ai_family = AF_INET; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = f != connect ? AI_PASSIVE : 0; + snprintf(strport, sizeof (strport), "%d", port); + if (getaddrinfo(address, strport, &ai, &aitop) != 0) { + event_warn("getaddrinfo"); + return (-1); + } + + fd = make_socket_ai(f, aitop); + + freeaddrinfo(aitop); + + return (fd); +} diff --git a/varnish-cache/contrib/libevent/http.h b/varnish-cache/contrib/libevent/http.h new file mode 100644 index 00000000..29e87b20 --- /dev/null +++ b/varnish-cache/contrib/libevent/http.h @@ -0,0 +1,125 @@ +/* + * Copyright 2001 Niels Provos + * All rights reserved. + * + * This header file contains definitions for dealing with HTTP requests + * that are internal to libevent. As user of the library, you should not + * need to know about these. + */ + +#ifndef _HTTP_H_ +#define _HTTP_H_ + +#define HTTP_CONNECT_TIMEOUT 45 +#define HTTP_WRITE_TIMEOUT 50 +#define HTTP_READ_TIMEOUT 50 + +#define HTTP_PREFIX "http://" +#define HTTP_DEFAULTPORT 80 + +struct evbuffer; +struct addrinfo; + +/* A stupid connection object - maybe make this a bufferevent later */ + +struct evhttp_connection { + int fd; + struct event ev; + + char *address; + u_short port; + + void (*cb)(struct evhttp_connection *, void *); + void *cb_arg; +}; + +enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE }; + +struct evhttp_request { + struct evkeyvalq *input_headers; + struct evkeyvalq *output_headers; + + char *remote_host; + u_short remote_port; + + enum evhttp_request_kind kind; + enum evhttp_cmd_type type; + + char *uri; /* uri after HTTP request was parsed */ + + char major; /* HTTP Major number */ + char minor; /* HTTP Minor number */ + + int got_firstline; + int response_code; /* HTTP Response code */ + char *response_code_line; /* Readable response */ + + int fd; + + struct event ev; + + struct evbuffer *buffer; + int ntoread; + + /* Callback */ + void (*cb)(struct evhttp_request *, void *); + void *cb_arg; + + void (*save_cb)(struct evhttp_request *, void *); + void *save_cbarg; +}; + +struct evhttp_cb { + TAILQ_ENTRY(evhttp_cb) next; + + char *what; + + void (*cb)(struct evhttp_request *req, void *); + void *cbarg; +}; + +struct evhttp { + struct event bind_ev; + + TAILQ_HEAD(httpcbq, evhttp_cb) callbacks; + + void (*gencb)(struct evhttp_request *req, void *); + void *gencbarg; +}; + +void evhttp_get_request(int, struct sockaddr *, socklen_t, + void (*)(struct evhttp_request *, void *), void *); + +/* + * Starts a connection to the specified address and invokes the callback + * if everything is fine. + */ +struct evhttp_connection *evhttp_connect( + const char *address, unsigned short port, + void (*cb)(struct evhttp_connection *, void *), void *cb_arg); + +/* Frees an http connection */ +void evhttp_connection_free(struct evhttp_connection *evcon); + +int evhttp_make_request(struct evhttp_connection *evcon, + struct evhttp_request *req, + enum evhttp_cmd_type type, const char *uri); + +int evhttp_hostportfile(char *, char **, u_short *, char **); + +int evhttp_parse_lines(struct evhttp_request *, struct evbuffer*); + +void evhttp_start_read(struct evhttp_request *); +void evhttp_read_header(int, short, void *); +void evhttp_make_header(struct evbuffer *, struct evhttp_request *); + +void evhttp_form_response(struct evbuffer *, struct evhttp_request *); +void evhttp_write_buffer(struct evhttp_request *, struct evbuffer *, + void (*)(struct evhttp_request *, void *), void *); + +/* response sending HTML the data in the buffer */ +void evhttp_response_code(struct evhttp_request *, int, const char *); +void evhttp_send_page(struct evhttp_request *, struct evbuffer *); +void evhttp_fail(struct evhttp_request *); + +#endif /* _HTTP_H */ diff --git a/varnish-cache/contrib/libevent/kqueue.c b/varnish-cache/contrib/libevent/kqueue.c index 3bc6c2db..a92315fe 100644 --- a/varnish-cache/contrib/libevent/kqueue.c +++ b/varnish-cache/contrib/libevent/kqueue.c @@ -75,6 +75,7 @@ int kq_del (void *, struct event *); int kq_recalc (struct event_base *, void *, int); int kq_dispatch (struct event_base *, void *, struct timeval *); int kq_insert (struct kqop *, struct kevent *); +void kq_dealloc (void *); const struct eventop kqops = { "kqueue", @@ -82,7 +83,8 @@ const struct eventop kqops = { kq_add, kq_del, kq_recalc, - kq_dispatch + kq_dispatch, + kq_dealloc }; void * @@ -266,10 +268,8 @@ kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) if (!which) continue; - if (!(ev->ev_events & EV_PERSIST)) { - ev->ev_flags &= ~EVLIST_X_KQINKERNEL; + if (!(ev->ev_events & EV_PERSIST)) event_del(ev); - } event_active(ev, which, ev->ev_events & EV_SIGNAL ? events[i].data : 1); @@ -396,3 +396,18 @@ kq_del(void *arg, struct event *ev) return (0); } + +void +kq_dealloc(void *arg) +{ + struct kqop *kqop = arg; + + if (kqop->changes) + free(kqop->changes); + if (kqop->events) + free(kqop->events); + if (kqop->kq) + close(kqop->kq); + memset(kqop, 0, sizeof(struct kqop)); + free(kqop); +} diff --git a/varnish-cache/contrib/libevent/log.h b/varnish-cache/contrib/libevent/log.h index e19d8a06..1f843cf9 100644 --- a/varnish-cache/contrib/libevent/log.h +++ b/varnish-cache/contrib/libevent/log.h @@ -33,11 +33,11 @@ void event_errx(int eval, const char *fmt, ...); void event_warnx(const char *fmt, ...); void event_msgx(const char *fmt, ...); void _event_debugx(const char *fmt, ...); -#undef USE_DEBUG + #ifdef USE_DEBUG #define event_debug(x) _event_debugx x #else -#define event_debug(x) +#define event_debug(x) do {;} while (0) #endif #endif diff --git a/varnish-cache/contrib/libevent/poll.c b/varnish-cache/contrib/libevent/poll.c index da35b20a..f05819e0 100644 --- a/varnish-cache/contrib/libevent/poll.c +++ b/varnish-cache/contrib/libevent/poll.c @@ -74,14 +74,16 @@ int poll_add (void *, struct event *); int poll_del (void *, struct event *); int poll_recalc (struct event_base *, void *, int); int poll_dispatch (struct event_base *, void *, struct timeval *); +void poll_dealloc (void *); -struct eventop pollops = { +const struct eventop pollops = { "poll", poll_init, poll_add, poll_del, poll_recalc, - poll_dispatch + poll_dispatch, + poll_dealloc }; void * @@ -89,11 +91,11 @@ poll_init(void) { struct pollop *pollop; - /* Disable kqueue when this environment variable is set */ + /* Disable poll when this environment variable is set */ if (getenv("EVENT_NOPOLL")) return (NULL); - if (!(pollop = calloc(1, sizeof(struct pollop)))) + if (!(pollop = calloc(1, sizeof(struct pollop)))) return (NULL); evsignal_init(&pollop->evsigmask); @@ -182,7 +184,7 @@ poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) return (0); for (i = 0; i < nfds; i++) { - int what = pop->event_set[i].revents; + int what = pop->event_set[i].revents; struct event *r_ev = NULL, *w_ev = NULL; if (!what) continue; @@ -355,3 +357,21 @@ poll_del(void *arg, struct event *ev) poll_check_ok(pop); return (0); } + +void +poll_dealloc(void *arg) +{ + struct pollop *pop = arg; + + if (pop->event_set) + free(pop->event_set); + if (pop->event_r_back) + free(pop->event_r_back); + if (pop->event_w_back) + free(pop->event_w_back); + if (pop->idxplus1_by_fd) + free(pop->idxplus1_by_fd); + + memset(pop, 0, sizeof(struct pollop)); + free(pop); +} diff --git a/varnish-cache/contrib/libevent/rtsig.c b/varnish-cache/contrib/libevent/rtsig.c index 32a782cd..29aade69 100644 --- a/varnish-cache/contrib/libevent/rtsig.c +++ b/varnish-cache/contrib/libevent/rtsig.c @@ -1,3 +1,152 @@ +/* + * Copyright (c) 2006 Mathew Mills + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Meta-level comments: You know that a kernel interface is wrong if + * supporting it requires three times more code than any of the other + * kernel interfaces supported in libevent. Niels - 2006-02-22 + */ +/** + + "RTSIG" is a shorthand for using O_ASYNC to make descriptors send + signals when readable/writable and to use POSIX real-time signals + witch are queued unlike normal signals. At first blush this may + seem like a alternative to epoll, but a number of problems arise + when attempting to build an eventloop entirely out of rtsig. + Still, we can use rtsig in combination with poll() to + provide an eventloop that allows for many thousands of sockets + without huge overheads implicit with using select() or poll() + alone. epoll and kqueue are far superior to rtsig and should be + used where available, but rtsig has been in standard Linux kernels + for a long time and have a huge installation base. epoll requires + special patches for 2.4 kernels and 2.6 kernels are not yet nearly + so ubiquitous. + + rtsig problems: + - O_ASYNC mechanisms work only on sockets - not pipes or tty's + + - O_ASYNC signals are edge-triggered, POLLIN on packet arriving + or socket close; POLLOUT when a socket transitions from + non-writable to writable. Being edge-triggered means the + event-handler callbacks must transition the level ( reading + completely the socket buffer contents ) or it will be unable to + reliably receive notification again. + + - rtsig implementations must be intimately involved in how a + process dispatches signals. + + - delivering signals per-event can be expensive, CPU-wise, but + sigtimedwait() blocks on signals only and means non-sockets + cannot be serviced. + + Theory of operation: + This libevent module uses rtsig to allow us to manage a set of + poll-event descriptors. We can drop uninteresting fd's from the + pollset if the fd will send a signal when it becomes interesting + again. + + poll() offers us level-triggering and, when we have verified the + level of a socket, we can trust the edge-trigger nature of the + ASYNC signal. + + As an eventloop we must poll for external events but leverage + kernel functionality to sleep between events ( until the loop's + next scheduled timed event ). + + If we are polling on any non-sockets then we simply have no choice + about blocking on the poll() call. If we blocked on the + sigtimedwait() call as rtsig papers recommend we will not wake on + non-socket state transitions. As part of libevent, this module + must support non-socket polling. + + Many applications, however, do not need to poll on non-sockets and + so this module should be able to optimize this case by using + sigtimedwait(). For this reason this module can actually trigger + events in each of three different ways: + - poll() returning ready events from descriptors in the pollset + + - real-time signals dequeued via sigtimedwait() + + - real-time signals that call an installed signal handler which in + turn writes the contents of siginfo to one end of a socketpair + DGRAM socket. The other end of the socket is always in the + pollset so poll will be guaranteed to return even if the signal is + received before entering poll(). + + non-socket descriptors force us to block on the poll() for the + duration of a dispatch. In this case we unblock (w/ sigprocmask) + the managed signals just before polling. Each managed signal is + handled by signal_handler() which send()'s the contents of siginfo + over the socketpair. Otherwise, we call poll() with a timeout of + 0ms so it checks the levels of the fd's in the pollset and returns + immediately. Any fd that is a socket and has no active state is + removed from the pollset for the next pass -- we will rely on + getting a signal for events on these fd's. + + The receiving end of the siginfo socketpair is in the pollset + (permanently) so if we are polling on non-sockets, the delivery of + signals immediately following sigprocmask( SIG_UNBLOCK...) will + result in a readable op->signal_recv_fd which ensures the poll() + will return immediately. If the poll() call is blocking and a + signal arrives ( possibly a real-time signal from a socket not in + the pollset ) its handler will write the data to the socketpair + and interrupt the poll(). + + After every poll call we attempt a non-blocking recv from the + signal_recv_fd and continue to recv and dispatch the events until + recv indicates the socket buffer is empty. + + One might raise concerns about receiving event activations from + both poll() and from the rtsig data in the signal_recv_fd. + Fortunately, libevent is already structured for event coalescing, + so this issue is mitigated ( though we do some work twice for the + same event making us less efficient ). I suspect that the cost of + turning off the O_ASYNC flag on fd's in the pollset is more + expensive than handling some events twice. Looking at the + kernel's source code for setting O_ASYNC, it looks like it takes a + global kernel lock... + + After a poll and recv-loop for the signal_recv_fd, we finally do a + sigtimedwait(). sigtimedwait will only block if we haven't + blocked in poll() and we have not enqueued events from either the + poll or the recv-loop. Because sigtimedwait blocks all signals + that are not in the set of signals to be dequeued, we need to + dequeue almost all signals and make sure we dispatch them + correctly. We dequeue any signal that is not blocked as well as + all libevent-managed signals. If we get a signal that is not + managed by libevent we lookup the sigaction for the specific + signal and call that function ourselves. + + Finally, I should mention that getting a SIGIO signal indicates + that the rtsig buffer has overflowed and we have lost events. + This forces us to add _every_ descriptor to the pollset to recover. + +*/ + + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -20,152 +169,465 @@ #include #include #include -#ifndef HAVE_WORKING_RTSIG -#include -#endif +#include #include - -#define EVLIST_X_NORT 0x1000 /* Skip RT signals (internal) */ +#include #include "event.h" +#include "event-internal.h" #include "log.h" extern struct event_list signalqueue; -struct rtsigop { - sigset_t sigs; - struct pollfd *poll; - struct event **toev; - int cur, max, total; -#ifndef HAVE_WORKING_RTSIG - int pollmode; +#include +#ifndef __NR_gettid +#define gettid() getpid() +#else + +#if ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3))) +_syscall0(pid_t,gettid) #endif + +#endif + +#define EVLIST_NONSOCK 0x1000 /* event is for a non-socket file-descriptor */ +#define EVLIST_DONTDEL 0x2000 /* event should always be in the pollset */ +#define MAXBUFFERSIZE (1024 * 1024 * 2) /* max socketbuffer for signal-spair */ +#define INIT_MAX 16 /* init/min # of fd positions in our pollset */ + +static int signal_send_fd[_NSIG]; /* the globalend of the signal socketpair */ +static int trouble[_NSIG]; /* 1 when signal-handler cant send to signal_send_fd */ + +struct rtdata; +TAILQ_HEAD(rtdata_list, rtdata); + +struct rtsigop { + sigset_t sigs; /* signal mask for all _managed_ signals */ + struct pollfd *poll; /* poll structures */ + struct rtdata **ptodat; /* map poll_position to rtdata */ + int cur; /* cur # fd's in a poll set */ + int max; /* max # fd's in a poll set, start at 16 and grow as needed */ + int total; /* count of fd's we are watching now */ + int signo; /* the signo we use for ASYNC fd notifications */ + int nonsock; /* number of non-socket fd's we are watching */ + int highestfd; /* highest fd accomodated by fdtodat */ + struct rtdata_list **fdtodat; /* map fd to rtdata ( and thus to event ) */ + int signal_recv_fd; /* recv side of the signal_send_fd */ + int signal_send_fd; /* recv side of the signal_send_fd */ + struct event sigfdev; /* our own event structure for the signal fd */ +}; + +struct rtdata { + /* rtdata holds rtsig-private state on each event */ + TAILQ_ENTRY (rtdata) next; + struct event *ev; + int poll_position; +}; + +void *rtsig_init(void); +int rtsig_add(void *, struct event *); +int rtsig_del(void *, struct event *); +int rtsig_recalc(struct event_base *, void *, int); +int rtsig_dispatch(struct event_base *, void *, struct timeval *); + +struct eventop rtsigops = { + "rtsig", + rtsig_init, + rtsig_add, + rtsig_del, + rtsig_recalc, + rtsig_dispatch +}; + +static void +signal_handler(int sig, siginfo_t *info, void *ctx) +{ + /* + * the signal handler for all libevent-managed signals only + * used if we need to do a blocking poll() call due to + * non-socket fd's in the pollset. + */ + + siginfo_t *i = info; + siginfo_t i_local; + + if (trouble[sig - 1]) { + i_local.si_signo = SIGIO; + i_local.si_errno = 0; + i_local.si_code = 0; + i = &i_local; + trouble[sig - 1] = 0; + } + + if (send(signal_send_fd[sig - 1], i, sizeof(*i), + MSG_DONTWAIT|MSG_NOSIGNAL) == -1) + trouble[sig - 1] = 1; +} + +static void +donothing(int fd, short event, void *arg) +{ + /* + * callback for our signal_recv_fd event structure + * we don't want to act on these events, we just want to wake the poll() + */ }; -#define INIT_MAX 16 +static void +signotset(sigset_t *set) +{ + int i, l; + l = sizeof(*set) / 4; + for (i = 0; i < l; i++) { + ((unsigned *)set)[i] = ~((unsigned *)set)[i]; + } +} + +/* The next three functions manage our private data about each event struct */ static int -poll_add(struct rtsigop *op, struct event *ev) +grow_fdset(struct rtsigop *op, int newhigh) { - struct pollfd *pfd; - - if (op->poll == NULL) return 0; - - if (op->cur == op->max) { - void *p; - - p = realloc(op->poll, sizeof(*op->poll) * (op->max << 1)); - if (!p) { - errno = ENOMEM; - return -1; - } - op->poll = p; - p = realloc(op->toev, sizeof(*op->toev) * (op->max << 1)); - if (!p) { - op->poll = realloc(op->poll, sizeof(*op->poll) * op->max); - errno = ENOMEM; - return -1; - } - op->toev = p; - op->max <<= 1; - } - - pfd = &op->poll[op->cur]; - pfd->fd = ev->ev_fd; - pfd->events = 0; - if (ev->ev_events & EV_READ) pfd->events |= POLLIN; - if (ev->ev_events & EV_WRITE) pfd->events |= POLLOUT; - pfd->revents = 0; - - op->toev[op->cur] = ev; - op->cur++; - - return 0; + /* + * grow the fd -> rtdata array because we have encountered a + * new fd too high to fit in the existing array + */ + + struct rtdata_list **p; + struct rtdata_list *datset; + int i,x; + int newcnt = (newhigh + 1) << 1; + + if (newhigh <= op->highestfd) + return (0); + + p = op->fdtodat; + p = realloc(op->fdtodat, sizeof(struct rtdata_list *) * newcnt); + if (p == NULL) + return (-1); + op->fdtodat = p; + + datset = calloc(newcnt - (op->highestfd + 1), + sizeof(struct rtdata_list)); + if (datset == NULL) + return (-1); + + for (i = op->highestfd + 1, x = 0; i < newcnt; i++, x++) { + op->fdtodat[i] = &(datset[x]); + TAILQ_INIT(op->fdtodat[i]); + } + + op->highestfd = newcnt - 1; + return (0); +} + +static struct rtdata * +ev2dat(struct rtsigop *op, struct event *ev, int create) +{ + /* + * given an event struct, find the dat structure that + * corresponds to it if create is non-zero and the rtdata + * structure does not exist, create it return NULL if not + * found + */ + + int found = 0; + int fd = ev->ev_fd; + struct rtdata *ret = NULL; + + if (op->highestfd < fd && create) + if (grow_fdset(op, fd) == -1) + return (NULL); + + TAILQ_FOREACH(ret, op->fdtodat[fd], next) { + if (ret->ev == ev) { + found = 1; + break; + } + } + + if (!found) { + if (!create) + return (NULL); + + ret = calloc(1, sizeof(struct rtdata)); + if (ret == NULL) + return (NULL); + ret->ev = ev; + ret->poll_position = -1; + TAILQ_INSERT_TAIL(op->fdtodat[fd], ret, next); + } + + return (ret); +} + +static void +dat_del(struct rtsigop *op, struct rtdata *dat) +{ + /* + * delete our private notes about a given event struct + * called from rtsig_del() only + */ + int fd; + if (dat == NULL) + return; + fd = dat->ev->ev_fd; + + TAILQ_REMOVE(op->fdtodat[fd], dat, next); + memset(dat, 0, sizeof(*dat)); + free(dat); +} + + +static void +set_sigaction(int sig) +{ + /* + * set the standard handler for any libevent-managed signal, + * including the rtsig used for O_ASYNC notifications + */ + struct sigaction act; + + act.sa_flags = SA_RESTART | SA_SIGINFO; + sigfillset(&(act.sa_mask)); + act.sa_sigaction = &signal_handler; + sigaction(sig, &act, NULL); +} + +static int +find_rt_signal() +{ + /* find an unused rtsignal */ + struct sigaction act; + int sig = SIGRTMIN; + + while (sig <= SIGRTMAX) { + if (sigaction(sig, NULL, &act) != 0) { + if (errno == EINTR) + continue; + } else { + if (act.sa_flags & SA_SIGINFO) { + if (act.sa_sigaction == NULL) + return (sig); + } else { + if (act.sa_handler == SIG_DFL) + return (sig); + } + } + sig++; + } + return (0); +} + +/* + * the next three functions manage our pollset and the memory management for + * fd -> rtdata -> event -> poll_position maps + */ + +static int +poll_add(struct rtsigop *op, struct event *ev, struct rtdata *dat) +{ + struct pollfd *pfd; + int newmax = op->max << 1; + int pp; + + if (op->poll == NULL) + return (0); + + if (dat == NULL) + dat = ev2dat(op, ev, 0); + + if (dat == NULL) + return (0); + + pp = dat->poll_position; + + if (pp != -1) { + pfd = &op->poll[pp]; + if (ev->ev_events & EV_READ) + pfd->events |= POLLIN; + + if (ev->ev_events & EV_WRITE) + pfd->events |= POLLOUT; + + return (0); + } + + if (op->cur == op->max) { + void *p = realloc(op->poll, sizeof(*op->poll) * newmax); + if (p == NULL) { + errno = ENOMEM; + return (-1); + } + op->poll = p; + + p = realloc(op->ptodat, sizeof(*op->ptodat) * newmax); + if (p == NULL) { + /* shrink the pollset back down */ + op->poll = realloc(op->poll, + sizeof(*op->poll) * op->max); + errno = ENOMEM; + return (-1); + } + op->ptodat = p; + op->max = newmax; + } + + pfd = &op->poll[op->cur]; + pfd->fd = ev->ev_fd; + pfd->revents = 0; + pfd->events = 0; + + if (ev->ev_events & EV_READ) + pfd->events |= POLLIN; + + if (ev->ev_events & EV_WRITE) + pfd->events |= POLLOUT; + + op->ptodat[op->cur] = dat; + dat->poll_position = op->cur; + op->cur++; + + return (0); } static void poll_free(struct rtsigop *op, int n) { - if (op->poll == NULL) return; - - op->cur--; - if (n < op->cur) { - memcpy(&op->poll[n], &op->poll[op->cur], sizeof(*op->poll)); - op->toev[n] = op->toev[op->cur]; - } - if (op->max > INIT_MAX && op->cur < op->max >> 1) { - op->max >>= 1; - op->poll = realloc(op->poll, sizeof(*op->poll) * op->max); - op->toev = realloc(op->toev, sizeof(*op->toev) * op->max); - } + if (op->poll == NULL) + return; + + op->cur--; + + if (n < op->cur) { + memcpy(&op->poll[n], &op->poll[op->cur], sizeof(*op->poll)); + op->ptodat[n] = op->ptodat[op->cur]; + op->ptodat[n]->poll_position = n; + } + + + /* less then half the max in use causes us to shrink */ + if (op->max > INIT_MAX && op->cur < op->max >> 1) { + op->max >>= 1; + op->poll = realloc(op->poll, sizeof(*op->poll) * op->max); + op->ptodat = realloc(op->ptodat, sizeof(*op->ptodat) * op->max); + } } static void -poll_remove(struct rtsigop *op, struct event *ev) +poll_remove(struct rtsigop *op, struct event *ev, struct rtdata *dat) { - int i; - - for (i = 0; i < op->cur; i++) { - if (op->toev[i] == ev) { - poll_free(op, i); - break; - } - } + int pp; + if (dat == NULL) + dat = ev2dat(op, ev, 0); + + if (dat == NULL) return; + + pp = dat->poll_position; + if (pp != -1) { + poll_free(op, pp); + dat->poll_position = -1; + } } static void activate(struct event *ev, int flags) { - if (!(ev->ev_events & EV_PERSIST)) event_del(ev); - event_active(ev, flags, 1); + /* activate an event, possibly removing one-shot events */ + if (!(ev->ev_events & EV_PERSIST)) + event_del(ev); + event_active(ev, flags, 1); } -void *rtsig_init(void); -int rtsig_add(void *, struct event *); -int rtsig_del(void *, struct event *); -int rtsig_recalc(struct event_base *, void *, int); -int rtsig_dispatch(struct event_base *, void *, struct timeval *); - -struct eventop rtsigops = { - "rtsig", - rtsig_init, - rtsig_add, - rtsig_del, - rtsig_recalc, - rtsig_dispatch -}; +#define FD_CLOSEONEXEC(x) do { \ + if (fcntl(x, F_SETFD, 1) == -1) \ + event_warn("fcntl(%d, F_SETFD)", x); \ +} while (0) void * rtsig_init(void) { struct rtsigop *op; + int sockets[2]; + int optarg; + struct rtdata *dat; + int flags; if (getenv("EVENT_NORTSIG")) - return (NULL); - - op = malloc(sizeof(*op)); - if (op == NULL) return (NULL); + goto err; - memset(op, 0, sizeof(*op)); + op = calloc(1, sizeof(*op)); + if (op == NULL) + goto err; op->max = INIT_MAX; op->poll = malloc(sizeof(*op->poll) * op->max); - if (op->poll == NULL) { - free(op); - return (NULL); - } - op->toev = malloc(sizeof(*op->toev) * op->max); - if (op->toev == NULL) { - free(op->poll); - free(op); - return (NULL); - } + if (op->poll == NULL) + goto err_free_op; + + op->signo = find_rt_signal(); + if (op->signo == 0) + goto err_free_poll; + + op->nonsock = 0; + + if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sockets) != 0) + goto err_free_poll; + + FD_CLOSEONEXEC(sockets[0]); + FD_CLOSEONEXEC(sockets[1]); + + signal_send_fd[op->signo - 1] = sockets[0]; + trouble[op->signo - 1] = 0; + op->signal_send_fd = sockets[0]; + op->signal_recv_fd = sockets[1]; + flags = fcntl(op->signal_recv_fd, F_GETFL); + fcntl(op->signal_recv_fd, F_SETFL, flags | O_NONBLOCK); + + optarg = MAXBUFFERSIZE; + setsockopt(signal_send_fd[op->signo - 1], + SOL_SOCKET, SO_SNDBUF, + &optarg, sizeof(optarg)); + + optarg = MAXBUFFERSIZE; + setsockopt(op->signal_recv_fd, + SOL_SOCKET, SO_RCVBUF, + &optarg, sizeof(optarg)); + + op->highestfd = -1; + op->fdtodat = NULL; + if (grow_fdset(op, 1) == -1) + goto err_close_pair; + + op->ptodat = malloc(sizeof(*op->ptodat) * op->max); + if (op->ptodat == NULL) + goto err_close_pair; sigemptyset(&op->sigs); sigaddset(&op->sigs, SIGIO); - sigaddset(&op->sigs, SIGRTMIN); + sigaddset(&op->sigs, op->signo); sigprocmask(SIG_BLOCK, &op->sigs, NULL); + set_sigaction(SIGIO); + set_sigaction(op->signo); + + event_set(&(op->sigfdev), op->signal_recv_fd, EV_READ|EV_PERSIST, + donothing, NULL); + op->sigfdev.ev_flags |= EVLIST_DONTDEL; + dat = ev2dat(op, &(op->sigfdev), 1); + poll_add(op, &(op->sigfdev), dat); return (op); + + err_close_pair: + close(op->signal_recv_fd); + close(signal_send_fd[op->signo - 1]); + + err_free_poll: + free(op->poll); + + err_free_op: + free(op); + err: + return (NULL); } int @@ -173,79 +635,102 @@ rtsig_add(void *arg, struct event *ev) { struct rtsigop *op = (struct rtsigop *) arg; int flags, i; -#ifndef HAVE_WORKING_RTSIG - struct stat st; -#endif + struct stat statbuf; + struct rtdata *dat; if (ev->ev_events & EV_SIGNAL) { + int signo = EVENT_SIGNAL(ev); + sigaddset(&op->sigs, EVENT_SIGNAL(ev)); - return sigprocmask(SIG_BLOCK, &op->sigs, NULL); + if (sigprocmask(SIG_BLOCK, &op->sigs, NULL) == -1) + return (-1); + + set_sigaction(signo); + + signal_send_fd[signo - 1] = op->signal_send_fd; + trouble[signo - 1] = 0; + + return (0); } - if (!(ev->ev_events & (EV_READ | EV_WRITE))) return 0; + if (!(ev->ev_events & (EV_READ|EV_WRITE))) + return (0); + + if (-1 == fstat(ev->ev_fd, &statbuf)) + return (-1); -#ifndef HAVE_WORKING_RTSIG - if (fstat(ev->ev_fd, &st) == -1) return -1; - if (S_ISFIFO(st.st_mode)) { - ev->ev_flags |= EVLIST_X_NORT; - op->pollmode++; - } -#endif + if (!S_ISSOCK(statbuf.st_mode)) + ev->ev_flags |= EVLIST_NONSOCK; flags = fcntl(ev->ev_fd, F_GETFL); if (flags == -1) return (-1); if (!(flags & O_ASYNC)) { - if (fcntl(ev->ev_fd, F_SETSIG, SIGRTMIN) == -1 - || fcntl(ev->ev_fd, F_SETOWN, (int) getpid()) == -1) + if (fcntl(ev->ev_fd, F_SETSIG, op->signo) == -1 || + fcntl(ev->ev_fd, F_SETOWN, (int) gettid()) == -1) return (-1); - - if (fcntl(ev->ev_fd, F_SETFL, flags | O_ASYNC)) + + /* + * the overhead of always handling writeable edges + * isn't going to be that bad... + */ + if (fcntl(ev->ev_fd, F_SETFL, flags | O_ASYNC|O_RDWR)) return (-1); } #ifdef O_ONESIGFD + /* + * F_SETAUXFL and O_ONESIGFD are defined in a non-standard + * linux kernel patch to coalesce events for fds + */ fcntl(ev->ev_fd, F_SETAUXFL, O_ONESIGFD); #endif + dat = ev2dat(op, ev, 1); + if (dat == NULL) + return (-1); + op->total++; - if (poll_add(op, ev) == -1) - goto err; + if (ev->ev_flags & EVLIST_NONSOCK) + op->nonsock++; + + if (poll_add(op, ev, dat) == -1) { + /* must check the level of new fd's */ + i = errno; + fcntl(ev->ev_fd, F_SETFL, flags); + errno = i; + return (-1); + } return (0); - - err: - i = errno; - fcntl(ev->ev_fd, F_SETFL, flags); - errno = i; - return (-1); } int rtsig_del(void *arg, struct event *ev) { + struct rtdata *dat; struct rtsigop *op = (struct rtsigop *) arg; if (ev->ev_events & EV_SIGNAL) { sigset_t sigs; sigdelset(&op->sigs, EVENT_SIGNAL(ev)); - + sigemptyset(&sigs); sigaddset(&sigs, EVENT_SIGNAL(ev)); return (sigprocmask(SIG_UNBLOCK, &sigs, NULL)); } - if (!(ev->ev_events & (EV_READ | EV_WRITE))) + if (!(ev->ev_events & (EV_READ|EV_WRITE))) return (0); -#ifndef HAVE_WORKING_RTSIG - if (ev->ev_flags & EVLIST_X_NORT) - op->pollmode--; -#endif - poll_remove(op, ev); + dat = ev2dat(op, ev, 0); + poll_remove(op, ev, dat); + dat_del(op, dat); op->total--; + if (ev->ev_flags & EVLIST_NONSOCK) + op->nonsock--; return (0); } @@ -253,183 +738,248 @@ rtsig_del(void *arg, struct event *ev) int rtsig_recalc(struct event_base *base, void *arg, int max) { - return (0); + return (0); } -int -rtsig_dispatch(struct event_base *base, void *arg, struct timeval *tv) +/* + * the following do_X functions implement the different stages of a single + * eventloop pass: poll(), recv(sigsock), sigtimedwait() + * + * do_siginfo_dispatch() is a common factor to both do_sigwait() and + * do_signals_from_socket(). + */ + +static inline int +do_poll(struct rtsigop *op, struct timespec *ts) { - struct rtsigop *op = (struct rtsigop *) arg; - struct timespec ts; - int res, i; + int res = 0; + int i = 0; + + if (op->cur > 1) { + /* non-empty poll set (modulo the signalfd) */ + if (op->nonsock) { + int timeout = ts->tv_nsec / 1000000 + ts->tv_sec * 1000; + + sigprocmask(SIG_UNBLOCK, &(op->sigs), NULL); + + res = poll(op->poll, op->cur, timeout); + + sigprocmask(SIG_BLOCK, &(op->sigs), NULL); + + ts->tv_sec = 0; + ts->tv_nsec = 0; + } else { + res = poll(op->poll, op->cur, 0); + } - if (op->poll == NULL) - goto retry_poll; -#ifndef HAVE_WORKING_RTSIG - if (op->pollmode) - goto poll_all; -#endif + if (res < 0) { + return (errno == EINTR ? 0 : -1); + } else if (res) { + ts->tv_sec = 0; + ts->tv_nsec = 0; + } - if (op->cur) { - ts.tv_sec = ts.tv_nsec = 0; - } else { - ts.tv_sec = tv->tv_sec; - ts.tv_nsec = tv->tv_usec * 1000; + i = 0; + while (i < op->cur) { + struct rtdata *dat = op->ptodat[i]; + struct event *ev = dat->ev; + + if (op->poll[i].revents) { + int flags = 0; + + if (op->poll[i].revents & (POLLIN | POLLERR)) + flags |= EV_READ; + + if (op->poll[i].revents & POLLOUT) + flags |= EV_WRITE; + + if (!(ev->ev_events & EV_PERSIST)) { + poll_remove(op, ev, op->ptodat[i]); + event_del(ev); + } else { + i++; + } + + event_active(ev, flags, 1); + } else { + if (ev->ev_flags & (EVLIST_NONSOCK|EVLIST_DONTDEL)) { + i++; + } else { + poll_remove(op, ev, op->ptodat[i]); + } + } + } } + return (res); +} - for (;;) { - siginfo_t info; - struct event *ev; - int signum; +static inline int +do_siginfo_dispatch(struct event_base *base, struct rtsigop *op, + siginfo_t *info) +{ + int signum; + struct rtdata *dat, *next_dat; + struct event *ev, *next_ev; - signum = sigtimedwait(&op->sigs, &info, &ts); + if (info == NULL) + return (-1); - if (signum == -1) { - if (errno == EAGAIN) - break; - return (errno == EINTR ? 0 : -1); - } + signum = info->si_signo; + if (signum == op->signo) { + int flags, sigok = 0; + flags = 0; - ts.tv_sec = ts.tv_nsec = 0; + if (info->si_band & (POLLIN|POLLERR)) + flags |= EV_READ; + if (info->si_band & POLLOUT) + flags |= EV_WRITE; - if (signum == SIGIO) { -#ifndef HAVE_WORKING_RTSIG - poll_all: -#endif - free(op->poll); - free(op->toev); - retry_poll: - op->cur = 0; - op->max = op->total; - op->poll = malloc(sizeof(*op->poll) * op->total); - if (op->poll == NULL) - return (-1); - op->toev = malloc(sizeof(*op->toev) * op->total); - if (op->toev == NULL) { - free(op->poll); - op->poll = NULL; - return (-1); - } + if (!flags) + return (0); - TAILQ_FOREACH(ev, &base->eventqueue, ev_next) - if (!(ev->ev_flags & EVLIST_X_NORT)) - poll_add(op, ev); + if (info->si_fd > op->highestfd) + return (-1); - break; + dat = TAILQ_FIRST(op->fdtodat[info->si_fd]); + while (dat != TAILQ_END(op->fdtodat[info->si_fd])) { + next_dat = TAILQ_NEXT(dat, next); + if (flags & dat->ev->ev_events) { + ev = dat->ev; + poll_add(op, ev, dat); + activate(ev, flags & ev->ev_events); + sigok = 1; + } + dat = next_dat; } - - if (signum == SIGRTMIN) { - int flags, i, sigok = 0; - - if (info.si_band <= 0) { /* SI_SIGIO */ - flags = EV_READ | EV_WRITE; + } else if (signum == SIGIO) { + TAILQ_FOREACH(ev, &base->eventqueue, ev_next) { + if (ev->ev_events & (EV_READ|EV_WRITE)) + poll_add(op, ev, NULL); + } + return (1); /* 1 means the caller should poll() again */ + + } else if (sigismember(&op->sigs, signum)) { + /* managed signals are queued */ + ev = TAILQ_FIRST(&signalqueue); + while (ev != TAILQ_END(&signalqueue)) { + next_ev = TAILQ_NEXT(ev, ev_signal_next); + if (EVENT_SIGNAL(ev) == signum) + activate(ev, EV_SIGNAL); + ev = next_ev; + } + } else { + /* dispatch unmanaged signals immediately */ + struct sigaction sa; + if (sigaction(signum, NULL, &sa) == 0) { + if ((sa.sa_flags & SA_SIGINFO) && sa.sa_sigaction) { + (*sa.sa_sigaction)(signum, info, NULL); + } else if (sa.sa_handler) { + if ((int)sa.sa_handler != 1) + (*sa.sa_handler)(signum); } else { - flags = 0; - if (info.si_band & POLLIN) flags |= EV_READ; - if (info.si_band & POLLOUT) flags |= EV_WRITE; - if (!flags) continue; + if (signum != SIGCHLD) { + /* non-blocked SIG_DFL */ + kill(gettid(), signum); + } } + } + } - for (i = 0; flags && i < op->cur; i++) { - ev = op->toev[i]; + return (0); +} - if (ev->ev_fd == info.si_fd) { - flags &= ~ev->ev_events; - sigok = 1; - } - } +/* + * return 1 if we should poll again + * return 0 if we are all set + * return -1 on error + */ +static inline int +do_sigwait(struct event_base *base, struct rtsigop *op, struct timespec *ts, + sigset_t *sigs) +{ + for (;;) { + siginfo_t info; + int signum; - for (ev = TAILQ_FIRST(&base->eventqueue); - flags && ev != TAILQ_END(&base->eventqueue); - ev = TAILQ_NEXT(ev, ev_next)) { - if (ev->ev_fd == info.si_fd) { - if (flags & ev->ev_events) { - i = poll_add(op, ev); - if (i == -1) return -1; - flags &= ~ev->ev_events; - } - sigok = 1; - } - } + signum = sigtimedwait(sigs, &info, ts); - if (!sigok) { - flags = fcntl(info.si_fd, F_GETFL); - if (flags == -1) return -1; - fcntl(info.si_fd, F_SETFL, flags & ~O_ASYNC); - } - } else { - TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) { - if (EVENT_SIGNAL(ev) == signum) - activate(ev, EV_SIGNAL); - } + ts->tv_sec = 0; + ts->tv_nsec = 0; + + if (signum == -1) { + if (errno == EAGAIN || errno == EINTR) + return (0); + return (-1); + } else if (1 == do_siginfo_dispatch(base, op, &info)) { + return (1); } } - if (!op->cur) - return (0); + /* NOTREACHED */ +} - res = poll(op->poll, op->cur, tv->tv_sec * 1000 + - (tv->tv_usec + 999) / 1000); - if (res < 0) - return (-1); +static inline int +do_signals_from_socket(struct event_base *base, struct rtsigop *op, + struct timespec *ts) +{ + int fd = op->signal_recv_fd; + siginfo_t info; + int res; - i = 0; -#ifdef HAVE_WORKING_RTSIG - while (i < res) { -#else - while (i < op->cur) { -#endif - if (op->poll[i].revents) { - int flags = 0; - struct event *ev = op->toev[i]; - - if (op->poll[i].revents & POLLIN) - flags |= EV_READ; - if (op->poll[i].revents & POLLOUT) - flags |= EV_WRITE; - - if (!(ev->ev_events & EV_PERSIST)) { - event_del(ev); - res--; - } else { - i++; - } - event_active(ev, flags, 1); - } else { -#ifndef HAVE_WORKING_RTSIG - if (op->toev[i]->ev_flags & EVLIST_X_NORT) { - i++; - res++; + for (;;) { + res = recv(fd, &info, sizeof(info), MSG_NOSIGNAL); + if (res == -1) { + if (errno == EAGAIN) + return (0); + if (errno == EINTR) continue; - } -#endif - for (;;) { - op->cur--; - if (i == op->cur) - break; - if (op->poll[op->cur].revents) { - memcpy(&op->poll[i], &op->poll[op->cur], sizeof(*op->poll)); - op->toev[i] = op->toev[op->cur]; - break; - } - } + return (-1); + } else { + ts->tv_sec = 0; + ts->tv_nsec = 0; + if (1 == do_siginfo_dispatch(base, op, &info)) + return (1); } } -#ifdef HAVE_WORKING_RTSIG - op->cur = res; -#endif + /* NOTREACHED */ +} - if (!op->cur) { - op->max = INIT_MAX; - free(op->poll); - free(op->toev); - /* We just freed it, we shouldn't have a problem getting it back. */ - op->poll = malloc(sizeof(*op->poll) * op->max); - op->toev = malloc(sizeof(*op->toev) * op->max); +int +rtsig_dispatch(struct event_base *base, void *arg, struct timeval *tv) +{ + struct rtsigop *op = (struct rtsigop *) arg; + struct timespec ts; + int res; + sigset_t sigs; - if (op->poll == NULL || op->toev == NULL) - event_err(1, "%s: malloc"); - } + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + + poll_for_level: + res = do_poll(op, &ts); /* ts can be modified in do_XXX() */ + + res = do_signals_from_socket(base, op, &ts); + if (res == 1) + goto poll_for_level; + else if (res == -1) + return (-1); + + /* + * the mask = managed_signals | unblocked-signals + * MM - if this is not blocking do we need to cast the net this wide? + */ + sigemptyset(&sigs); + sigprocmask(SIG_BLOCK, &sigs, &sigs); + signotset(&sigs); + sigorset(&sigs, &sigs, &op->sigs); + + res = do_sigwait(base, op, &ts, &sigs); + + if (res == 1) + goto poll_for_level; + else if (res == -1) + return (-1); return (0); } + diff --git a/varnish-cache/contrib/libevent/select.c b/varnish-cache/contrib/libevent/select.c index 00909b79..1e039e1f 100644 --- a/varnish-cache/contrib/libevent/select.c +++ b/varnish-cache/contrib/libevent/select.c @@ -76,6 +76,7 @@ int select_add (void *, struct event *); int select_del (void *, struct event *); int select_recalc (struct event_base *, void *, int); int select_dispatch (struct event_base *, void *, struct timeval *); +void select_dealloc (void *); const struct eventop selectops = { "select", @@ -83,7 +84,8 @@ const struct eventop selectops = { select_add, select_del, select_recalc, - select_dispatch + select_dispatch, + select_dealloc }; static int select_resize(struct selectop *sop, int fdsz); @@ -93,7 +95,7 @@ select_init(void) { struct selectop *sop; - /* Disable kqueue when this environment variable is set */ + /* Disable select when this environment variable is set */ if (getenv("EVENT_NOSELECT")) return (NULL); @@ -131,7 +133,7 @@ check_selectop(struct selectop *sop) } #else -#define check_selectop(sop) +#define check_selectop(sop) do {;} while (0) #endif /* @@ -350,3 +352,25 @@ select_del(void *arg, struct event *ev) check_selectop(sop); return (0); } + +void +select_dealloc(void *arg) +{ + struct selectop *sop = arg; + + if (sop->event_readset_in) + free(sop->event_readset_in); + if (sop->event_writeset_in) + free(sop->event_writeset_in); + if (sop->event_readset_out) + free(sop->event_readset_out); + if (sop->event_writeset_out) + free(sop->event_writeset_out); + if (sop->event_r_by_fd) + free(sop->event_r_by_fd); + if (sop->event_w_by_fd) + free(sop->event_w_by_fd); + + memset(sop, 0, sizeof(struct selectop)); + free(sop); +} diff --git a/varnish-cache/contrib/libevent/signal.c b/varnish-cache/contrib/libevent/signal.c index 19a85f14..8a6963b1 100644 --- a/varnish-cache/contrib/libevent/signal.c +++ b/varnish-cache/contrib/libevent/signal.c @@ -26,7 +26,9 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifdef HAVE_CONFIG_H #include "config.h" +#endif #include #ifdef HAVE_SYS_TIME_H @@ -52,7 +54,7 @@ extern struct event_list signalqueue; -static short evsigcaught[NSIG]; +static sig_atomic_t evsigcaught[NSIG]; static int needrecalc; volatile sig_atomic_t evsignal_caught = 0; @@ -61,11 +63,12 @@ static int ev_signal_pair[2]; static int ev_signal_added; /* Callback for when the signal handler write a byte to our signaling socket */ -static void evsignal_cb(int fd, short what, void *arg) +static void +evsignal_cb(int fd, short what, void *arg) { static char signals[100]; struct event *ev = arg; - int n; + ssize_t n; n = read(fd, signals, sizeof(signals)); if (n == -1) @@ -98,6 +101,8 @@ evsignal_init(sigset_t *evsigmask) FD_CLOSEONEXEC(ev_signal_pair[0]); FD_CLOSEONEXEC(ev_signal_pair[1]); + fcntl(ev_signal_pair[0], F_SETFL, O_NONBLOCK); + event_set(&ev_signal, ev_signal_pair[1], EV_READ, evsignal_cb, &ev_signal); ev_signal.ev_flags |= EVLIST_INTERNAL; @@ -107,12 +112,12 @@ int evsignal_add(sigset_t *evsigmask, struct event *ev) { int evsignal; - + if (ev->ev_events & (EV_READ|EV_WRITE)) event_errx(1, "%s: EV_SIGNAL incompatible use", __func__); evsignal = EVENT_SIGNAL(ev); sigaddset(evsigmask, evsignal); - + return (0); } @@ -135,11 +140,14 @@ evsignal_del(sigset_t *evsigmask, struct event *ev) static void evsignal_handler(int sig) { + int save_errno = errno; + evsigcaught[sig]++; evsignal_caught = 1; /* Wake up our notification mechanism */ write(ev_signal_pair[0], "a", 1); + errno = save_errno; } int @@ -147,7 +155,7 @@ evsignal_recalc(sigset_t *evsigmask) { struct sigaction sa; struct event *ev; - + if (!ev_signal_added) { ev_signal_added = 1; event_add(&ev_signal, NULL); @@ -159,13 +167,13 @@ evsignal_recalc(sigset_t *evsigmask) if (sigprocmask(SIG_BLOCK, evsigmask, NULL) == -1) return (-1); - + /* Reinstall our signal handler. */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = evsignal_handler; sa.sa_mask = *evsigmask; sa.sa_flags |= SA_RESTART; - + TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) { if (sigaction(EVENT_SIGNAL(ev), &sa, NULL) == -1) return (-1); @@ -187,7 +195,7 @@ void evsignal_process(void) { struct event *ev; - short ncalls; + sig_atomic_t ncalls; TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) { ncalls = evsigcaught[EVENT_SIGNAL(ev)]; diff --git a/varnish-cache/contrib/libevent/strlcpy.c b/varnish-cache/contrib/libevent/strlcpy.c new file mode 100644 index 00000000..163f4258 --- /dev/null +++ b/varnish-cache/contrib/libevent/strlcpy.c @@ -0,0 +1,74 @@ +/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/varnish-cache/contrib/libevent/test/Makefile.am b/varnish-cache/contrib/libevent/test/Makefile.am index 0efdd40c..157d261e 100644 --- a/varnish-cache/contrib/libevent/test/Makefile.am +++ b/varnish-cache/contrib/libevent/test/Makefile.am @@ -4,20 +4,28 @@ LDADD = ../libevent.la CPPFPLAGS = -I.. CFLAGS = -I../compat @CFLAGS@ +EXTRA_DIST = regress.rpc + noinst_PROGRAMS = test-init test-eof test-weof test-time regress bench -test_init_sources = test-init.c -test_eof_sources = test-eof.c -test_weof_sources = test-weof.c -test_time_sources = test-time.c -regress_sources = regress.c -bench_sources = bench.c +BUILT_SOURCES = regress.gen.c regress.gen.h +test_init_SOURCES = test-init.c +test_eof_SOURCES = test-eof.c +test_weof_SOURCES = test-weof.c +test_time_SOURCES = test-time.c +regress_SOURCES = regress.c regress.h regress_http.c \ + regress.gen.c regress.gen.h +bench_SOURCES = bench.c + +regress.gen.c regress.gen.h: regress.rpc + ../event_rpcgen.py regress.rpc DISTCLEANFILES = *~ +CLEANFILES = regress.gen.h regress.gen.c test: test-init test-eof test-weof test-time regress verify: test @./test.sh -bench test-init test-eof test-weof test-time regress: ../libevent.la +bench test-init test-eof test-weof test-time: ../libevent.la diff --git a/varnish-cache/contrib/libevent/test/regress.c b/varnish-cache/contrib/libevent/test/regress.c index ce47d225..3f515838 100644 --- a/varnish-cache/contrib/libevent/test/regress.c +++ b/varnish-cache/contrib/libevent/test/regress.c @@ -39,21 +39,28 @@ #ifdef HAVE_SYS_TIME_H #include #endif +#include #ifndef WIN32 #include #include #include #endif +#include #include #include #include #include #include -#include +#include "event.h" +#include "log.h" +#include "http.h" -static int pair[2]; -static int test_ok; +#include "regress.h" +#include "regress.gen.h" + +int pair[2]; +int test_ok; static int called; static char wbuf[4096]; static char rbuf[4096]; @@ -276,7 +283,7 @@ cleanup_test(void) } void -test1(void) +test_simpleread(void) { struct event ev; @@ -295,7 +302,7 @@ test1(void) } void -test2(void) +test_simplewrite(void) { struct event ev; @@ -311,7 +318,7 @@ test2(void) } void -test3(void) +test_multiple(void) { struct event ev, ev2; int i; @@ -340,7 +347,7 @@ test3(void) } void -test4(void) +test_persistent(void) { struct event ev, ev2; int i; @@ -369,7 +376,7 @@ test4(void) } void -test5(void) +test_combined(void) { struct both r1, r2, w1, w2; @@ -404,7 +411,7 @@ test5(void) } void -test6(void) +test_simpletimeout(void) { struct timeval tv; struct event ev; @@ -424,7 +431,7 @@ test6(void) #ifndef WIN32 void -test7(void) +test_simplesignal(void) { struct event ev; struct itimerval itv; @@ -447,7 +454,7 @@ test7(void) #endif void -test8(void) +test_loopexit(void) { struct timeval tv, tv_start, tv_end; struct event ev; @@ -476,6 +483,21 @@ test8(void) cleanup_test(); } +void +test_evbuffer(void) { + setup_test("Evbuffer: "); + + struct evbuffer *evb = evbuffer_new(); + + evbuffer_add_printf(evb, "%s/%d", "hello", 1); + + if (EVBUFFER_LENGTH(evb) == 7 && + strcmp(EVBUFFER_DATA(evb), "hello/1") == 0) + test_ok = 1; + + cleanup_test(); +} + void readcb(struct bufferevent *bev, void *arg) { @@ -499,7 +521,7 @@ errorcb(struct bufferevent *bev, short what, void *arg) } void -test9(void) +test_bufferevent(void) { struct bufferevent *bev1, *bev2; char buffer[8333]; @@ -637,6 +659,210 @@ test_multiple_events_for_same_fd(void) cleanup_test(); } +int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf); + +void +read_once_cb(int fd, short event, void *arg) +{ + char buf[256]; + int len; + + len = read(fd, buf, sizeof(buf)); + + if (called) { + test_ok = 0; + } else if (len) { + /* Assumes global pair[0] can be used for writing */ + write(pair[0], TEST1, strlen(TEST1)+1); + test_ok = 1; + } + + called++; +} + +void +test_want_only_once(void) +{ + struct event ev; + struct timeval tv; + + /* Very simple read test */ + setup_test("Want read only once: "); + + write(pair[0], TEST1, strlen(TEST1)+1); + + /* Setup the loop termination */ + timerclear(&tv); + tv.tv_sec = 1; + event_loopexit(&tv); + + event_set(&ev, pair[1], EV_READ, read_once_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_dispatch(); + + cleanup_test(); +} + +#define TEST_MAX_INT 6 + +void +evtag_int_test(void) +{ + struct evbuffer *tmp = evbuffer_new(); + u_int32_t integers[TEST_MAX_INT] = { + 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000 + }; + u_int32_t integer; + int i; + + for (i = 0; i < TEST_MAX_INT; i++) { + int oldlen, newlen; + oldlen = EVBUFFER_LENGTH(tmp); + encode_int(tmp, integers[i]); + newlen = EVBUFFER_LENGTH(tmp); + fprintf(stdout, "\t\tencoded 0x%08x with %d bytes\n", + integers[i], newlen - oldlen); + } + + for (i = 0; i < TEST_MAX_INT; i++) { + if (decode_int(&integer, tmp) == -1) { + fprintf(stderr, "decode %d failed", i); + exit(1); + } + if (integer != integers[i]) { + fprintf(stderr, "got %x, wanted %x", + integer, integers[i]); + exit(1); + } + } + + if (EVBUFFER_LENGTH(tmp) != 0) { + fprintf(stderr, "trailing data"); + exit(1); + } + evbuffer_free(tmp); + + fprintf(stdout, "\t%s: OK\n", __func__); +} + +void +evtag_fuzz() +{ + u_char buffer[4096]; + struct evbuffer *tmp = evbuffer_new(); + struct timeval tv; + int i, j; + + int not_failed = 0; + for (j = 0; j < 100; j++) { + for (i = 0; i < sizeof(buffer); i++) + buffer[i] = rand(); + evbuffer_drain(tmp, -1); + evbuffer_add(tmp, buffer, sizeof(buffer)); + + if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) + not_failed++; + } + + /* The majority of decodes should fail */ + if (not_failed >= 10) { + fprintf(stderr, "evtag_unmarshal should have failed"); + exit(1); + } + + /* Now insert some corruption into the tag length field */ + evbuffer_drain(tmp, -1); + timerclear(&tv); + tv.tv_sec = 1; + evtag_marshal_timeval(tmp, 0, &tv); + evbuffer_add(tmp, buffer, sizeof(buffer)); + + EVBUFFER_DATA(tmp)[1] = 0xff; + if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) { + fprintf(stderr, "evtag_unmarshal_timeval should have failed"); + exit(1); + } + + evbuffer_free(tmp); + + fprintf(stdout, "\t%s: OK\n", __func__); +} + +void +evtag_test(void) +{ + fprintf(stdout, "Testing Tagging:\n"); + + evtag_init(); + evtag_int_test(); + evtag_fuzz(); + + fprintf(stdout, "OK\n"); +} + +void +rpc_test(void) +{ + struct msg *msg, *msg2; + struct kill *kill; + struct run *run; + struct evbuffer *tmp = evbuffer_new(); + int i; + + fprintf(stdout, "Testing RPC: "); + + msg = msg_new(); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "phoenix"); + + if (EVTAG_GET(msg, kill, &kill) == -1) { + fprintf(stderr, "Failed to set kill message.\n"); + exit(1); + } + + EVTAG_ASSIGN(kill, weapon, "feather"); + EVTAG_ASSIGN(kill, action, "tickle"); + + for (i = 0; i < 3; ++i) { + run = EVTAG_ADD(msg, run); + if (run == NULL) { + fprintf(stderr, "Failed to add run message.\n"); + exit(1); + } + EVTAG_ASSIGN(run, how, "very fast"); + } + + if (msg_complete(msg) == -1) { + fprintf(stderr, "Failed to make complete message.\n"); + exit(1); + } + + evtag_marshal_msg(tmp, 0, msg); + + msg2 = msg_new(); + if (evtag_unmarshal_msg(tmp, 0, msg2) == -1) { + fprintf(stderr, "Failed to unmarshal message.\n"); + exit(1); + } + + if (!EVTAG_HAS(msg2, from_name) || + !EVTAG_HAS(msg2, to_name) || + !EVTAG_HAS(msg2, kill)) { + fprintf(stderr, "Missing data structures.\n"); + exit(1); + } + + if (EVTAG_LEN(msg2, run) != 3) { + fprintf(stderr, "Wrong number of run messages.\n"); + exit(1); + } + + msg_free(msg); + msg_free(msg2); + + fprintf(stdout, "OK\n"); +} int main (int argc, char **argv) @@ -656,23 +882,27 @@ main (int argc, char **argv) /* Initalize the event library */ event_base = event_init(); - test1(); + http_suite(); + + test_simpleread(); - test2(); + test_simplewrite(); - test3(); + test_multiple(); - test4(); + test_persistent(); - test5(); + test_combined(); - test6(); + test_simpletimeout(); #ifndef WIN32 - test7(); + test_simplesignal(); #endif - test8(); + test_loopexit(); - test9(); + test_evbuffer(); + + test_bufferevent(); test_priorities(1); test_priorities(2); @@ -680,6 +910,12 @@ main (int argc, char **argv) test_multiple_events_for_same_fd(); + test_want_only_once(); + + evtag_test(); + + rpc_test(); + return (0); } diff --git a/varnish-cache/contrib/libevent/test/regress.h b/varnish-cache/contrib/libevent/test/regress.h new file mode 100644 index 00000000..3006830c --- /dev/null +++ b/varnish-cache/contrib/libevent/test/regress.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _REGRESS_H_ +#define _REGRESS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void http_suite(void); +void http_basic_test(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _REGRESS_H_ */ diff --git a/varnish-cache/contrib/libevent/test/regress.rpc b/varnish-cache/contrib/libevent/test/regress.rpc new file mode 100644 index 00000000..7bc6b75d --- /dev/null +++ b/varnish-cache/contrib/libevent/test/regress.rpc @@ -0,0 +1,17 @@ +/* tests data packing and unpacking */ + +struct msg { + string from_name = 1; + string to_name = 2; + optional struct[kill] kill = 3; + array struct[run] run = 4; +} + +struct kill { + string weapon = 1; + string action = 2; +} + +struct run { + string how = 1; +} diff --git a/varnish-cache/contrib/libevent/test/regress_http.c b/varnish-cache/contrib/libevent/test/regress_http.c new file mode 100644 index 00000000..f96fa69d --- /dev/null +++ b/varnish-cache/contrib/libevent/test/regress_http.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2003-2006 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef WIN32 +#include +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifndef WIN32 +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "event.h" +#include "log.h" +#include "http.h" + +extern int pair[]; +extern int test_ok; + +static struct evhttp *http; + +void http_basic_cb(struct evhttp_request *req, void *arg); +void http_post_cb(struct evhttp_request *req, void *arg); + +struct evhttp * +http_setup(short *pport) +{ + int i; + struct evhttp *myhttp; + short port = -1; + + /* Try a few different ports */ + for (i = 0; i < 50; ++i) { + myhttp = evhttp_start("127.0.0.1", 8080 + i); + if (myhttp != NULL) { + port = 8080 + i; + break; + } + } + + if (port == -1) + event_errx(1, "Could not start web server"); + + /* Register a callback for certain types of requests */ + evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL); + evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL); + + *pport = port; + return (myhttp); +} + +int +http_connect(const char *address, u_short port) +{ + /* Stupid code for connecting */ + struct addrinfo ai, *aitop; + char strport[NI_MAXSERV]; + int fd; + + memset(&ai, 0, sizeof (ai)); + ai.ai_family = AF_INET; + ai.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof (strport), "%d", port); + if (getaddrinfo(address, strport, &ai, &aitop) != 0) { + event_warn("getaddrinfo"); + return (-1); + } + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) + event_err(1, "socket failed"); + + if (connect(fd, aitop->ai_addr, aitop->ai_addrlen) == -1) + event_err(1, "connect failed"); + + freeaddrinfo(aitop); + + return (fd); +} + +void +http_readcb(struct bufferevent *bev, void *arg) +{ + const char *what = "This is funny"; + + event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); + + if (evbuffer_find(bev->input, what, strlen(what)) != NULL) { + struct evhttp_request *req = evhttp_request_new(NULL, NULL); + req->kind = EVHTTP_RESPONSE; + int done = evhttp_parse_lines(req, bev->input); + + if (done == 1 && + evhttp_find_header(req->input_headers, + "Content-Type") != NULL) + test_ok++; + evhttp_request_free(req); + bufferevent_disable(bev, EV_READ); + event_loopexit(NULL); + } +} + +void +http_writecb(struct bufferevent *bev, void *arg) +{ + if (EVBUFFER_LENGTH(bev->output) == 0) { + /* enable reading of the reply */ + bufferevent_enable(bev, EV_READ); + test_ok++; + } +} + +void +http_errorcb(struct bufferevent *bev, short what, void *arg) +{ + test_ok = -2; + event_loopexit(NULL); +} + +void +http_basic_cb(struct evhttp_request *req, void *arg) +{ + event_debug((stderr, "%s: called\n", __func__)); + + struct evbuffer *evb = evbuffer_new(); + evbuffer_add_printf(evb, "This is funny"); + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); +} + +void +http_basic_test(void) +{ + struct bufferevent *bev; + int fd; + char *http_request; + short port = -1; + + test_ok = 0; + fprintf(stdout, "Testing Basic HTTP Server: "); + + http = http_setup(&port); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, http_readcb, http_writecb, + http_errorcb, NULL); + + http_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost \r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_dispatch(); + + bufferevent_free(bev); + close(fd); + + evhttp_free(http); + + if (test_ok != 2) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +void http_connectcb(struct evhttp_connection *evcon, void *arg); + +void +http_connection_test(void) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + + test_ok = 0; + fprintf(stdout, "Testing Basic HTTP Connection: "); + + http = http_setup(&port); + + evcon = evhttp_connect("127.0.0.1", port, http_connectcb, NULL); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + evhttp_connection_free(evcon); + evhttp_free(http); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +void http_request_done(struct evhttp_request *, void *); + +void +http_connectcb(struct evhttp_connection *evcon, void *arg) +{ + struct evhttp_request *req = NULL; + + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit (1); + } + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } +} + +void +http_request_done(struct evhttp_request *req, void *arg) +{ + const char *what = "This is funny"; + + if (req->response_code != HTTP_OK) { + + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->buffer) != strlen(what)) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (memcmp(EVBUFFER_DATA(req->buffer), what, strlen(what)) != 0) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +/* + * HTTP POST test. + */ + +void http_connect_forpostcb(struct evhttp_connection *evcon, void *arg); + +void +http_post_test(void) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + + test_ok = 0; + fprintf(stdout, "Testing HTTP POST Request: "); + + http = http_setup(&port); + + evcon = evhttp_connect("127.0.0.1", port, http_connect_forpostcb, NULL); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + evhttp_connection_free(evcon); + evhttp_free(http); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +void http_postrequest_done(struct evhttp_request *, void *); + +#define POST_DATA "Okay. Not really printf" + +void +http_connect_forpostcb(struct evhttp_connection *evcon, void *arg) +{ + struct evhttp_request *req = NULL; + + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit (1); + } + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(http_postrequest_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + evbuffer_add_printf(req->buffer, POST_DATA); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } +} + +void +http_post_cb(struct evhttp_request *req, void *arg) +{ + event_debug((stderr, "%s: called\n", __func__)); + + /* Yes, we are expecting a post request */ + if (req->type != EVHTTP_REQ_POST) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->buffer) != strlen(POST_DATA)) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + if (strcmp(EVBUFFER_DATA(req->buffer), POST_DATA)) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + struct evbuffer *evb = evbuffer_new(); + evbuffer_add_printf(evb, "This is funny"); + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); +} + +void +http_postrequest_done(struct evhttp_request *req, void *arg) +{ + const char *what = "This is funny"; + + if (req->response_code != HTTP_OK) { + + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->buffer) != strlen(what)) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (memcmp(EVBUFFER_DATA(req->buffer), what, strlen(what)) != 0) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + + +void +http_suite(void) +{ + http_basic_test(); + http_connection_test(); + http_post_test(); +} diff --git a/varnish-cache/contrib/libevent/test/test-weof.c b/varnish-cache/contrib/libevent/test/test-weof.c index 8862b4a2..5f3be277 100644 --- a/varnish-cache/contrib/libevent/test/test-weof.c +++ b/varnish-cache/contrib/libevent/test/test-weof.c @@ -47,7 +47,7 @@ main (int argc, char **argv) { struct event ev; - if (signal(SIGPIPE, SIG_IGN) == SIG_IGN) + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) return (1); if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) diff --git a/varnish-cache/contrib/libevent/test/test.sh b/varnish-cache/contrib/libevent/test/test.sh index d23c1fac..878d468d 100755 --- a/varnish-cache/contrib/libevent/test/test.sh +++ b/varnish-cache/contrib/libevent/test/test.sh @@ -1,17 +1,19 @@ #!/bin/sh setup () { - export EVENT_NOKQUEUE=yes - export EVENT_NODEVPOLL=yes - export EVENT_NOPOLL=yes - export EVENT_NOSELECT=yes - export EVENT_NOEPOLL=yes - export EVENT_NORTSIG=yes + EVENT_NOKQUEUE=yes; export EVENT_NOKQUEUE + EVENT_NODEVPOLL=yes; export EVENT_NODEVPOLL + EVENT_NOPOLL=yes; export EVENT_NOPOLL + EVENT_NOSELECT=yes; export EVENT_NOSELECT + EVENT_NOEPOLL=yes; export EVENT_NOEPOLL + EVENT_NORTSIG=yes; export EVENT_NORTSIG } test () { - if ! ./test-init 2>/dev/null ; + if ./test-init 2>/dev/null ; then + true + else echo Skipping test return fi @@ -51,31 +53,37 @@ echo "Running tests:" # Need to do this by hand? setup unset EVENT_NOKQUEUE +export EVENT_NOKQUEUE echo "KQUEUE" test setup unset EVENT_NODEVPOLL +export EVENT_NODEVPOLL echo "DEVPOLL" test setup unset EVENT_NOPOLL +export EVENT_NOPOLL echo "POLL" test setup unset EVENT_NOSELECT +export EVENT_NOSELECT echo "SELECT" test setup unset EVENT_NORTSIG +export EVENT_NORTSIG echo "RTSIG" test setup unset EVENT_NOEPOLL +export EVENT_NOEPOLL echo "EPOLL" test -- 2.39.5