BBlack has uploaded a new change for review. https://gerrit.wikimedia.org/r/57761
Change subject: Initial code upload, WIP ...................................................................... Initial code upload, WIP Change-Id: I67cbaf13366700762506e272839e2287a8024d94 --- A .gitignore A COPYING A LICENSE A Makefile.am A README.rst A acaux/.gitignore A autogen.sh A configure.ac A m4/.gitignore A src/.gitignore A src/Makefile.am A src/tests/test01.vtc A src/vmod_netmapper.c A src/vmod_netmapper.vcc 14 files changed, 568 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/operations/software/varnish/libvmod-netmapper refs/changes/61/57761/1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..748ee22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Autotools junk at top level +/autom4te.cache/ +/aclocal.m4 +/config.h +/config.h.in +/config.log +/config.status +/configure +/libtool +/stamp-h1 + +# Various tool outputs in all dirs +Makefile +Makefile.in +.dirstamp +.deps +.libs +*.o +*.so +*.lo +*.la +*.gcov +*.trucov +*.info +*.gcda +*.gcno +*.tmp +*.log +*.trs +*~ + +# Generated manpages +*.[0-9] diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..f792696 --- /dev/null +++ b/COPYING @@ -0,0 +1,7 @@ +Copyright (c) 2011 Varnish Software AS +... +See LICENSE for details. + +You're free to use and distribute this under terms in the +LICENSE. Please add your relevant copyright statements. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..264d421 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +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. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d7dcb2b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,21 @@ +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src + +EXTRA_DIST = README.rst + +dist_man_MANS = vmod_netmapper.3 +MAINTAINERCLEANFILES = $(dist_man_MANS) + +vmod_netmapper.3: README.rst + +%.1 %.2 %.3 %.4 %.5 %.6 %.7 %.8 %.9: +if HAVE_RST2MAN + ${RST2MAN} $< $@ +else + @echo "========================================" + @echo "You need rst2man installed to make dist" + @echo "========================================" + @false +endif + diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..ed81b0b --- /dev/null +++ b/README.rst @@ -0,0 +1,100 @@ +================= +libvmod_netmapper +================= + +XXX this project was recently copied from vmod_example. Things like docs +are not at all up to date with the experimental code! + +------------------------------------------------- +Varnish module to map an IP address to a string +------------------------------------------------- + +:Author: Martin Blix Grydeland +:Date: 2011-05-26 +:Version: 1.0 +:Manual section: 3 + +SYNOPSIS +======== + +import netmapper; + +DESCRIPTION +=========== + +Example Varnish vmod demonstrating how to write an out-of-tree Varnish vmod +for Varnish 3.0 and later. + +Implements the traditional Hello World as a vmod. + +FUNCTIONS +========= + +hello +----- + +Prototype + :: + + hello(STRING S) +Return value + STRING +Description + Returns "Hello, " prepended to S +Example + :: + + set resp.http.hello = netmapper.hello("World"); + +INSTALLATION +============ + +This is an netmapper skeleton for developing out-of-tree Varnish +vmods available from the 3.0 release. It implements the "Hello, World!" +as a vmod callback. Not particularly useful in good hello world +tradition,but demonstrates how to get the glue around a vmod working. + +The source tree is based on autotools to configure the building, and +does also have the necessary bits in place to do functional unit tests +using the varnishtest tool. + +Usage:: + + ./configure VARNISHSRC=DIR [VMODDIR=DIR] + +`VARNISHSRC` is the directory of the Varnish source tree for which to +compile your vmod. Both the `VARNISHSRC` and `VARNISHSRC/include` +will be added to the include search paths for your module. + +Optionally you can also set the vmod install directory by adding +`VMODDIR=DIR` (defaults to the pkg-config discovered directory from your +Varnish installation). + +Make targets: + +* make - builds the vmod +* make install - installs your vmod in `VMODDIR` +* make check - runs the unit tests in ``src/tests/*.vtc`` + +In your VCL you could then use this vmod along the following lines:: + + import netmapper; + + sub vcl_deliver { + # This sets resp.http.hello to "Hello, World" + set resp.http.hello = netmapper.hello("World"); + } + +HISTORY +======= + +This manual page was released as part of the libvmod-netmapper package, +demonstrating how to create an out-of-tree Varnish vmod. + +COPYRIGHT +========= + +This document is licensed under the same license as the +libvmod-netmapper project. See LICENSE for details. + +* Copyright (c) 2011 Varnish Software diff --git a/acaux/.gitignore b/acaux/.gitignore new file mode 100644 index 0000000..c470f5d --- /dev/null +++ b/acaux/.gitignore @@ -0,0 +1,8 @@ +ar-lib +config.guess +config.sub +depcomp +install-sh +ltmain.sh +missing +test-driver diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..9a12ef5 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +warn() { + echo "WARNING: $@" 1>&2 +} + +case `uname -s` in +Darwin) + LIBTOOLIZE=glibtoolize + ;; +FreeBSD) + LIBTOOLIZE=libtoolize + ;; +Linux) + LIBTOOLIZE=libtoolize + ;; +SunOS) + LIBTOOLIZE=libtoolize + ;; +*) + warn "unrecognized platform:" `uname -s` + LIBTOOLIZE=libtoolize +esac + +automake_version=`automake --version | tr ' ' '\n' | egrep '^[0-9]\.[0-9a-z.-]+'` +if [ -z "$automake_version" ] ; then + warn "unable to determine automake version" +else + case $automake_version in + 0.*|1.[0-8]|1.[0-8][.-]*) + warn "automake ($automake_version) detected; 1.9 or newer recommended" + ;; + *) + ;; + esac +fi + +set -ex + +aclocal -I m4 +$LIBTOOLIZE --copy --force +autoheader +automake --add-missing --copy --foreign +autoconf diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..192fcea --- /dev/null +++ b/configure.ac @@ -0,0 +1,83 @@ +AC_PREREQ(2.59) +AC_COPYRIGHT([Copyright (c) 2011 Varnish Software AS]) +AC_INIT([libvmod-netmapper], [trunk]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([acaux]) +AC_CONFIG_SRCDIR(src/vmod_netmapper.vcc) +AM_CONFIG_HEADER(config.h) + +AC_CANONICAL_SYSTEM +AC_LANG(C) + +AM_INIT_AUTOMAKE([foreign]) + +AC_GNU_SOURCE +AC_PROG_CC +AC_PROG_CC_STDC +if test "x$ac_cv_prog_cc_c99" = xno; then + AC_MSG_ERROR([Could not find a C99 compatible compiler]) +fi +AC_PROG_CPP + +AC_PROG_INSTALL +AC_PROG_LIBTOOL +AC_PROG_MAKE_SET + +# Check for rst utilities +AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], "no") +if test "x$RST2MAN" = "xno"; then + AC_MSG_WARN([rst2man not found - not building man pages]) +fi +AM_CONDITIONAL(HAVE_RST2MAN, [test "x$RST2MAN" != "xno"]) + +# Check for pkg-config +PKG_PROG_PKG_CONFIG + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([sys/stdlib.h]) + +# Check for python +AC_CHECK_PROGS(PYTHON, [python3 python3.1 python3.2 python2.7 python2.6 python2.5 python2 python], [AC_MSG_ERROR([Python is needed to build this vmod, please install python.])]) + +# Varnish source tree +AC_ARG_VAR([VARNISHSRC], [path to Varnish source tree (mandatory)]) +if test "x$VARNISHSRC" = x; then + AC_MSG_ERROR([No Varnish source tree specified]) +fi +VARNISHSRC=`cd $VARNISHSRC && pwd` +AC_CHECK_FILE([$VARNISHSRC/include/varnishapi.h], + [], + [AC_MSG_FAILURE(["$VARNISHSRC" is not a Varnish source directory])] +) + +# Check that varnishtest is built in the varnish source directory +AC_CHECK_FILE([$VARNISHSRC/bin/varnishtest/varnishtest], + [], + [AC_MSG_FAILURE([Can't find "$VARNISHSRC/bin/varnishtest/varnishtest". Please build your varnish source directory])] +) + +# vmod installation dir +AC_ARG_VAR([VMODDIR], [vmod installation directory @<:@LIBDIR/varnish/vmods@:>@]) +if test "x$VMODDIR" = x; then + VMODDIR=`pkg-config --variable=vmoddir varnishapi` + if test "x$VMODDIR" = x; then + AC_MSG_FAILURE([Can't determine vmod installation directory]) + fi +fi + +# userspace-rcu for lockless netmap reload +AC_CHECK_HEADER(urcu-qsbr.h,[ + AC_CHECK_LIB([urcu-qsbr],[perror],[],AC_MSG_ERROR("liburcu-qsbr missing!")) +], AC_MSG_ERROR("urcu-qsbr.h missing!")) + +# JSON parser for the input data +AC_CHECK_HEADER(jansson.h,[ + AC_CHECK_LIB([jansson],[json_object_update],[],AC_MSG_ERROR("libjansson missing!")) +], AC_MSG_ERROR("jansson.h missing!")) + +AC_CONFIG_FILES([ + Makefile + src/Makefile +]) +AC_OUTPUT diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 0000000..38066dd --- /dev/null +++ b/m4/.gitignore @@ -0,0 +1,5 @@ +libtool.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 +lt~obsolete.m4 diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..64eef33 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +vcc_if.[ch] diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..3d59033 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = -I$(VARNISHSRC)/include -I$(VARNISHSRC) + +vmoddir = $(VMODDIR) +vmod_LTLIBRARIES = libvmod_netmapper.la + +libvmod_netmapper_la_LDFLAGS = -module -export-dynamic -avoid-version -shared +libvmod_netmapper_la_LIBS = -lurcu-qsbr -ljansson +libvmod_netmapper_la_SOURCES = \ + vcc_if.c \ + vcc_if.h \ + vmod_netmapper.c + +vcc_if.c vcc_if.h: $(VARNISHSRC)/lib/libvmod_std/vmod.py $(top_srcdir)/src/vmod_netmapper.vcc + @PYTHON@ $(VARNISHSRC)/lib/libvmod_std/vmod.py $(top_srcdir)/src/vmod_netmapper.vcc + +VMOD_TESTS = tests/*.vtc +.PHONY: $(VMOD_TESTS) + +tests/*.vtc: + $(VARNISHSRC)/bin/varnishtest/varnishtest -Dvarnishd=$(VARNISHSRC)/bin/varnishd/varnishd -Dvmod_topbuild=$(abs_top_builddir) $@ + +check: $(VMOD_TESTS) + +EXTRA_DIST = \ + vmod_netmapper.vcc \ + $(VMOD_TESTS) + +CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h diff --git a/src/tests/test01.vtc b/src/tests/test01.vtc new file mode 100644 index 0000000..721e867 --- /dev/null +++ b/src/tests/test01.vtc @@ -0,0 +1,22 @@ +varnishtest "Test netmapper vmod" + +server s1 { + rxreq + txresp +} -start + +varnish v1 -vcl+backend { + import netmapper from "${vmod_topbuild}/src/.libs/libvmod_netmapper.so"; + + sub vcl_deliver { + set resp.http.hello = netmapper.hello("World"); + } +} -start + +client c1 { + txreq -url "/" + rxresp + expect resp.http.hello == "Hello, World" +} + +client c1 -run diff --git a/src/vmod_netmapper.c b/src/vmod_netmapper.c new file mode 100644 index 0000000..c778267 --- /dev/null +++ b/src/vmod_netmapper.c @@ -0,0 +1,193 @@ +#include "vrt.h" +#include "bin/varnishd/cache.h" +#include "vcc_if.h" + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include <jansson.h> + +#define _LGPL_SOURCE 1 +#include <urcu-qsbr.h> + +// XXX this whole thing needs error retval checking on lib calls, etc... + +typedef struct { + unsigned len; // includes NUL + char* data; // NUL-terminated +} str_t; + +str_t* str_new(const char* input) { + assert(input); + str_t* rv = malloc(sizeof(str_t)); + rv->len = strlen(input) + 1; + rv->data = malloc(rv->len); + memcpy(rv->data, input, rv->len); + return rv; +} + +void str_destroy(str_t* str) { + free(str->data); + free(str); +} + +// Copy a str_t*'s data to a const char* in the session workspace, +// so that after return we're not holding references to data in +// the ndb, so that it can be swapped for update between... +static const char* str_to_vcl(struct sess* sp, const str_t* str) { + const char* rv = NULL; + unsigned used = 0; + const unsigned space = WS_Reserve(sp->ws, 0); + if(space < str->len) { + // XXX not SLT_LostHeader, what? + WSP(sp, SLT_LostHeader, "vmod_netmapper: no space for string retval!"); + } + else { + used = str->len; + rv = sp->ws->f; + memcpy(sp->ws->f, str->data, used); + } + WS_Release(sp->ws, used); + return rv; +} + +/* ndb_t */ + +static pthread_t ndb_updater; + +typedef struct { + // XXX TODO: flesh this out + str_t* the_only_one; +} ndb_t; + +// global singleton, swapped out on update +static ndb_t* ndb; + +static void ndb_destruct(ndb_t* n) { + str_destroy(n->the_only_one); + free(n); +} + +static ndb_t* ndb_parse(void) { + + /* XXX TODO: actually parse JSON into a real datastructure... + json_error_t errobj; + json_t* toplevel = json_load_file("/home/bblack/test.json", 0, &errobj); + */ + + ndb_t* n = malloc(sizeof(ndb_t)); + n->the_only_one = str_new("001-01"); + return n; // XXX or NULL if parse fails +} + +static void* ndb_updater_start(void* x) { + while(1) { + sleep(1); // XXX more like ~15-60s for the real thing, this + // is to help show bugs :) + if(1) { // XXX stat-check indicates reload necc... + ndb_t* new_db = ndb_parse(); + if(new_db) { + ndb_t* old_db = ndb; + rcu_assign_pointer(ndb, new_db); + synchronize_rcu(); + ndb_destruct(old_db); + } + } + } + + return NULL; +} + +static void ndb_fini(void) { + // clean up the updater thread + pthread_cancel(ndb_updater); + pthread_join(ndb_updater, NULL); + + // free the most-recent data + ndb_destruct(ndb); +} + +static void ndb_init(void) { + // initial database load + ndb = ndb_parse(); + // start the updater thread + pthread_create(&ndb_updater, NULL, ndb_updater_start, NULL); +} + +static const str_t* ndb_lookup(const char* addr_str) { + // important! + ndb_t* cur_db = rcu_dereference(ndb); + + // XXX actually look up a specific string + // based on the text address in addr_str + return cur_db->the_only_one; +} + +// Crazy hack to get per-thread rcu register/unregister, even though +// Varnish doesn't give us per-thread hooks for the workers +// (at least, not that I noticed...) +static pthread_key_t unreg_hack; +static pthread_once_t unreg_hack_once = PTHREAD_ONCE_INIT; +static void destruct_rcu(void* x) { pthread_setspecific(unreg_hack, NULL); rcu_unregister_thread(); } +static void make_unreg_hack(void) { pthread_key_create(&unreg_hack, destruct_rcu); } + +static const char* ndb_map(struct sess* sp, const char* ip_string) { + assert(ndb); assert(sp); assert(ip_string); + + // The rest of the rcu register/unregister hack + static __thread bool rcu_registered = false; + if(!rcu_registered) { + pthread_once(&unreg_hack_once, make_unreg_hack); + pthread_setspecific(unreg_hack, (void*)1); + rcu_register_thread(); + rcu_registered = true; + } + + const char* rv = NULL; + + // normal rcu reader stuff + rcu_thread_online(); + rcu_read_lock(); + + // search net database. if match, convert + // string to a vcl string and return it... + const str_t* str = ndb_lookup(ip_string); + if(str) + rv = str_to_vcl(sp, str); + + // normal rcu reader stuff + rcu_read_unlock(); + rcu_thread_offline(); + + return rv; +} + +/***************************** + * Actual VMOD/VCL/VRT Hooks * + *****************************/ + +// init-tracking +static unsigned vcl_count = 0; + +static void fini_func(void* x) { + if(!--vcl_count) + ndb_fini(); +} + +int init_function(struct vmod_priv *priv, const struct VCL_conf *conf) { + assert(priv); + + priv->free = fini_func; + if(!vcl_count++) + ndb_init(); + return 0; +} + +const char* vmod_map(struct sess *sp, const char* ip_string) { + assert(sp); + + CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); // XXX ??? + return ndb_map(sp, ip_string); +} diff --git a/src/vmod_netmapper.vcc b/src/vmod_netmapper.vcc new file mode 100644 index 0000000..6c6642c --- /dev/null +++ b/src/vmod_netmapper.vcc @@ -0,0 +1,3 @@ +Module netmapper +Init init_function +Function STRING map(STRING) -- To view, visit https://gerrit.wikimedia.org/r/57761 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I67cbaf13366700762506e272839e2287a8024d94 Gerrit-PatchSet: 1 Gerrit-Project: operations/software/varnish/libvmod-netmapper Gerrit-Branch: master Gerrit-Owner: BBlack <bbl...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits