Commit 2075ead3 authored by Geoff Simmons's avatar Geoff Simmons

inital commit of a Varnish 4 version -- sources complile, but nothing works

parent c69d5078
#
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src
EXTRA_DIST = LICENSE autogen.sh
DISTCHECK_CONFIGURE_FLAGS = \
--enable-developer-warnings \
--enable-debugging-symbols \
--enable-dependency-tracking \
--enable-diagnostics \
--enable-extra-developer-warnings \
--enable-tests \
--enable-werror
#!/bin/sh
#
warn() {
echo "WARNING: $@" 1>&2
}
case `uname -s` in
Darwin)
LIBTOOLIZE=glibtoolize
;;
FreeBSD)
LIBTOOLIZE=libtoolize
;;
OpenBSD)
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
$LIBTOOLIZE --copy --force
aclocal -I m4
autoheader
automake --add-missing --copy --foreign
autoconf
AC_PREREQ(2.59)
AC_COPYRIGHT([Copyright (c) 2015 UPLEX Nils Goroll Systemoptimierung
Copyright (c) 2015 Otto Gmbh & Co KG])
AC_INIT([varnishevent], [4.0.0])
AC_CONFIG_SRCDIR(src/varnishevent.h)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_SYSTEM
AC_LANG(C)
AM_INIT_AUTOMAKE([foreign])
# Checks for programs.
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
AX_PTHREAD(,[AC_MSG_ERROR([Could not configure pthreads support])])
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
AC_PROG_INSTALL
AC_PROG_LIBTOOL
AC_PROG_MAKE_SET
AC_ARG_WITH([rst2man],
AS_HELP_STRING([--with-rst2man=PATH],
[Location of rst2man (auto)]),
[RST2MAN="$withval"],
[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"])
AC_ARG_WITH([rst2html],
AS_HELP_STRING([--with-rst2html=PATH],
[Location of rst2html (auto)]),
[RST2HTML="$withval"],
[AC_CHECK_PROGS(RST2HTML, [rst2html rst2html.py], "no")
if test "x$RST2HTML" = "xno"; then
AC_MSG_WARN([rst2html not found – not building changelog])
fi])
AM_CONDITIONAL(HAVE_RST2HTML,[test "x$RST2HTML" != "xno"])
# Check for pkg-config
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([VARNISH], [varnishapi >= 4.0.0],
[ac_varnish_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir varnishapi`],
[AC_MSG_ERROR([Varnish >= 4.0.0 installation is required])])
if test "x$ac_varnish_pkgdatadir" = x; then
AC_MSG_ERROR([couldn't get Varnish data dir from pkg-config])
fi
ac_varnish_pkgincludedir=`$PKG_CONFIG --variable=pkgincludedir varnishapi`
ac_varnish_libdir=`$PKG_CONFIG --variable=libdir varnishapi`
AC_SUBST(VARNISH_CFLAGS)
AC_SUBST(VARNISH_LIBS)
AC_SUBST(VARNISH_PKG_INCLUDE, $ac_varnish_pkgincludedir)
AC_SUBST(VARNISH_SHARE_INCLUDE, "$ac_varnish_pkgdatadir/include")
AC_SUBST(VARNISH_PKG_LIB, $ac_varnish_libdir)
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([sys/stdlib.h])
# Checks for libraries.
save_LIBS="${LIBS}"
LIBS=""
AC_CHECK_LIB(rt, clock_gettime)
RT_LIBS="${LIBS}"
LIBS="${save_LIBS}"
AC_SUBST(RT_LIBS)
save_LIBS="${LIBS}"
LIBS=""
AC_CHECK_LIB(dl, dlopen)
DL_LIBS="${LIBS}"
LIBS="${save_LIBS}"
AC_SUBST(DL_LIBS)
save_LIBS="${LIBS}"
LIBS=""
AC_SEARCH_LIBS(initscr, [curses ncurses],
[have_curses=yes], [have_curses=no])
CURSES_LIBS="${LIBS}"
LIBS="${save_LIBS}"
AC_SUBST(CURSES_LIBS)
if test "$have_curses" = no; then
AC_MSG_WARN([curses not found; some tools will not be built])
fi
AC_CHECK_HEADERS([ncurses/curses.h curses.h])
AM_CONDITIONAL([HAVE_CURSES], [test x$have_curses = xyes])
save_LIBS="${LIBS}"
LIBS=""
AC_SEARCH_LIBS(pthread_create, [thr pthread c_r])
PTHREAD_LIBS="${LIBS}"
LIBS="${save_LIBS}"
AC_SUBST(PTHREAD_LIBS)
save_LIBS="${LIBS}"
LIBS=""
AC_CHECK_LIB(socket, socket)
AC_CHECK_LIB(nsl, getaddrinfo)
NET_LIBS="${LIBS}"
LIBS="${save_LIBS}"
AC_SUBST(NET_LIBS)
AC_CHECK_LIBM
AC_SUBST(LIBM)
m4_ifndef([PKG_PROG_PKG_CONFIG], [m4_fatal([pkg.m4 missing, please install pkg-config])])
PKG_PROG_PKG_CONFIG
if test -n $PKG_CONFIG; then
PKG_CHECK_MODULES([PCRE], [libpcre])
else
AC_CHECK_PROG(PCRE_CONFIG, pcre-config, pcre-config)
AC_ARG_WITH(pcre-config,
AS_HELP_STRING([--with-pcre-config=PATH],
[Location of PCRE pcre-config (auto)]),
[pcre_config="$withval"],
[pcre_config=""])
if test "x$pcre_config" != "x" ; then
AC_MSG_CHECKING(for $pcre_config)
if test -f $pcre_config ; then
PCRE_CONFIG=$pcre_config
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no - searching PATH)
fi
fi
if test "x$PCRE_CONFIG" = "x"; then
AC_CHECK_PROGS(PCRE_CONFIG, pcre-config)
fi
PCRE_CFLAGS=`$PCRE_CONFIG --cflags`
PCRE_LIBS=`$PCRE_CONFIG --libs`
fi
AC_SUBST(PCRE_CFLAGS)
AC_SUBST(PCRE_LIBS)
PKG_CHECK_MODULES([LIBEDIT], [libedit],
[AC_DEFINE([HAVE_LIBEDIT], [1], [Define we have libedit])],
[AX_LIB_READLINE])
if test "$ac_cv_have_readline" = no; then
AC_MSG_ERROR([libedit or readline not found])
fi
# Checks for header files.
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_HEADER_TIME
AC_CHECK_HEADERS([sys/param.h])
AC_CHECK_HEADERS([sys/types.h])
AC_CHECK_HEADERS([sys/endian.h])
AC_CHECK_HEADERS([sys/filio.h])
AC_CHECK_HEADERS([sys/mount.h], [], [], [#include <sys/param.h>])
AC_CHECK_HEADERS([sys/socket.h])
AC_CHECK_HEADERS([sys/statvfs.h])
AC_CHECK_HEADERS([sys/vfs.h])
AC_CHECK_HEADERS([endian.h])
AC_CHECK_HEADERS([execinfo.h])
AC_CHECK_HEADERS([netinet/in.h])
AC_CHECK_HEADERS([pthread_np.h])
AC_CHECK_HEADERS([stddef.h])
AC_CHECK_HEADERS([stdlib.h])
AC_CHECK_HEADERS([unistd.h])
AC_CHECK_HEADERS([priv.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_CHECK_MEMBERS([struct sockaddr.sa_len],,,[
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
])
# Checks for library functions.
AC_TYPE_SIGNAL
AC_TYPE_SIZE_T
AC_FUNC_VPRINTF
AC_CHECK_FUNCS([strerror])
AC_FUNC_STRERROR_R
AC_CHECK_FUNCS([dladdr])
AC_CHECK_FUNCS([socket])
AC_CHECK_FUNCS([strptime])
AC_CHECK_FUNCS([fmtcheck])
AC_CHECK_FUNCS([getdtablesize])
AC_CHECK_FUNCS([abort2])
AC_CHECK_FUNCS([timegm])
AC_CHECK_FUNCS([nanosleep])
AC_CHECK_FUNCS([setppriv])
save_LIBS="${LIBS}"
LIBS="${PTHREAD_LIBS}"
AC_CHECK_FUNCS([pthread_set_name_np])
AC_CHECK_FUNCS([pthread_mutex_isowned_np])
AC_CHECK_FUNCS([pthread_timedjoin_np])
LIBS="${save_LIBS}"
# sendfile is tricky: there are multiple versions, and most of them
# don't work.
case $target in
*-*-freebsd*)
AC_CACHE_CHECK([whether sendfile works],
[ac_cv_so_sendfile_works],
[AC_RUN_IFELSE(
[AC_LANG_PROGRAM([[
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
]],[[
return (SF_SYNC == 0);
]])],
[ac_cv_so_sendfile_works=yes],
[ac_cv_so_sendfile_works=no])
])
;;
#*-*-solaris*)
# save_LIBS="${LIBS}"
# LIBS="${NET_LIBS}"
# AC_CHECK_LIB(sendfile, sendfile)
# AC_CHECK_FUNCS([sendfile])
# AC_CHECK_FUNCS([sendfilev])
# NET_LIBS="${LIBS}"
# LIBS="${save_LIBS}"
*)
AC_MSG_WARN([won't look for sendfile() on $target])
;;
esac
if test "$ac_cv_so_sendfile_works" = yes; then
AC_DEFINE([SENDFILE_WORKS], [1], [Define if SENDFILE works])
fi
# Support for visibility attribute
save_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} -Werror"
AC_CACHE_CHECK([whether we have support for visibility attributes],
[ac_cv_have_viz],
[AC_RUN_IFELSE(
[AC_LANG_PROGRAM([[
#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33)
# define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
#else
# define ZLIB_INTERNAL
#endif
int ZLIB_INTERNAL foo;
]],[])],
[ac_cv_have_viz=yes],
[ac_cv_have_viz=no])
])
if test "$ac_cv_have_viz" = no; then
libvgz_extra_cflags="-DNO_VIZ"
AC_SUBST(libvgz_extra_cflags)
fi
CFLAGS="${save_CFLAGS}"
# Use jemalloc on Linux
JEMALLOC_SUBDIR=
JEMALLOC_LDADD=
AC_ARG_WITH([jemalloc],
[AS_HELP_STRING([--with-jemalloc],
[use jemalloc memory allocator. Default is yes on Linux, no elsewhere])],
[],
[with_jemalloc=check])
case $target in
*-*-linux*)
if test "x$with_jemalloc" != xno; then
AC_CHECK_LIB([jemalloc], [malloc_conf],
[JEMALLOC_LDADD="-ljemalloc"],
[AC_MSG_NOTICE([No system jemalloc found, using bundled version])
JEMALLOC_SUBDIR=libjemalloc
JEMALLOC_LDADD='$(top_builddir)/lib/libjemalloc/libjemalloc_mt.la'])
fi
;;
esac
AC_SUBST(JEMALLOC_SUBDIR)
AC_SUBST(JEMALLOC_LDADD)
# Userland slab allocator, available only on Solaris
case $target in
*-*-solaris*)
AC_CHECK_HEADERS([umem.h])
if test "$ac_cv_have_umem_h" = yes; then
save_LIBS="${LIBS}"
LIBS=""
AC_CHECK_LIB(umem, umem_alloc)
LIBUMEM="${LIBS}"
LIBS="${save_LIBS}"
fi
;;
esac
AC_SUBST(LIBUMEM)
# These functions are provided by libcompat on platforms where they
# are not available
AC_CHECK_FUNCS([setproctitle])
AC_CHECK_FUNCS([srandomdev])
AC_CHECK_FUNCS([backtrace])
# white lie - we don't actually test it
AC_MSG_CHECKING([whether daemon() works])
case $target in
*-*-darwin*)
# present but not functional
AC_MSG_RESULT([no])
ac_cv_func_daemon=no
;;
*)
AC_CHECK_FUNCS([daemon])
;;
esac
AC_SYS_LARGEFILE
save_LIBS="${LIBS}"
LIBS="${LIBS} ${RT_LIBS}"
AC_CHECK_FUNCS([clock_gettime])
AC_CHECK_FUNCS([gethrtime])
LIBS="${save_LIBS}"
# --enable-kqueue
AC_ARG_ENABLE(kqueue,
AS_HELP_STRING([--enable-kqueue],
[use kqueue if available (default is YES)]),
,
[enable_kqueue=yes])
if test "$enable_kqueue" = yes; then
AC_CHECK_FUNCS([kqueue])
else
ac_cv_func_kqueue=no
fi
# --enable-epoll
AC_ARG_ENABLE(epoll,
AS_HELP_STRING([--enable-epoll],
[use epoll if available (default is YES)]),
,
[enable_epoll=yes])
if test "$enable_epoll" = yes; then
AC_CHECK_FUNCS([epoll_ctl])
else
ac_cv_func_epoll_ctl=no
fi
# --enable-ports
AC_ARG_ENABLE(ports,
AS_HELP_STRING([--enable-ports],
[use ports if available (default is YES)]),
,
[enable_ports=yes])
if test "$enable_ports" = yes; then
AC_CHECK_FUNCS([port_create])
else
ac_cv_func_port_create=no
fi
AM_MISSING_HAS_RUN
AC_CHECK_PROGS(PYTHON, [python3 python3.1 python3.2 python2.7 python2.6 python2.5 python2 python], "no")
if test "x$PYTHON" = "xno"; then
AC_MSG_ERROR([Python is needed to build Varnish, please install python.])
fi
# Older Solaris versions define SO_{RCV,SND}TIMEO, but do not
# implement them.
#
# Varnish will build and run without these, but connections will not
# time out, which may leave Varnish vulnerable to denail-of-service
# attacks which would not be possible on other platforms.
#
# Newer Solaris releases with the Volo framework (Solaris 11,
# Opensolaris starting with onnv_106) do support SO_{RCV,SND}TIMEO
# (see PSARC 2007/587, initially committed into onnv-gate /
# OpenSolaris 8348:4137e18bfaf0 Thu Dec 11 20:04:13 2008)
save_LIBS="${LIBS}"
LIBS="${LIBS} ${NET_LIBS}"
AC_CACHE_CHECK([whether SO_RCVTIMEO works],
[ac_cv_so_rcvtimeo_works],
[AC_RUN_IFELSE(
[AC_LANG_PROGRAM([[
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
]],[[
int sd = socket(AF_INET, SOCK_STREAM, 0);
struct timeval tv = { 1, 0 };
if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv) == 0) {
socklen_t l = sizeof tv;
if (getsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &tv, &l) == 0) {
return (l != sizeof tv);
}
}
return 1;
]])],
[ac_cv_so_rcvtimeo_works=yes],
[ac_cv_so_rcvtimeo_works=no])
])
if test "$ac_cv_so_rcvtimeo_works" = yes; then
AC_DEFINE([SO_RCVTIMEO_WORKS], [1], [Define if SO_RCVTIMEO works])
fi
LIBS="${save_LIBS}"
save_LIBS="${LIBS}"
LIBS="${LIBS} ${NET_LIBS}"
AC_CACHE_CHECK([whether SO_SNDTIMEO works],
[ac_cv_so_sndtimeo_works],
[AC_RUN_IFELSE(
[AC_LANG_PROGRAM([[
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
]],[[
int sd = socket(AF_INET, SOCK_STREAM, 0);
struct timeval tv = { 1, 0 };
if (setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv) == 0) {
socklen_t l = sizeof tv;
if (getsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, &tv, &l) == 0) {
return (l != sizeof tv);
}
}
return 1;
]])],
[ac_cv_so_sndtimeo_works=yes],
[ac_cv_so_sndtimeo_works=no])
])
if test "$ac_cv_so_sndtimeo_works" = yes; then
AC_DEFINE([SO_SNDTIMEO_WORKS], [1], [Define if SO_SNDTIMEO works])
fi
if test "$ac_cv_so_rcvtimeo_works" = no ||
test "$ac_cv_so_sndtimeo_works" = no; then
AC_MSG_WARN([connection timeouts will not work])
fi
LIBS="${save_LIBS}"
# Run-time directory
VARNISH_STATE_DIR='${localstatedir}/varnish'
AC_SUBST(VARNISH_STATE_DIR)
# Default configuration directory.
varnishconfdir='${sysconfdir}/varnish'
AC_SUBST(varnishconfdir)
# Check for linker script support
gl_LD_VERSION_SCRIPT
# Now that we're done using the compiler to look for functions and
# libraries, set CFLAGS to what we want them to be for our own code
# This corresponds to FreeBSD's WARNS level 6
DEVELOPER_CFLAGS="-Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wcast-align -Wunused-parameter -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls -Wformat"
# Additional flags for GCC 4
EXTRA_DEVELOPER_CFLAGS="-Wextra -Wno-missing-field-initializers -Wno-sign-compare"
# --enable-developer-warnings
AC_ARG_ENABLE(developer-warnings,
AS_HELP_STRING([--enable-developer-warnings],[enable strict warnings (default is NO)]),
CFLAGS="${CFLAGS} ${DEVELOPER_CFLAGS}")
# --enable-debugging-symbols
AC_ARG_ENABLE(debugging-symbols,
AS_HELP_STRING([--enable-debugging-symbols],[enable debugging symbols (default is NO)]),
CFLAGS="${CFLAGS} -O0 -g -fno-inline")
# --enable-diagnostics
AC_ARG_ENABLE(diagnostics,
AS_HELP_STRING([--enable-diagnostics],[enable run-time diagnostics (default is NO)]),
CFLAGS="${CFLAGS} -DDIAGNOSTICS")
# --enable-extra-developer-warnings
AC_ARG_ENABLE(extra-developer-warnings,
AS_HELP_STRING([--enable-extra-developer-warnings],[enable even stricter warnings (default is NO)]),
[],
[enable_extra_developer_warnings=no])
if test "x$enable_stack_protector" != "xno"; then
save_CFLAGS="$CFLAGS"
CFLAGS="${CFLAGS} ${EXTRA_DEVELOPER_CFLAGS}"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([],[],[])],
[],
[AC_MSG_WARN([All of ${EXTRA_DEVELOPER_CFLAGS} not supported, disabling])
CFLAGS="$save_CFLAGS"])
fi
# --enable-stack-protector
AC_ARG_ENABLE(stack-protector,
AS_HELP_STRING([--enable-stack-protector],[enable stack protector (default is NO)]),
[],
[enable_stack_protector=no])
if test "x$enable_stack_protector" != "xno"; then
save_CFLAGS="$CFLAGS"
CFLAGS="${CFLAGS} -fstack-protector-all"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([],[],[])],
[],
[AC_MSG_WARN([-fstack-protector not supported, disabling])
CFLAGS="$save_CFLAGS"])
fi
# --enable-tests
AC_ARG_ENABLE(tests,
AS_HELP_STRING([--enable-tests],[build test programs (default is NO)]))
AM_CONDITIONAL([ENABLE_TESTS], [test x$enable_tests = xyes])
# --enable-werror
AC_ARG_ENABLE(werror,
AS_HELP_STRING([--enable-werror],[use -Werror (default is NO)]),
CFLAGS="${CFLAGS} -Werror")
# Command line for compiling VCL code. I wish there were a simple way
# to figure this out dynamically without introducing a run-time
# dependency on libtool.
AC_ARG_VAR([VCC_CC], [C compiler command line for VCL code])
if test "$ac_cv_env_VCC_CC_set" = "set"; then
VCC_CC="$ac_cv_env_VCC_CC_value"
else
case $target in
*-*-solaris*)
case $PTHREAD_CC in
*gcc*)
VCC_CC="$PTHREAD_CC $OCFLAGS $PTHREAD_CFLAGS -fpic -shared -o %o %s"
break
;;
*cc)
VCC_CC="$PTHREAD_CC $OCFLAGS $PTHREAD_CFLAGS -Kpic -G -o %o %s"
;;
esac
;;
*-*-darwin*)
VCC_CC="exec cc $OCFLAGS -dynamiclib -Wl,-undefined,dynamic_lookup -o %o %s"
;;
*)
VCC_CC="exec $PTHREAD_CC $OCFLAGS $PTHREAD_CFLAGS -fpic -shared -Wl,-x -o %o %s"
;;
esac
fi
AC_DEFINE_UNQUOTED([VCC_CC],"$VCC_CC",[C compiler command line for VCL code])
AM_CONDITIONAL([HAVE_TMPDIR], [test x$TMPDIR != x])
# --enable-pcre-jit
AC_ARG_ENABLE(pcre-jit,
AS_HELP_STRING([--enable-pcre-jit],
[use the PCRE JIT compiler (default is NO)]),
,
[enable_pcre_jit=no])
if test "$enable_pcre_jit" = yes; then
AC_DEFINE([USE_PCRE_JIT],[1],[use the PCRE JIT compiler])
fi
# Generate output
AC_CONFIG_FILES([
Makefile
src/Makefile
src/test/Makefile
])
AC_OUTPUT
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_lib_readline.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_LIB_READLINE
#
# DESCRIPTION
#
# Searches for a readline compatible library. If found, defines
# `HAVE_LIBREADLINE'. If the found library has the `add_history' function,
# sets also `HAVE_READLINE_HISTORY'. Also checks for the locations of the
# necessary include files and sets `HAVE_READLINE_H' or
# `HAVE_READLINE_READLINE_H' and `HAVE_READLINE_HISTORY_H' or
# 'HAVE_HISTORY_H' if the corresponding include files exists.
#
# The libraries that may be readline compatible are `libedit',
# `libeditline' and `libreadline'. Sometimes we need to link a termcap
# library for readline to work, this macro tests these cases too by trying
# to link with `libtermcap', `libcurses' or `libncurses' before giving up.
#
# Here is an example of how to use the information provided by this macro
# to perform the necessary includes or declarations in a C file:
#
# #ifdef HAVE_LIBREADLINE
# # if defined(HAVE_READLINE_READLINE_H)
# # include <readline/readline.h>
# # elif defined(HAVE_READLINE_H)
# # include <readline.h>
# # else /* !defined(HAVE_READLINE_H) */
# extern char *readline ();
# # endif /* !defined(HAVE_READLINE_H) */
# char *cmdline = NULL;
# #else /* !defined(HAVE_READLINE_READLINE_H) */
# /* no readline */
# #endif /* HAVE_LIBREADLINE */
#
# #ifdef HAVE_READLINE_HISTORY
# # if defined(HAVE_READLINE_HISTORY_H)
# # include <readline/history.h>
# # elif defined(HAVE_HISTORY_H)
# # include <history.h>
# # else /* !defined(HAVE_HISTORY_H) */
# extern void add_history ();
# extern int write_history ();
# extern int read_history ();
# # endif /* defined(HAVE_READLINE_HISTORY_H) */
# /* no history */
# #endif /* HAVE_READLINE_HISTORY */
#
# LICENSE
#
# Copyright (c) 2008 Ville Laurikari <vl@iki.fi>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 6
AU_ALIAS([VL_LIB_READLINE], [AX_LIB_READLINE])
AC_DEFUN([AX_LIB_READLINE], [
AC_CACHE_CHECK([for a readline compatible library],
ax_cv_lib_readline, [
ORIG_LIBS="$LIBS"
for readline_lib in readline edit editline; do
for termcap_lib in "" termcap curses ncurses; do
if test -z "$termcap_lib"; then
TRY_LIB="-l$readline_lib"
else
TRY_LIB="-l$readline_lib -l$termcap_lib"
fi
LIBS="$ORIG_LIBS $TRY_LIB"
AC_TRY_LINK_FUNC(readline, ax_cv_lib_readline="$TRY_LIB")
if test -n "$ax_cv_lib_readline"; then
break
fi
done
if test -n "$ax_cv_lib_readline"; then
break
fi
done
if test -z "$ax_cv_lib_readline"; then
ax_cv_lib_readline="no"
fi
LIBS="$ORIG_LIBS"
])
if test "$ax_cv_lib_readline" != "no"; then
LIBS="$LIBS $ax_cv_lib_readline"
AC_DEFINE(HAVE_LIBREADLINE, 1,
[Define if you have a readline compatible library])
AC_CHECK_HEADERS(readline.h readline/readline.h)
AC_CACHE_CHECK([whether readline supports history],
ax_cv_lib_readline_history, [
ax_cv_lib_readline_history="no"
AC_TRY_LINK_FUNC(add_history, ax_cv_lib_readline_history="yes")
])
if test "$ax_cv_lib_readline_history" = "yes"; then
AC_DEFINE(HAVE_READLINE_HISTORY, 1,
[Define if your readline library has \`add_history'])
AC_CHECK_HEADERS(history.h readline/history.h)
fi
fi
])dnl
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well. e.g. you should link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threads programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 7
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_SAVE
AC_LANG_C
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
AC_MSG_RESULT($ax_pthread_ok)
if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case "${host_cpu}-${host_os}" in
*solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
;;
*-darwin*)
acx_pthread_flags="-pthread $acx_pthread_flags"
;;
esac
if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do
case $flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $flag])
PTHREAD_CFLAGS="$flag"
;;
pthread-config)
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$flag])
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_TRY_LINK([#include <pthread.h>
static void routine(void* a) {a=0;}
static void* start_routine(void* a) {return a;}],
[pthread_t th; pthread_attr_t attr;
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_create(&th,0,start_routine,0);
pthread_cleanup_pop(0); ],
[ax_pthread_ok=yes])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($ax_pthread_ok)
if test "x$ax_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_MSG_CHECKING([for joinable pthread attribute])
attr_name=unknown
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
[attr_name=$attr; break])
done
AC_MSG_RESULT($attr_name)
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_CHECKING([if more special flags are required for pthreads])
flag=no
case "${host_cpu}-${host_os}" in
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: must compile with xlc_r or cc_r
if test x"$GCC" != xyes; then
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
else
PTHREAD_CC=$CC
fi
else
PTHREAD_CC="$CC"
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_RESTORE
])dnl AX_PTHREAD
# ld-version-script.m4 serial 1
dnl Copyright (C) 2008, 2009 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
dnl From Simon Josefsson
# FIXME: The test below returns a false positive for mingw
# cross-compiles, 'local:' statements does not reduce number of
# exported symbols in a DLL. Use --disable-ld-version-script to work
# around the problem.
# gl_LD_VERSION_SCRIPT
# --------------------
# Check if LD supports linker scripts, and define automake conditional
# HAVE_LD_VERSION_SCRIPT if so.
AC_DEFUN([gl_LD_VERSION_SCRIPT],
[
AC_ARG_ENABLE([ld-version-script],
AS_HELP_STRING([--enable-ld-version-script],
[enable linker version script (default is enabled when possible)]),
[have_ld_version_script=$enableval], [])
if test -z "$have_ld_version_script"; then
AC_MSG_CHECKING([if LD -Wl,--version-script works])
save_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,--version-script=conftest.map"
cat > conftest.map <<EOF
VERS_1 {
global: sym;
};
VERS_2 {
global: sym;
} VERS_1;
EOF
AC_LINK_IFELSE(AC_LANG_PROGRAM([], []),
[have_ld_version_script=yes], [have_ld_version_script=no])
rm -f conftest.map
LDFLAGS="$save_LDFLAGS"
AC_MSG_RESULT($have_ld_version_script)
fi
AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
])
#
INCLUDES = -I${VARNISH_SHARE_INCLUDE} -I${VARNISH_PKG_INCLUDE}
SUBDIRS = test
bin_PROGRAMS = varnishevent
dist_man_MANS = varnishevent.1
varnishevent_SOURCES = \
varnishevent.c \
varnishevent.h \
signals.h \
base64.c \
base64.h \
data.c \
spscq.c \
writer.c \
config.c \
log.c \
monitor.c
# format.c \
# handler.c \
# strfTIM.h \
# strfTIM.c
varnishevent_LDADD = \
${PTHREAD_LIBS} ${RT_LIBS} ${LIBM} @VARNISH_LIBS@ \
-L${VARNISH_PKG_LIB}/varnish -lvarnish
varnishevent.1: $(top_srcdir)/doc/varnishevent.rst
if HAVE_RST2MAN
${RST2MAN} $? $@
else
@echo "========================================"
@echo "You need rst2man installed to make dist"
@echo "========================================"
@false
endif
/*
* Written by Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* This file is in the public domain.
*/
#include "config.h"
#include <sys/types.h>
#include "varnishapi.h"
#include "base64.h"
static const char b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static char i64[256];
void
VB64_init(void)
{
int i;
const char *p;
for (i = 0; i < 256; i++)
i64[i] = -1;
for (p = b64, i = 0; *p; p++, i++)
i64[(int)*p] = (char)i;
i64['='] = 0;
}
int
VB64_decode(char *d, unsigned dlen, const char *s)
{
unsigned u, v, l;
int i;
u = 0;
l = 0;
while (*s) {
for (v = 0; v < 4; v++) {
if (!*s)
break;
i = i64[(int)*s++];
if (i < 0)
return (-1);
u <<= 6;
u |= i;
}
for (v = 0; v < 3; v++) {
if (l >= dlen - 1)
return (-1);
*d = (u >> 16) & 0xff;
u <<= 8;
l++;
d++;
}
}
*d = '\0';
return (0);
}
#ifdef TEST_DRIVER
#include <stdio.h>
const char *test1 =
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=";
int
main(int argc, char **argv)
{
int i;
char buf[BUFSIZ];
unsigned l;
(void)argc;
(void)argv;
VB64_init();
l = sizeof buf;
VB64_decode(buf, &l, test1);
printf("%s\n", buf);
return (0);
}
#endif
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2011 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* 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.
*
*/
void VB64_init(void);
int VB64_decode(char *d, unsigned dlen, const char *s);
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <syslog.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <unistd.h>
#include <pwd.h>
#include "varnishevent.h"
#include "vas.h"
#define DEFAULT_USER "nobody"
static const int facilitynum[8] =
{ LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5,
LOG_LOCAL6, LOG_LOCAL7 };
static int
conf_getFacility(const char *facility) {
int localnum;
if (strcasecmp(facility, "USER") == 0)
return LOG_USER;
if (strlen(facility) != 6
|| strncasecmp(facility, "LOCAL", 5) != 0
|| !isdigit(facility[5]))
return(-1);
localnum = atoi(&facility[5]);
if (localnum > 7)
return(-1);
return(facilitynum[localnum]);
}
static int
conf_getUnsignedInt(const char *rval, unsigned *i)
{
long n;
char *p;
errno = 0;
n = strtoul(rval, &p, 10);
if (errno)
return(errno);
if (strlen(p) != 0)
return(EINVAL);
if (n < 0 || n > UINT_MAX)
return(ERANGE);
*i = (unsigned int) n;
return(0);
}
#define confString(name,fld) \
if (strcmp(lval, (name)) == 0) { \
strcpy((config.fld), rval); \
return(0); \
}
#define confUnsigned(name,fld) \
if (strcmp(lval, name) == 0) { \
unsigned int i; \
int err = conf_getUnsignedInt(rval, &i); \
if (err != 0) \
return err; \
config.fld = i; \
return(0); \
}
int
CONF_Add(const char *lval, const char *rval)
{
int ret;
confString("pid.file", pid_file);
confString("varnish.name", varnish_name);
confString("log.file", log_file);
confString("varnish.bindump", varnish_bindump);
confString("cformat", cformat);
confString("bformat", bformat);
confString("zformat", zformat);
confString("output.file", output_file);
confString("syslog.ident", syslog_ident);
confUnsigned("max.reclen", max_reclen);
confUnsigned("max.headers", max_headers);
confUnsigned("max.vcl_log", max_vcl_log);
confUnsigned("max.vcl_call", max_vcl_call);
confUnsigned("max.fd", max_fd);
confUnsigned("max.data", max_data);
confUnsigned("monitor.interval", monitor_interval);
confUnsigned("output.bufsiz", output_bufsiz);
confUnsigned("housekeep.interval", housekeep_interval);
confUnsigned("ttl", ttl);
confUnsigned("append", append);
if (strcmp(lval, "syslog.facility") == 0) {
if ((ret = conf_getFacility(rval)) < 0)
return EINVAL;
config.syslog_facility = ret;
strcpy(config.syslog_facility_name, rval);
char *p = &config.syslog_facility_name[0];
do { *p = toupper(*p); } while (*++p);
return(0);
}
if (strcmp(lval, "user") == 0) {
struct passwd *pw;
pw = getpwnam(rval);
if (pw == NULL)
return(EINVAL);
strcpy(config.user_name, pw->pw_name);
config.uid = pw->pw_uid;
config.gid = pw->pw_gid;
return(0);
}
if (strcmp(lval, "output.timeout") == 0) {
char *p;
errno = 0;
double to = strtod(rval, &p);
if (errno == ERANGE)
return errno;
if (p[0] != '\0' || to < 0 || isnan(to) || !finite(to))
return EINVAL;
config.output_timeout.tv_sec = trunc(to);
config.output_timeout.tv_usec = (int)(1e6 * (to - trunc(to)));
return(0);
}
return EINVAL;
}
static int
conf_ParseLine(char *ptr, char **lval, char **rval)
{
char *endlval;
*lval = ptr;
while(*++ptr && !isspace(*ptr) && *ptr != '=')
;
if (*ptr == '\0')
return(1);
endlval = ptr;
while(isspace(*ptr) && *++ptr)
;
if (ptr == '\0' || *ptr != '=')
return(1);
while(*++ptr && isspace(*ptr))
;
if (ptr == '\0')
return(1);
*endlval = '\0';
*rval = ptr;
return(0);
}
void
CONF_Init(void)
{
struct passwd *pw;
strcpy(config.pid_file, DEFAULT_PID_FILE);
strcpy(config.cformat, DEFAULT_CFORMAT);
strcpy(config.syslog_ident, "varnishevent");
config.bformat[0] = '\0';
config.zformat[0] = '\0';
config.varnish_name[0] = '\0';
config.log_file[0] = '\0';
config.varnish_bindump[0] = '\0';
config.syslog_facility = LOG_LOCAL0;
strcpy(config.syslog_facility_name, "LOCAL0");
config.monitor_interval = 30;
config.output_bufsiz = BUFSIZ;
config.max_reclen = DEFAULT_MAX_RECLEN;
config.max_headers = DEFAULT_MAX_HEADERS;
config.max_vcl_log = DEFAULT_MAX_HEADERS;
config.max_vcl_call = DEFAULT_MAX_HEADERS;
config.max_fd = DEFAULT_MAX_FD;
config.max_data = DEFAULT_MAX_DATA;
config.housekeep_interval = DEFAULT_HOUSEKEEP_INTERVAL;
config.ttl = DEFAULT_TTL;
/* Default is stdout */
config.output_file[0] = '\0';
config.append = 0;
config.output_timeout.tv_sec = 0;
config.output_timeout.tv_usec = 0;
pw = getpwnam(DEFAULT_USER);
if (pw == NULL)
pw = getpwuid(getuid());
AN(pw);
strcpy(config.user_name, pw->pw_name);
config.uid = pw->pw_uid;
config.gid = pw->pw_gid;
}
int
CONF_ReadFile(const char *file) {
FILE *in;
char line[BUFSIZ];
int linenum = 0;
in = fopen(file, "r");
if (in == NULL) {
perror(file);
return(-1);
}
while (fgets(line, BUFSIZ, in) != NULL) {
char orig[BUFSIZ];
linenum++;
char *comment = strchr(line, '#');
if (comment != NULL)
*comment = '\0';
if (strlen(line) == 0)
continue;
char *ptr = line + strlen(line) - 1;
while (ptr != line && isspace(*ptr))
--ptr;
ptr[isspace(*ptr) ? 0 : 1] = '\0';
if (strlen(line) == 0)
continue;
ptr = line;
while (isspace(*ptr) && *++ptr)
;
strcpy(orig, ptr);
char *lval, *rval;
if (conf_ParseLine(ptr, &lval, &rval) != 0) {
fprintf(stderr, "Cannot parse %s line %d: '%s'\n", file, linenum,
orig);
return(-1);
}
int ret;
if ((ret = CONF_Add((const char *) lval, (const char *) rval)) != 0) {
fprintf(stderr, "Error in %s line %d (%s): '%s'\n", file, linenum,
strerror(ret), orig);
return(-1);
}
}
fclose(in);
return(0);
}
#define confdump(str,val) \
LOG_Log(LOG_INFO, "config: " str, (val))
void
CONF_Dump(void)
{
confdump("pid.file = %s", config.pid_file);
confdump("varnish.name = %s", config.varnish_name);
confdump("log.file = %s",
strcmp(config.log_file,"-") == 0 ? "stdout" : config.log_file);
confdump("varnish.bindump = %s", config.varnish_bindump);
confdump("output.file = %s",
EMPTY(config.output_file) ? "stdout" : config.output_file);
confdump("append = %u", config.append);
confdump("output.timeout = %f",
config.output_timeout.tv_sec
+ (double) config.output_timeout.tv_usec / 1e-6);
confdump("cformat = %s", config.cformat);
confdump("bformat = %s", config.bformat);
confdump("zformat = %s", config.zformat);
confdump("syslog.facility = %s", config.syslog_facility_name);
confdump("syslog.ident = %s", config.syslog_ident);
confdump("monitor.interval = %u", config.monitor_interval);
confdump("max.reclen = %u", config.max_reclen);
confdump("max.headers = %u", config.max_headers);
confdump("max.vcl_log = %u", config.max_vcl_log);
confdump("max.vcl_call = %u", config.max_vcl_call);
confdump("max.fd = %u", config.max_fd);
confdump("max.data = %u", config.max_data);
confdump("housekeep.interval = %u", config.housekeep_interval);
confdump("ttl = %u", config.ttl);
confdump("output.bufsiz = %u", config.output_bufsiz);
confdump("user = %s", config.user_name);
}
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Authors: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <pthread.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>
#include "varnishevent.h"
#include "vas.h"
#include "miniobj.h"
#include "vqueue.h"
#include "vsb.h"
#define FAKE_DEFAULT_LINES_PER_TX 10
#if 0
static const char *statename[3] = { "EMPTY", "OPEN", "DONE" };
#endif
static pthread_mutex_t freelist_lock = PTHREAD_MUTEX_INITIALIZER;
static char *bufptr;
static int lines_per_tx = FAKE_DEFAULT_LINES_PER_TX;
#if 0
static void
free_hdrs(hdr_t *hdrs)
{
if (hdrs != NULL) {
free(hdrs->record);
free(hdrs);
}
}
#endif
static void
data_Cleanup(void)
{
for (int i = 0; i < config.max_data; i++) {
/* XXX: etc. ... */
}
free(txn);
free(bufptr);
AZ(pthread_mutex_destroy(&freelist_lock));
}
void
DATA_Clear_Logline(tx_t *tx)
{
CHECK_OBJ_NOTNULL(tx, TX_MAGIC);
tx->state = TX_EMPTY;
/* XXX: etc. ... */
}
#define INIT_HDR_RECORDS(tag, hdr, max) do { \
if (FMT_Read_Hdr(tag)) { \
hdr = (hdr_t *) malloc(sizeof(hdr_t)); \
if (hdr == NULL) \
return errno; \
hdr->record \
= (record_t *) calloc(max, sizeof(record_t)); \
if (hdr->record == NULL) \
return errno; \
for (int j = 0; j < max; j++) { \
hdr->record[j].magic = RECORD_MAGIC; \
hdr->record[j].data = &bufptr[bufidx++ * config.max_reclen]; \
} \
} \
else { \
hdr = NULL; \
} \
} while(0)
int
DATA_Init(void)
{
int bufidx = 0;
int nrecords;
#if 0
lines_per_tx = FMT_Get_nTags();
#endif
nrecords = config.max_data * lines_per_tx;
/* XXX: set up tables of txen, lines & chunks, set/estimate sizes */
LOG_Log(LOG_DEBUG, "Allocating space for %d records (%d bytes)", nrecords,
nrecords * config.max_reclen);
bufptr = (char *) calloc(nrecords, config.max_reclen);
if (bufptr == NULL)
return errno;
txn = (tx_t *) calloc(config.max_data, sizeof(tx_t));
if (txn == NULL)
return errno;
VSTAILQ_INIT(&freetxhead);
for (int i = 0; i < config.max_data; i++) {
/* XXX: init */
txn[i].magic = TX_MAGIC;
DATA_Clear_Logline(&txn[i]);
VSTAILQ_INSERT_TAIL(&freetxhead, &txn[i], freelist);
}
assert(bufidx == nrecords);
data_open = data_done = data_occ_hi = 0;
global_nfree = config.max_data;
atexit(data_Cleanup);
return(0);
}
/*
* take all free entries from the datatable for lockless allocation
*/
unsigned
DATA_Take_Freelist(struct txhead_s *dst)
{
unsigned nfree;
AZ(pthread_mutex_lock(&freelist_lock));
VSTAILQ_CONCAT(dst, &freetxhead);
nfree = global_nfree;
global_nfree = 0;
AZ(pthread_mutex_unlock(&freelist_lock));
return nfree;
}
/*
* return to global freelist
* returned must be locked by caller, if required
*/
void
DATA_Return_Freelist(struct txhead_s *returned, unsigned nreturned)
{
AZ(pthread_mutex_lock(&freelist_lock));
VSTAILQ_CONCAT(&freetxhead, returned);
global_nfree += nreturned;
AZ(pthread_mutex_unlock(&freelist_lock));
}
#define DUMP_HDRS(vsb, ll, hdr) do { \
if (ll->hdr) \
for (j = 0; j < ll->hdr->nrec; j++) \
if (ll->hdr->record[j].len) { \
VSB_putc(vsb, '['); \
VSB_bcat(vsb, ll->hdr->record[j].data, \
ll->hdr->record[j].len); \
VSB_cat(vsb, "] "); \
} \
} while (0)
void
DATA_Dump(void)
{
#if 0
struct vsb *data;
logline_t *ll;
data = VSB_new_auto();
for (int i = 0; i < config.max_data; i++) {
int j;
if (logline == NULL || logline[i].magic != LOGLINE_MAGIC)
continue;
if (logline[i].state == DATA_EMPTY)
continue;
ll = &logline[i];
VSB_clear(data);
VSB_printf(data, "Data entry %d: state=%s dir=%c tags={",
i, statename[ll->state],
C(ll->spec) ? 'c' : B(ll->spec) ? 'b' : '-');
for (j = 0; j < ntags; j++)
if (ll->tag[j].len) {
VSB_cat(data, VSL_tags[idx2tag[j]]);
VSB_cat(data, "=[");
VSB_bcat(data, ll->tag[j].data, ll->tag[j].len);
VSB_cat(data, "] ");
}
VSB_cat(data, "} rx_headers={");
DUMP_HDRS(data, ll, rx_headers);
VSB_cat(data, "} tx_headers={");
DUMP_HDRS(data, ll, tx_headers);
VSB_cat(data, "} vcl_log={");
DUMP_HDRS(data, ll, vcl_log);
VSB_putc(data, '}');
VSB_finish(data);
LOG_Log(LOG_INFO, "%s", VSB_data(data));
}
#endif
}
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "varnishevent.h"
#include "vas.h"
#include "miniobj.h"
#include "base64.h"
#define TAG(ll,t) ((ll)->tag[tag2idx[(t)]])
typedef void formatter_f(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len);
typedef struct arg_t {
char *name;
enum VSL_tag_e tag;
} arg_t;
typedef struct compiled_fmt_t {
unsigned n;
char **str;
formatter_f **formatter;
arg_t *args;
char tags[MAX_VSL_TAG];
} compiled_fmt_t;
static compiled_fmt_t cformat, bformat, zformat;
static char i_arg[BUFSIZ] = "";
static int read_rx_hdr = 0, read_tx_hdr = 0, read_vcl_log = 0,
read_vcl_call = 0, ntags = 0;
static char *scratch;
static char hit[4];
static char miss[5];
static char pass[5];
static char dash[2];
static inline record_t *
get_hdr(const char *hdr, hdr_t *tbl)
{
int l = strlen(hdr);
for (int i = 0; i < tbl->nrec; i++)
if (strncasecmp(hdr, tbl->record[i].data, l) == 0
&& tbl->record[i].data[l] == ':'
&& tbl->record[i].data[l+1] == ' ')
return &tbl->record[i];
return NULL;
}
#define GET_HDR(ll, dir, hdr) get_hdr((hdr), (ll)-> dir## _headers)
static inline char *
get_fld(record_t *rec, int n)
{
char *ret = NULL, *s;
int i = 0;
AN(scratch);
strncpy(scratch, rec->data, rec->len);
s = scratch;
do {
ret = strtok(s, " \t");
s = NULL;
} while (++i < n && ret != NULL);
return ret;
}
static inline int
get_tm(tx_t *ll, struct tm * t) {
char *ts = NULL;
record_t *date_rec = NULL;
time_t tt = 0;
if (C(ll->spec)) {
if (TAG(ll,SLT_ReqEnd).len
&& (ts = get_fld(&TAG(ll,SLT_ReqEnd), 2)) != NULL)
tt = (time_t) atol(ts);
else
date_rec = GET_HDR(ll, rx, "Date");
}
else if (B(ll->spec)) {
if ((date_rec = GET_HDR(ll, rx, "Date")) == NULL)
tt = (time_t) ll->t;
}
else
tt = (time_t) ll->t;
if (date_rec)
tt = TIM_parse(&date_rec->data[strlen("Date: ")]);
if (tt != 0) {
localtime_r(&tt, t);
return 1;
}
else
return 0;
}
#define RETURN_REC(rec, s, len) do { \
(*s) = (rec).data; \
(*(len)) = (rec).len; \
} while (0)
#define RETURN_HDR(rec, hdr, s, len) do { \
(*s) = &((rec)->data[strlen(hdr)+2]); \
(*(len)) = (rec)->len - (strlen(hdr)+2); \
} while (0)
#define RETURN_FLD(rec, fld, s, len) do { \
(*s) = get_fld(&(rec), fld); \
(*(len)) = strlen(*s); \
} while (0)
#define FORMAT(dir, ltr, slt) \
static void \
format_##ltr##_##dir(logline_t *ll, char *name, enum VSL_tag_e tag, \
char **s, size_t *len) \
{ \
(void) name; \
(void) tag; \
if (TAG(ll,SLT_##slt).len) \
RETURN_REC(TAG(ll,SLT_##slt), s, len); \
}
#define FORMAT_b(dir, hx) \
static void \
format_b_##dir(logline_t *ll, char *name, enum VSL_tag_e tag, \
char **s, size_t *len) \
{ \
(void) name; \
(void) tag; \
record_t *rec; \
if (TAG(ll,SLT_Length).len) \
RETURN_REC(TAG(ll,SLT_Length), s, len); \
else if ((rec = GET_HDR(ll, hx, "content-length")) != NULL) \
RETURN_HDR(rec, "content-length", s, len); \
}
FORMAT_b(client, tx)
FORMAT_b(backend, rx)
FORMAT(client, H, RxProtocol)
FORMAT(backend, H, TxProtocol)
static void
format_h_client(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len)
{
(void) name;
(void) tag;
if (TAG(ll,SLT_ReqStart).len)
RETURN_FLD(TAG(ll,SLT_ReqStart), 0, s, len);
}
static void
format_h_backend(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len)
{
(void) name;
(void) tag;
if (TAG(ll,SLT_BackendOpen).len)
RETURN_FLD(TAG(ll,SLT_BackendOpen), 0, s, len);
else if (TAG(ll,SLT_BackendReuse).len)
RETURN_REC(TAG(ll,SLT_BackendReuse), s, len);
else if (TAG(ll,SLT_BackendClose).len)
RETURN_REC(TAG(ll,SLT_BackendClose), s, len);
}
FORMAT(client, m, RxRequest)
FORMAT(backend, m, TxRequest)
#define FORMAT_q(dir, xurl) \
static void \
format_q_##dir(logline_t *ll, char *name, enum VSL_tag_e tag, \
char **s, size_t *len) \
{ \
(void) name; \
(void) tag; \
char *qs = NULL; \
qs = memchr(TAG(ll,SLT_##xurl).data, '?', TAG(ll,SLT_##xurl).len); \
if (qs != NULL) { \
*s = qs + 1; \
*len = TAG(ll,SLT_##xurl).len - (qs - TAG(ll,SLT_##xurl).data - 1); \
} \
}
FORMAT_q(client, RxURL)
FORMAT_q(backend, TxURL)
#define FORMAT_r(dir, dx, hx) \
static void \
format_r_##dir(logline_t *ll, char *name, enum VSL_tag_e tag, \
char **s, size_t *len) \
{ \
(void) name; \
(void) tag; \
\
record_t *rec; \
\
rec = &TAG(ll, SLT_##dx##Request); \
if (rec->len) \
snprintf(scratch, rec->len+1, "%s", rec->data); \
else \
strcpy(scratch, "-"); \
strcat(scratch, " "); \
\
if ((rec = GET_HDR(ll, hx, "Host")) != NULL) { \
if (strncmp(rec->data, "http://", 7) != 0) \
strcat(scratch, "http://"); \
strncat(scratch, rec->data+6, rec->len-6); \
} \
else \
strcat(scratch, "http://localhost"); \
\
rec = &TAG(ll, SLT_##dx##URL); \
if (rec->len) \
strncat(scratch, rec->data, rec->len); \
else \
strcat(scratch, "-"); \
\
strcat(scratch, " "); \
rec = &TAG(ll, SLT_##dx##Protocol); \
if (rec->len) \
strncat(scratch, rec->data, rec->len); \
else \
strcat(scratch, "HTTP/1.0"); \
\
*s = scratch; \
*len = strlen(scratch); \
}
FORMAT_r(client, Rx, rx)
FORMAT_r(backend, Tx, tx)
FORMAT(client, s, TxStatus)
FORMAT(backend, s, RxStatus)
#define FORMAT_tim(ltr, fmt, extra) \
static void \
format_##ltr(logline_t *ll, char *name, enum VSL_tag_e tag, \
char **s, size_t *len) \
{ \
struct tm t; \
(void) tag; \
extra; \
if (get_tm(ll, &t)) { \
AN(scratch); \
size_t n = strftime(scratch, config.max_reclen, fmt, &t); \
if (n == 0) \
*scratch = '\0'; \
*s = scratch; \
*len = strlen(scratch); \
} \
}
FORMAT_tim(t, "[%d/%b/%Y:%T %z]", (void) name)
#define FORMAT_U(dir, dx) \
static void \
format_U_##dir(logline_t *ll, char *name, enum VSL_tag_e tag, \
char **s, size_t *len) \
{ \
char *q = NULL; \
unsigned ulen; \
(void) name; \
(void) tag; \
q = memchr(TAG(ll,SLT_##dx##URL).data, '?', TAG(ll,SLT_##dx##URL).len); \
if (q == NULL) \
ulen = TAG(ll,SLT_##dx##URL).len; \
else \
ulen = q - TAG(ll,SLT_##dx##URL).data; \
*s = TAG(ll,SLT_##dx##URL).data; \
*len = ulen; \
}
FORMAT_U(client, Rx)
FORMAT_U(backend, Tx)
#define FORMAT_u(dir, hx) \
static void \
format_u_##dir(logline_t *ll, char *name, enum VSL_tag_e tag, \
char **s, size_t *len) \
{ \
(void) name; \
(void) tag; \
record_t *rec; \
\
if ((rec = GET_HDR(ll, hx, "Authorization")) != NULL \
&& strncasecmp(rec->data + strlen("Authorization: "), "Basic", \
strlen("Basic")) == 0) { \
char *c, *auth = get_fld(rec, 3); \
VB64_init(); \
VB64_decode(scratch, config.max_reclen, auth); \
c = strchr(scratch, ':'); \
if (c != NULL) \
*c = '\0'; \
*s = scratch; \
*len = strlen(scratch); \
} \
else { \
strcpy(scratch, "-"); \
*s = scratch; \
*len = 1; \
} \
}
FORMAT_u(client, rx)
FORMAT_u(backend, tx)
#define FORMAT_Xio(dir, io, hx) \
static void \
format_X##io##_##dir(logline_t *ll, char *name, enum VSL_tag_e tag, \
char **s, size_t *len) \
{ \
(void) tag; \
record_t *rec = GET_HDR(ll, hx, name); \
if (rec) \
RETURN_HDR(rec, name, s, len); \
}
FORMAT_Xio(client, i, rx)
FORMAT_Xio(backend, i, tx)
FORMAT_Xio(client, o, tx)
FORMAT_Xio(backend, o, rx)
FORMAT_tim(Xt, name, )
static void
format_Xttfb_client(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len)
{
(void) name;
(void) tag;
if (TAG(ll,SLT_ReqEnd).len)
RETURN_FLD(TAG(ll,SLT_ReqEnd), 5, s, len);
}
#ifdef BESTATS
static void
format_Xttfb_backend(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len)
{
(void) name;
(void) tag;
if (TAG(ll,SLT_BackendReq).len && TAG(ll,SLT_Fetch_Hdr).len) {
double req_end, fetch_start;
errno = 0;
req_end = strtod(get_fld(&TAG(ll,SLT_BackendReq), 2), NULL);
AZ(errno);
fetch_start = strtod(get_fld(&TAG(ll,SLT_Fetch_Hdr), 2), NULL);
AZ(errno);
sprintf(scratch, "%.9f", fetch_start - req_end);
*s = scratch;
*len = strlen(scratch);
}
}
#endif
static void
format_VCL_disp(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len)
{
hdr_t *vcl_call = ll->vcl_call;
(void) tag;
*s = dash;
for (int i = 0; i < vcl_call->nrec; i++) {
record_t *rec = &vcl_call->record[i];
if (strncmp(rec->data, "hit", rec->len) == 0) {
*s = hit;
break;
}
else if (strncmp(rec->data, "miss", rec->len) == 0) {
*s = miss;
break;
}
else if (strncmp(rec->data, "pass", rec->len) == 0) {
if (*name == 'm')
*s = miss;
else
*s = pass;
break;
}
else if (strncmp(rec->data, "pipe", rec->len) == 0)
break;
}
*len = strlen(*s);
}
static void
format_VCL_Log(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len)
{
(void) tag;
record_t *rec = get_hdr(name, ll->vcl_log);
if (rec)
RETURN_HDR(rec, name, s, len);
}
static void
format_SLT(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len)
{
(void) name;
if (TAG(ll,tag).len)
RETURN_REC(TAG(ll,tag), s, len);
}
static void
format_incomplete(logline_t *ll, char *name, enum VSL_tag_e tag,
char **s, size_t *len)
{
(void) tag;
char *colon = strchr(name, ':');
AN(colon);
if (ll->incomplete)
strncpy(scratch, name, colon - name);
else
strcpy(scratch, colon + 1);
*s = scratch;
*len = strlen(scratch);
}
static int
add_fmt(compiled_fmt_t *fmt, struct vsb *os, unsigned n, formatter_f formatter,
const char *name, enum VSL_tag_e tag)
{
fmt->str[n] = (char *) malloc(VSB_len(os) + 1);
if (fmt->str[n] == NULL)
return errno;
if (name == NULL)
fmt->args[n].name = NULL;
else {
fmt->args[n].name = (char *) malloc(strlen(name) + 1);
if (fmt->args[n].name == NULL)
return errno;
strcpy(fmt->args[n].name, name);
}
VSB_finish(os);
strcpy(fmt->str[n], VSB_data(os));
VSB_clear(os);
fmt->formatter[n] = formatter;
fmt->args[n].tag = tag;
return 0;
}
#define ADD_FMT(spec, fmt, os, n, format_ltr, name, tag) do { \
if (C(spec)) \
add_fmt((fmt), (os), (n), format_ltr##_client, (name), (tag)); \
else if (B(spec)) \
add_fmt((fmt), (os), (n), format_ltr##_backend, (name), (tag)); \
} while(0)
#define ADD_TAG(tags, tag) (tags[SLT_##tag]) = 1
#define ADD_CB_TAG(spec, tags, ctag, btag) do { \
if (C(spec)) ADD_TAG(tags, ctag); else ADD_TAG(tags, btag); \
} while(0)
static int
compile_fmt(char *format, compiled_fmt_t *fmt, unsigned spec, char *err)
{
const char *p;
unsigned n = 1;
struct vsb *os;
for (p = format; *p != '\0'; p++)
if (*p == '%')
n++;
fmt->n = n;
fmt->str = (char **) calloc(n, sizeof(char *));
if (fmt->str == NULL) {
strcpy(err, strerror(errno));
return 0;
}
fmt->formatter = (formatter_f **) calloc(n, sizeof(formatter_f *));
if (fmt->formatter == NULL) {
strcpy(err, strerror(errno));
return 0;
}
fmt->args = (arg_t *) calloc(n, sizeof(arg_t));
if (fmt->args == NULL) {
strcpy(err, strerror(errno));
return 0;
}
memset(fmt->tags, 0, MAX_VSL_TAG);
/* starting tags */
if (C(spec)) {
ADD_TAG(fmt->tags, SessionOpen);
ADD_TAG(fmt->tags, ReqStart);
}
if (B(spec)) {
ADD_TAG(fmt->tags, BackendOpen);
ADD_TAG(fmt->tags, BackendXID);
}
/* always read the closing tags for clients and backends */
if (C(spec) || B(spec)) {
ADD_TAG(fmt->tags, ReqEnd);
ADD_TAG(fmt->tags, BackendReuse);
ADD_TAG(fmt->tags, BackendClose);
}
n = 0;
os = VSB_new_auto();
for (p = format; *p != '\0'; p++) {
/* allow the most essential escape sequences in format. */
if (*p == '\\') {
p++;
if (*p == 't') VSB_putc(os, '\t');
if (*p == 'n') VSB_putc(os, '\n');
continue;
}
if (*p != '%') {
VSB_putc(os, *p);
continue;
}
p++;
/* Only the SLT or time formatters permitted for the "zero" format
(neither client nor backend) */
if (Z(spec)
&& sscanf(p, "{tag:%s}x", scratch) != 1
&& *p != 't'
&& sscanf(p, "{%s}t", scratch) != 1) {
sprintf(err, "Unknown format starting at: %s", --p);
return 1;
}
switch (*p) {
case 'd':
VSB_putc(os, C(spec) ? 'c' : 'b');
break;
case 'b':
ADD_FMT(spec, fmt, os, n, format_b, NULL, 0);
ADD_TAG(fmt->tags, Length);
ADD_CB_TAG(spec, fmt->tags, TxHeader, RxHeader);
n++;
break;
case 'H':
ADD_FMT(spec, fmt, os, n, format_H, NULL, 0);
ADD_CB_TAG(spec, fmt->tags, RxProtocol, TxProtocol);
n++;
break;
case 'h':
ADD_FMT(spec, fmt, os, n, format_h, NULL, 0);
if (C(spec))
ADD_TAG(fmt->tags, ReqStart);
else {
ADD_TAG(fmt->tags, BackendOpen);
ADD_TAG(fmt->tags, BackendReuse);
ADD_TAG(fmt->tags, BackendClose);
}
n++;
break;
case 'l':
VSB_putc(os, '-');
break;
case 'm':
ADD_FMT(spec, fmt, os, n, format_m, NULL, 0);
ADD_CB_TAG(spec, fmt->tags, RxRequest, TxRequest);
n++;
break;
case 'q':
ADD_FMT(spec, fmt, os, n, format_q, NULL, 0);
ADD_CB_TAG(spec, fmt->tags, RxURL, TxURL);
n++;
break;
case 'r':
ADD_FMT(spec, fmt, os, n, format_r, NULL, 0);
ADD_CB_TAG(spec, fmt->tags, RxRequest, TxRequest);
ADD_CB_TAG(spec, fmt->tags, RxHeader, TxHeader);
ADD_CB_TAG(spec, fmt->tags, RxURL, TxURL);
ADD_CB_TAG(spec, fmt->tags, RxProtocol, TxProtocol);
n++;
break;
case 's':
ADD_FMT(spec, fmt, os, n, format_s, NULL, 0);
ADD_CB_TAG(spec, fmt->tags, TxStatus, RxStatus);
n++;
break;
case 't':
add_fmt(fmt, os, n, format_t, NULL, 0);
if (C(spec)) {
ADD_TAG(fmt->tags, ReqEnd);
ADD_TAG(fmt->tags, TxHeader);
}
else if (B(spec))
ADD_TAG(fmt->tags, RxHeader);
n++;
break;
case 'U':
ADD_FMT(spec, fmt, os, n, format_U, NULL, 0);
ADD_CB_TAG(spec, fmt->tags, RxURL, TxURL);
n++;
break;
case 'u':
ADD_FMT(spec, fmt, os, n, format_u, NULL, 0);
ADD_CB_TAG(spec, fmt->tags, RxHeader, TxHeader);
n++;
break;
case '{': {
const char *tmp;
char fname[100], type;
tmp = p;
type = 0;
while (*tmp != '\0' && *tmp != '}')
tmp++;
if (*tmp == '}') {
tmp++;
type = *tmp;
memcpy(fname, p+1, tmp-p-2);
fname[tmp-p-2] = 0;
}
switch (type) {
case 'i':
ADD_FMT(spec, fmt, os, n, format_Xi, fname, 0);
ADD_CB_TAG(spec, fmt->tags, RxHeader, TxHeader);
n++;
p = tmp;
break;
case 'o':
ADD_FMT(spec, fmt, os, n, format_Xo, fname, 0);
ADD_CB_TAG(spec, fmt->tags, TxHeader, RxHeader);
n++;
p = tmp;
break;
case 't':
add_fmt(fmt, os, n, format_Xt, fname, 0);
if (C(spec)) {
ADD_TAG(fmt->tags, ReqEnd);
ADD_TAG(fmt->tags, RxHeader);
}
else if (B(spec)) {
ADD_TAG(fmt->tags, TxHeader);
}
n++;
p = tmp;
break;
case 'x':
if (strcmp(fname, "Varnish:time_firstbyte") == 0) {
if (C(spec))
ADD_TAG(fmt->tags, ReqEnd);
#ifdef BESTATS
else {
ADD_TAG(fmt->tags, BackendReq);
ADD_TAG(fmt->tags, Fetch_Hdr);
}
ADD_FMT(spec, fmt, os, n, format_Xttfb, NULL, 0);
#else
else {
sprintf(err,
"Varnish:time_firstbyte only permitted "
"for client formats");
return 1;
}
add_fmt(fmt, os, n, format_Xttfb_client, NULL, 0);
#endif
}
else if (strcmp(fname, "Varnish:hitmiss") == 0) {
if (C(spec)) {
add_fmt(fmt, os, n, format_VCL_disp, "m", 0);
ADD_TAG(fmt->tags, VCL_call);
}
else {
sprintf(err,
"Varnish:hitmiss only permitted for client formats");
return 1;
}
}
else if (strcmp(fname, "Varnish:handling") == 0) {
if (C(spec)) {
add_fmt(fmt, os, n, format_VCL_disp, "n", 0);
ADD_TAG(fmt->tags, VCL_call);
}
else {
sprintf(err,
"Varnish:handling only permitted for client formats");
return 1;
}
}
else if (strncmp(fname, "VCL_Log:", 8) == 0) {
// support pulling entries logged with std.log() into
// output.
// Format: %{VCL_Log:keyname}x
// Logging: std.log("keyname:value")
add_fmt(fmt, os, n, format_VCL_Log, fname+8, 0);
ADD_TAG(fmt->tags, VCL_Log);
}
else if (strncmp(fname, "tag:", 4) == 0) {
int t = 0;
/* retrieve the tag contents from the log */
if ((t = VSL_Name2Tag(fname+4, strlen(fname+4))) < 0) {
sprintf(err, "Unknown or non-unique tag %s", fname+4);
return 1;
}
add_fmt(fmt, os, n, format_SLT, NULL, t);
fmt->tags[t] = 1;
}
else if (strncmp(fname, "incomplete:", 11) == 0) {
if (strchr(fname+11, ':') == NULL) {
sprintf(err, "':' not found in incomplete formatter %s",
fname+11);
return 1;
}
add_fmt(fmt, os, n, format_incomplete, fname+11, 0);
}
n++;
p = tmp;
break;
default:
sprintf(err, "Unknown format starting at: %s", --p);
return 1;
}
}
break;
/* Fall through if we haven't handled something */
/* FALLTHROUGH*/
default:
sprintf(err, "Unknown format starting at: %s", --p);
return 1;
}
}
/* Add any remaining string after the last formatter,
* and the terminating newline
*/
VSB_putc(os, '\n');
add_fmt(fmt, os, n, NULL, NULL, 0);
VSB_delete(os);
return 0;
}
int
FMT_Init(char *err)
{
scratch = (char *) malloc(config.max_reclen);
if (scratch == NULL)
return errno;
if (!EMPTY(config.cformat))
if (compile_fmt(config.cformat, &cformat, VSL_S_CLIENT, err) != 0)
return EINVAL;
if (!EMPTY(config.bformat))
if (compile_fmt(config.bformat, &bformat, VSL_S_BACKEND, err) != 0)
return EINVAL;
if (!EMPTY(config.zformat))
if (compile_fmt(config.zformat, &zformat, 0, err) != 0)
return EINVAL;
strcpy(hit, "hit");
strcpy(miss, "miss");
strcpy(pass, "pass");
strcpy(dash, "-");
for (int i = 0; i < MAX_VSL_TAG; i++) {
char tag = cformat.tags[i] | bformat.tags[i] | zformat.tags[i];
if (tag) {
strcat(i_arg, VSL_tags[i]);
strcat(i_arg, ",");
switch(i) {
case SLT_RxHeader:
read_rx_hdr = 1;
break;
case SLT_TxHeader:
read_tx_hdr = 1;
break;
case SLT_VCL_Log:
read_vcl_log = 1;
break;
case SLT_VCL_call:
read_vcl_call = 1;
break;
default:
idx2tag[ntags] = i;
tag2idx[i] = ntags++;
}
}
else
tag2idx[i] = idx2tag[i] = -1;
}
return 0;
}
char *
FMT_Get_i_Arg(void)
{
return i_arg;
}
int
FMT_Get_nTags(void)
{
return ntags;
}
int
FMT_Read_Hdr(enum VSL_tag_e tag)
{
switch(tag) {
case SLT_RxHeader:
return read_rx_hdr;
case SLT_TxHeader:
return read_tx_hdr;
case SLT_VCL_Log:
return read_vcl_log;
case SLT_VCL_call:
return read_vcl_call;
default:
/* Not allowed */
AN(0);
}
/* Unreachable */
return -1;
}
void
FMT_Format(logline_t *ll, struct vsb *os)
{
compiled_fmt_t fmt;
CHECK_OBJ_NOTNULL(ll, LOGLINE_MAGIC);
assert(ll->state == DATA_DONE);
if (C(ll->spec))
fmt = cformat;
else if (B(ll->spec))
fmt = bformat;
else
fmt = zformat;
for (int i = 0; i < fmt.n; i++) {
char *s = NULL;
size_t len = 0;
if (fmt.str[i] != NULL)
VSB_cat(os, fmt.str[i]);
if (fmt.formatter[i] != NULL) {
(fmt.formatter[i])(ll, fmt.args[i].name, fmt.args[i].tag, &s, &len);
if (s != NULL && len > 0)
VSB_bcat(os, s, len);
}
}
}
static void
free_format(compiled_fmt_t *fmt)
{
for (int i = 0; i < fmt->n; i++) {
free(fmt->str[i]);
if (fmt->args[i].name != NULL)
free(fmt->args[i].name);
}
free(fmt->str);
free(fmt->formatter);
free(fmt->args);
}
void
FMT_Shutdown(void)
{
free(scratch);
if (!EMPTY(config.cformat))
free_format(&cformat);
if (!EMPTY(config.bformat))
free_format(&bformat);
if (!EMPTY(config.zformat))
free_format(&zformat);
}
/*-
* Copyright (c) 2012 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2012 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Authors: Geoffrey Simmons <geoffrey.simmons@uplex.de>
* Nils Goroll <nils.goroll@uplex.de>
*
* Portions adopted from varnishlog.c from the Varnish project
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2011 Varnish Software AS
*
* 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.
*
*/
#include "config.h"
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include <ctype.h>
#ifndef HAVE_EXECINFO_H
#include "compat/execinfo.h"
#else
#include <execinfo.h>
#endif
#include "vas.h"
#include "vsb.h"
#include "varnishevent.h"
/* XXX: configurable? */
#define MAX_STACK_DEPTH 100
/*--------------------------------------------------------------------*/
/*
* This hack is almost verbatim from varnishd.c -- attempt to run nm(1) on
* ourselves at startup to get a mapping from lib pointers to symbolic
* function names for stack traces.
*
* +1 to phk's rant in varnishd.c about the lack of a standard for this.
*/
struct symbols {
uintptr_t a;
char *n;
VTAILQ_ENTRY(symbols) list;
};
static VTAILQ_HEAD(,symbols) symbols = VTAILQ_HEAD_INITIALIZER(symbols);
static int
symbol_lookup(struct vsb *vsb, void *ptr)
{
struct symbols *s, *s0;
uintptr_t pp;
pp = (uintptr_t)ptr;
s0 = NULL;
VTAILQ_FOREACH(s, &symbols, list) {
if (s->a > pp)
continue;
if (s0 == NULL || s->a > s0->a)
s0 = s;
}
if (s0 == NULL)
return (-1);
VSB_printf(vsb, "%p: %s+%jx", ptr, s0->n, (uintmax_t)pp - s0->a);
return (0);
}
static void
symbol_hack(const char *a0)
{
char buf[BUFSIZ], *p, *e;
FILE *fi;
uintptr_t a;
struct symbols *s;
sprintf(buf, "nm -an %s 2>/dev/null", a0);
fi = popen(buf, "r");
if (fi == NULL)
return;
while (fgets(buf, sizeof buf, fi)) {
if (buf[0] == ' ')
continue;
p = NULL;
a = strtoul(buf, &p, 16);
if (p == NULL)
continue;
if (a == 0)
continue;
if (*p++ != ' ')
continue;
p++;
if (*p++ != ' ')
continue;
if (*p <= ' ')
continue;
e = strchr(p, '\0');
AN(e);
while (e > p && isspace(e[-1]))
e--;
*e = '\0';
s = malloc(sizeof *s + strlen(p) + 1);
AN(s);
s->a = a;
s->n = (void*)(s + 1);
strcpy(s->n, p);
VTAILQ_INSERT_TAIL(&symbols, s, list);
}
(void)pclose(fi);
}
static void
stacktrace(void)
{
void *buf[MAX_STACK_DEPTH];
int depth, i;
struct vsb *sb = VSB_new_auto();
depth = backtrace (buf, MAX_STACK_DEPTH);
if (depth == 0) {
LOG_Log0(LOG_ERR, "Stacktrace empty");
return;
}
for (i = 0; i < depth; i++) {
VSB_clear(sb);
if (symbol_lookup(sb, buf[i]) < 0) {
char **strings;
strings = backtrace_symbols(&buf[i], 1);
if (strings != NULL && strings[0] != NULL)
VSB_printf(sb, "%p: %s", buf[i], strings[0]);
else
VSB_printf(sb, "%p: (?)", buf[i]);
}
VSB_finish(sb);
LOG_Log(LOG_ERR, "%s", VSB_data(sb));
}
}
void
HNDL_Init(const char *a0)
{
symbol_hack(a0);
}
void
HNDL_Abort(int sig)
{
AZ(sigaction(SIGABRT, &default_action, NULL));
LOG_Log(LOG_ALERT, "Received signal %d (%s), stacktrace follows", sig,
strsignal(sig));
stacktrace();
CONF_Dump();
DATA_Dump();
MON_Output();
LOG_Log0(LOG_ALERT, "Aborting");
abort();
}
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <syslog.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include "varnishevent.h"
static const char *level2name[LOG_DEBUG+1];
static void
syslog_setlevel(int level)
{
setlogmask(LOG_UPTO(level));
}
static void
stdio_initnames(void)
{
level2name[LOG_EMERG] = "EMERG";
level2name[LOG_ALERT] = "ALERT";
level2name[LOG_CRIT] = "CRIT";
level2name[LOG_ERR] = "ERR";
level2name[LOG_WARNING] = "WARNING";
level2name[LOG_NOTICE] = "NOTICE";
level2name[LOG_INFO] = "INFO";
level2name[LOG_DEBUG] = "DEBUG";
}
static void
stdio_log(int level, const char *msg, ...)
{
va_list ap;
if (level > logconf.level)
return;
fprintf(logconf.out, "%s: ", level2name[level]);
va_start(ap, msg);
(void) vfprintf(logconf.out, msg, ap);
va_end(ap);
fprintf(logconf.out, "\n");
fflush(logconf.out);
}
static void
stdio_setlevel(int level)
{
logconf.level = level;
}
static void
stdio_close(void)
{
fclose(logconf.out);
}
int LOG_Open(const char *progname)
{
if (EMPTY(config.log_file)) {
/* syslog */
logconf.log = syslog;
logconf.setlevel = syslog_setlevel;
logconf.close = closelog;
openlog(progname, LOG_PID | LOG_CONS | LOG_NDELAY | LOG_NOWAIT,
config.syslog_facility);
setlogmask(LOG_UPTO(LOG_INFO));
atexit(closelog);
return(0);
}
if (strcmp(config.log_file, "-") == 0)
logconf.out = stdout;
else {
logconf.out = fopen(config.log_file, "a");
if (logconf.out == NULL) {
perror(config.log_file);
return(-1);
}
}
logconf.level = LOG_INFO;
logconf.log = stdio_log;
logconf.setlevel = stdio_setlevel;
logconf.close = stdio_close;
stdio_initnames();
return(0);
}
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <syslog.h>
#include <pthread.h>
#include <errno.h>
#include "varnishevent.h"
#include "vas.h"
static int run = 0;
static pthread_t monitor;
static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
static void
log_output(void)
{
LOG_Log(LOG_INFO, "Data table: len=%u open=%u done=%u load=%.2f occ_hi=%u "
"global_free=%u", config.max_data, data_open, data_done,
100.0 * (data_open + data_done) / config.max_data,
data_occ_hi, global_nfree);
RDR_Stats();
WRT_Stats();
SPSCQ_Stats();
}
static void
monitor_cleanup(void *arg)
{
(void) arg;
log_output();
LOG_Log0(LOG_INFO, "Monitoring thread exiting");
}
static void *
monitor_main(void *arg)
{
LOG_Log(LOG_INFO, "Monitor thread running every %u secs",
config.monitor_interval);
run = 1;
pthread_cleanup_push(monitor_cleanup, arg);
while (run) {
#if 0
TIM_sleep(config.monitor_interval);
#endif
log_output();
}
pthread_cleanup_pop(0);
LOG_Log0(LOG_INFO, "Monitoring thread exiting");
pthread_exit((void *) NULL);
}
void
MON_Output(void)
{
log_output();
}
void
MON_Shutdown(void)
{
if (run) {
run = 0;
AZ(pthread_cancel(monitor));
AZ(pthread_join(monitor, NULL));
}
AZ(pthread_mutex_destroy(&stats_lock));
}
void
MON_Start(void)
{
AZ(pthread_create(&monitor, NULL, monitor_main, NULL));
}
void
MON_StatsUpdate(stats_update_t update)
{
AZ(pthread_mutex_lock(&stats_lock));
switch(update) {
case STATS_WRITTEN:
data_done--;
break;
case STATS_DONE:
data_done++;
break;
default:
/* Unreachable */
AN(NULL);
}
AZ(pthread_mutex_unlock(&stats_lock));
}
/*-
* Copyright (c) 2012 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2012 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
SIGDISP(SIGTERM, terminate_action);
SIGDISP(SIGINT, terminate_action);
SIGDISP(SIGUSR1, dump_action);
SIGDISP(SIGUSR2, ignore_action);
SIGDISP(SIGABRT, stacktrace_action);
SIGDISP(SIGSEGV, stacktrace_action);
SIGDISP(SIGBUS, stacktrace_action);
SIGDISP(SIGPIPE, reopen_action);
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <syslog.h>
#include <pthread.h>
#include <errno.h>
#include "varnishevent.h"
#include "vqueue.h"
#include "vas.h"
static volatile unsigned long enqs = 0, deqs = 0, occ_hi = 0;
VSTAILQ_HEAD(spscq_s, tx_t);
struct spscq_s spscq_head = VSTAILQ_HEAD_INITIALIZER(spscq_head);
struct spscq_s deq_head = VSTAILQ_HEAD_INITIALIZER(deq_head);
static pthread_mutex_t spscq_lock = PTHREAD_MUTEX_INITIALIZER;
void
SPSCQ_Enq(tx_t *ptr)
{
AZ(pthread_mutex_lock(&spscq_lock));
assert(enqs - deqs < config.max_data);
enqs++;
if (enqs - deqs > occ_hi)
occ_hi = enqs - deqs;
VSTAILQ_INSERT_TAIL(&spscq_head, ptr, spscq);
AZ(pthread_mutex_unlock(&spscq_lock));
}
tx_t
*SPSCQ_Deq(void)
{
void *ptr;
if (VSTAILQ_EMPTY(&deq_head)) {
AZ(pthread_mutex_lock(&spscq_lock));
VSTAILQ_CONCAT(&deq_head, &spscq_head);
AZ(pthread_mutex_unlock(&spscq_lock));
}
if (VSTAILQ_EMPTY(&deq_head))
return NULL;
ptr = VSTAILQ_FIRST(&deq_head);
VSTAILQ_REMOVE_HEAD(&deq_head, spscq);
deqs++;
return ptr;
}
void
SPSCQ_Stats(void)
{
unsigned len = enqs - deqs;
LOG_Log(LOG_INFO, "Queue: max=%u len=%u load=%.2f occ_hi=%u",
config.max_data, len, 100.0 * len / config.max_data, occ_hi);
}
void
SPSCQ_Shutdown(void)
{
AZ(pthread_mutex_destroy(&spscq_lock));
}
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <time.h>
#include "strfTIM.h"
#include "vsb.h"
#include "libvarnish.h"
size_t
strfTIM(char *s, size_t max, const char *fmt, struct tm *tm, long nsec)
{
struct vsb *vsb = VSB_new(NULL, NULL, max, VSB_FIXEDLEN);
const char *p;
size_t n;
for (p = fmt; *p; p++) {
if (*p != '%') {
VSB_putc(vsb, *p);
continue;
}
p++;
if (*p == '%') {
VSB_cat(vsb, "%%");
continue;
}
if (*p != 'N') {
VSB_putc(vsb, '%');
VSB_putc(vsb, *p);
continue;
}
VSB_printf(vsb, "%09ld", nsec);
}
VSB_finish(vsb);
if (VSB_error(vsb)) {
VSB_delete(vsb);
return 0;
}
n = strftime(s, max, VSB_data(vsb), tm);
VSB_delete(vsb);
return n;
}
#define strfTIM_tz(tz) \
size_t \
strfTIM##tz(char *s, size_t max, const char *fmt, double t) \
{ \
struct timespec tim = TIM_timespec(t); \
struct tm tm; \
\
AN(tz##time_r((time_t *) &tim.tv_sec, &tm)); \
return(strfTIM(s, max, fmt, &tm, tim.tv_nsec)); \
}
strfTIM_tz(local)
strfTIM_tz(gm)
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <time.h>
#include <stddef.h>
size_t strfTIM(char *s, size_t max, const char *fmt, struct tm *tm, long nsec);
size_t strfTIMlocal(char *s, size_t max, const char *fmt, double t);
size_t strfTIMgm(char *s, size_t max, const char *fmt, double t);
/*-
* Copyright (c) 2015 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2015 Otto Gmbh & Co KG
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Portions adapted from varnishncsa.c from the Varnish project
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2011 Varnish Software AS
* Author: Anders Berg <andersb@vgnett.no>
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
* Author: Tollef Fog Heen <tfheen@varnish-software.com>
*
* 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.
*
* Obtain log data from the shared memory log and output single-line
* events to an output stream. By default just like varnishncsa, but:
*
* - output lines may correspond to both client and backend transactions
* - also events on the pseudo fd 0, such as backend health checks, may be
* logged
* - output formats are defined for client, backend and "zero" events
* - some additional formatting tags are available
* - the internal architecture is designed to ensure that the VSL-reading
* process keeps pace with varnishd writing to VSL under heavy loads
*/
#include "config.h"
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
#include <syslog.h>
#include <stdint.h>
#include <errno.h>
#include "vsb.h"
#if 0
#include "vpf.h"
#endif
#include "vqueue.h"
#include "vapi/vsl.h"
#include "vre.h"
#include "miniobj.h"
#include "vas.h"
#include "vcs.h"
#include "varnishevent.h"
#define DEFAULT_CONFIG "/etc/varnishevent.conf"
#define DISPATCH_CONTINUE 0
#define DISPATCH_TERMINATE 7
typedef enum {
FD_EMPTY = 0,
FD_OPEN
} fd_state_e;
typedef struct fd_t {
unsigned magic;
#define FD_MAGIC 0xa06b2960
logline_t *ll;
fd_state_e state;
double t;
VTAILQ_ENTRY(fd_t) insert_list;
} fd_t;
static fd_t *fd_tbl;
VTAILQ_HEAD(insert_head_s, fd_t);
static struct insert_head_s insert_head = VTAILQ_HEAD_INITIALIZER(insert_head);
static unsigned open = 0, occ_hi = 0, len_hi = 0;
static unsigned long seen = 0, submitted = 0, not_logged = 0,
waits = 0, fd_overflows = 0, len_overflows = 0,
hdr_overflows = 0, expired = 0, spec_mismatches = 0, wrong_tags = 0;
/* Hack, because we cannot have #ifdef in the macro definition SIGDISP */
#define _UNDEFINED(SIG) ((#SIG)[0] == 0)
#define UNDEFINED(SIG) _UNDEFINED(SIG)
#define SIGDISP(SIG, action) \
do { if (UNDEFINED(SIG)) break; \
if (sigaction((SIG), (&action), NULL) != 0) \
LOG_Log(LOG_ALERT, \
"Cannot install handler for " #SIG ": %s", \
strerror(errno)); \
} while(0)
static struct sigaction dump_action, terminate_action, reopen_action,
stacktrace_action, ignore_action;
static volatile sig_atomic_t reopen = 0, term = 0;
struct VSL_data *vsl;
static int m_flag = 0;
#if 0
static int cb_flag = 0;
static int z_flag = 0;
#endif
/* Local freelist */
static struct freehead_s reader_freelist =
VSTAILQ_HEAD_INITIALIZER(reader_freelist);
static unsigned rdr_free = 0;
static struct txhead_s rdr_tx_freelist =
VSTAILQ_HEAD_INITIALIZER(rdr_tx_freelist);
static unsigned rdr_tx_free = 0;
static int waiting = 0;
static char cli_config_filename[BUFSIZ] = "";
int
RDR_Waiting(void)
{
return waiting;
}
void
RDR_Stats(void)
{
LOG_Log(LOG_INFO, "Reader (%s): fd_max=%u seen=%lu open=%u load=%.2f "
"submitted=%lu not_logged=%lu occ_hi=%u waits=%lu expired=%lu free=%u "
"len_hi=%u fd_overflows=%lu len_overflows=%lu hdr_overflows=%lu "
"spec_mismatches=%lu wrong_tags=%lu",
waiting ? "waiting" : "running", config.max_fd, seen, open,
100.0 * open / config.max_fd, submitted, not_logged, occ_hi, waits,
expired, rdr_free, len_hi, fd_overflows, len_overflows, hdr_overflows,
spec_mismatches, wrong_tags);
}
static inline void
signal_spscq_ready(void)
{
if (WRT_Waiting()) {
AZ(pthread_mutex_lock(&spscq_ready_lock));
AZ(pthread_cond_signal(&spscq_ready_cond));
AZ(pthread_mutex_unlock(&spscq_ready_lock));
}
}
#if 0
static inline logline_t
*take(void)
{
struct logline_t *data;
while (VSTAILQ_EMPTY(&reader_freelist)) {
rdr_free = DATA_Take_Freelist(&reader_freelist);
if (VSTAILQ_EMPTY(&reader_freelist)) {
AZ(rdr_free);
signal_spscq_ready();
LOG_Log0(LOG_DEBUG, "Reader: waiting for free list");
waiting = 1;
AZ(pthread_mutex_lock(&data_ready_lock));
if (!WRT_Waiting()) {
waits++;
AZ(pthread_cond_wait(&data_ready_cond, &data_ready_lock));
}
waiting = 0;
AZ(pthread_mutex_unlock(&data_ready_lock));
rdr_free = DATA_Take_Freelist(&reader_freelist);
LOG_Log(LOG_DEBUG, "Reader: took %u from free list", rdr_free);
}
}
data = VSTAILQ_FIRST(&reader_freelist);
VSTAILQ_REMOVE_HEAD(&reader_freelist, freelist);
rdr_free--;
return (data);
}
#endif
static inline tx_t
*take_tx(void)
{
struct tx_t *tx;
while (VSTAILQ_EMPTY(&rdr_tx_freelist)) {
rdr_tx_free = DATA_Take_Freelist(&rdr_tx_freelist);
if (VSTAILQ_EMPTY(&rdr_tx_freelist)) {
AZ(rdr_tx_free);
signal_spscq_ready();
LOG_Log0(LOG_DEBUG, "Reader: waiting for free list");
waiting = 1;
AZ(pthread_mutex_lock(&data_ready_lock));
if (!WRT_Waiting()) {
waits++;
AZ(pthread_cond_wait(&data_ready_cond, &data_ready_lock));
}
waiting = 0;
AZ(pthread_mutex_unlock(&data_ready_lock));
rdr_tx_free = DATA_Take_Freelist(&rdr_tx_freelist);
LOG_Log(LOG_DEBUG, "Reader: took %u txen from free list",
rdr_tx_free);
}
}
tx = VSTAILQ_FIRST(&rdr_tx_freelist);
VSTAILQ_REMOVE_HEAD(&rdr_tx_freelist, freelist);
rdr_tx_free--;
return (tx);
}
static inline void
take_chunks(linehead_t *lines, unsigned nchunks)
{
(void) lines;
(void) nchunks;
}
static inline void
submit(tx_t *tx)
{
CHECK_OBJ_NOTNULL(tx, TX_MAGIC);
assert(tx->state == TX_DONE);
#if 0
assert(lp->state == DATA_DONE);
data_open--;
if ((m_flag && !VSL_Matched(vd, lp->bitmap))
|| (lp->spec && !(lp->spec & cb_flag))) {
not_logged++;
DATA_Clear_Logline(lp);
rdr_free++;
VSTAILQ_INSERT_TAIL(&reader_freelist, lp, freelist);
return;
}
SPSCQ_Enq((void *) lp);
#endif
signal_spscq_ready();
MON_StatsUpdate(STATS_DONE);
submitted++;
}
static inline void
fd_free(fd_t *entry)
{
CHECK_OBJ_NOTNULL(entry, FD_MAGIC);
VTAILQ_REMOVE(&insert_head, entry, insert_list);
entry->state = FD_EMPTY;
entry->ll = NULL;
open--;
}
static int
event(struct VSL_data *_vsl, struct VSL_transaction * const pt[], void *priv)
{
struct tx_t *tx = NULL;
struct VSL_transaction *t;
int status = DISPATCH_CONTINUE;
(void) priv;
if (term)
return DISPATCH_TERMINATE;
if (pt[0] == NULL)
return reopen;
/* XXX: assert length(pt) == 1? */
for (t = pt[0]; t != NULL; t = *++pt) {
tx = take_tx();
CHECK_OBJ_NOTNULL(tx, TX_MAGIC);
assert(tx->state == TX_EMPTY);
assert(!VSTAILQ_EMPTY(tx->lines));
switch(t->type) {
case VSL_t_req:
tx->spec = 'c';
break;
case VSL_t_bereq:
tx->spec = 'b';
break;
case VSL_t_raw:
tx->spec = '-';
break;
default:
WRONG("Unexpected transaction type");
}
LOG_Log(LOG_DEBUG, "Tx: [%u %c]", t->vxid, tx->spec);
logline_t *line = VSTAILQ_FIRST(tx->lines);
while (1) {
int len;
status = VSL_Next(t->c);
if (status <= 0)
break;
if (!VSL_Match(_vsl, t->c))
continue;
len = VSL_LEN(t->c->rec.ptr);
LOG_Log(LOG_DEBUG, "Line: [%u %s %.*s]", VSL_ID(t->c->rec.ptr),
VSL_tags[VSL_TAG(t->c->rec.ptr)], len,
VSL_CDATA(t->c->rec.ptr));
if (line == NULL) {
/* XXX: increment counter */
#if 0
line = VSTAILQ_LAST(tx->lines, logline_t, linelist);
take_lines(tx->lines);
line = VSTAILQ_NEXT(line, linelist);
#endif
}
CHECK_OBJ_NOTNULL(line, LOGLINE_MAGIC);
assert(line->state == DATA_EMPTY);
line->tag = VSL_TAG(t->c->rec.ptr);
line->len = len;
if (len != 0) {
/* Copy the payload into chunks */
AN(line->chunks);
assert(!VSTAILQ_EMPTY(line->chunks));
int nchunks = (len + config.chunk_size - 1) / config.chunk_size;
if (nchunks > 1)
/* XXX: increment counter */
take_chunks(tx->lines, nchunks);
int n = len;
chunk_t *chunk = VSTAILQ_FIRST(line->chunks);
const char *p = (const char *) VSL_CDATA(t->c->rec.ptr);
while (n > 0) {
CHECK_OBJ_NOTNULL(chunk, CHUNK_MAGIC);
int cp = n;
if (cp > config.chunk_size)
cp = config.chunk_size;
memcpy(chunk->data, p, cp);
p += cp;
n -= cp;
chunk = VSTAILQ_NEXT(chunk, chunklist);
}
}
line->state = DATA_DONE;
line = VSTAILQ_NEXT(line, linelist);
}
}
seen++;
data_done++;
if (data_done > data_occ_hi)
data_occ_hi = data_done;
submit(tx);
return reopen;
}
/*--------------------------------------------------------------------*/
static void
sigreopen(int sig)
{
LOG_Log(LOG_WARNING, "Received signal %d (%s), reopening output",
sig, strsignal(sig));
reopen = 1;
}
static void
dump(int sig)
{
(void) sig;
CONF_Dump();
DATA_Dump();
}
static void
terminate(int sig)
{
(void) sig;
term = 1;
}
static vas_f assert_fail __attribute__((__noreturn__));
static void
assert_fail(const char *func, const char *file, int line, const char *cond,
int err, enum vas_e err_e)
{
(void) err_e;
LOG_Log(LOG_ALERT, "Condition (%s) failed in %s(), %s line %d",
cond, func, file, line);
if (err)
LOG_Log(LOG_ALERT, "errno = %d (%s)", err, strerror(err));
abort();
}
/*--------------------------------------------------------------------*/
static void
read_default_config(void) {
if (access(DEFAULT_CONFIG, F_OK) == 0) {
if (access(DEFAULT_CONFIG, R_OK) != 0) {
perror(DEFAULT_CONFIG);
exit(EXIT_FAILURE);
}
printf("Reading config from %s\n", DEFAULT_CONFIG);
if (CONF_ReadFile(DEFAULT_CONFIG) != 0)
exit(EXIT_FAILURE);
}
}
static void
usage(void)
{
fprintf(stderr,
"usage: varnishevent [-aDVg] [-G configfile] [-P pidfile] "
"[-w outputfile]\n");
exit(1);
}
int
main(int argc, char *argv[])
{
int c, errnum, finite = 0, a_flag = 0, g_flag = 0, format_flag = 0;
#if 0
int D_flag = 0;
const char *P_arg = NULL;
#endif
const char *w_arg = NULL;
char scratch[BUFSIZ];
#if 0
struct vpf_fh *pfh = NULL;
#endif
vsl = VSL_New();
CONF_Init();
read_default_config();
while ((c = getopt(argc, argv, "aDP:Vw:fF:gG:")) != -1) {
switch (c) {
case 'a':
a_flag = 1;
break;
case 'f':
if (format_flag) {
fprintf(stderr, "-f and -F can not be combined\n");
exit(1);
}
strcpy(config.cformat, ALT_CFORMAT);
format_flag = 1;
break;
case 'F':
if (format_flag) {
fprintf(stderr, "-f and -F can not be combined\n");
exit(1);
}
format_flag = 1;
strcpy(config.cformat, optarg);
break;
#if 0
case 'D':
D_flag = 1;
break;
case 'P':
P_arg = optarg;
break;
#endif
case 'V':
VCS_Message("varnishevent");
exit(0);
case 'w':
w_arg = optarg;
break;
case 'g':
g_flag = 1;
break;
case 'G':
strcpy(cli_config_filename, optarg);
break;
case 'b':
case 'i':
case 'I':
case 'c':
fprintf(stderr, "-%c is not valid for varnishevent\n", c);
exit(1);
break;
case 'm':
m_flag = 1; /* Fall through */
default:
if (c == 'r') {
finite = 1;
strcpy(config.varnish_bindump, optarg);
}
if (VSL_Arg(vsl, c, optarg) > 0)
break;
usage();
}
}
if (! EMPTY(cli_config_filename)) {
printf("Reading config from %s\n", cli_config_filename);
if (CONF_ReadFile(cli_config_filename) != 0) {
fprintf(stderr, "Error reading config from %s\n",
cli_config_filename);
exit(EXIT_FAILURE);
}
}
/* XXX: set this up properly, possible for reading bin logs */
/* XXX: should do this after opening syslog, to log errors */
struct VSM_data *vd = VSM_New();
struct VSL_cursor *cursor = VSL_CursorVSM(vsl, vd, 0);
if (cursor == NULL) {
fprintf(stderr, "Cannot open log: %s\n", VSL_Error(vsl));
exit(1);
}
/* XXX: set up the query to filter the log contents narrowly for
the output format */
struct VSLQ *vslq;
vslq = VSLQ_New(vsl, &cursor, VSL_g_vxid, "");
if (vslq == NULL) {
fprintf(stderr, "Cannot init log query: %s\n", VSL_Error(vsl));
exit(1);
}
#if 0
if (P_arg && (pfh = VPF_Open(P_arg, 0644, NULL)) == NULL) {
perror(P_arg);
exit(1);
}
if (D_flag && varnish_daemon(0, 0) == -1) {
perror("daemon()");
if (pfh != NULL)
VPF_Remove(pfh);
exit(1);
}
#endif
terminate_action.sa_handler = terminate;
AZ(sigemptyset(&terminate_action.sa_mask));
terminate_action.sa_flags &= ~SA_RESTART;
dump_action.sa_handler = dump;
AZ(sigemptyset(&dump_action.sa_mask));
dump_action.sa_flags |= SA_RESTART;
reopen_action.sa_handler = sigreopen;
AZ(sigemptyset(&reopen_action.sa_mask));
reopen_action.sa_flags |= SA_RESTART;
#if 0
stacktrace_action.sa_handler = HNDL_Abort;
#endif
ignore_action.sa_handler = SIG_IGN;
default_action.sa_handler = SIG_DFL;
#if 0
HNDL_Init(argv[0]);
#endif
/* Install signal handlers */
#include "signals.h"
#if 0
if (pfh != NULL)
VPF_Write(pfh);
#endif
if (w_arg)
strcpy(config.output_file, w_arg);
if (!EMPTY(config.output_file))
SIGDISP(SIGHUP, reopen_action);
else
SIGDISP(SIGHUP, ignore_action);
if (a_flag)
config.append = 1;
if (LOG_Open(config.syslog_ident) != 0) {
exit(EXIT_FAILURE);
}
if (g_flag)
LOG_SetLevel(LOG_DEBUG);
LOG_Log(LOG_INFO, "initializing (%s)", VCS_version);
VAS_Fail = assert_fail;
#if 0
if (FMT_Init(scratch) != 0) {
LOG_Log(LOG_ALERT, "Error in output formats: %s", scratch);
exit(EXIT_FAILURE);
}
#endif
if (!EMPTY(config.varnish_bindump))
LOG_Log(LOG_INFO, "Reading from file: %s", config.varnish_bindump);
else {
strcpy(scratch, VSM_Name(vd));
if (EMPTY(scratch))
LOG_Log0(LOG_INFO, "Reading default varnish instance");
else
LOG_Log(LOG_INFO, "Reading varnish instance %s", scratch);
}
#if 0
strcpy(scratch, FMT_Get_i_Arg());
#endif
if (EMPTY(scratch)) {
LOG_Log0(LOG_ALERT, "Not configured to read any log data, exiting");
exit(EXIT_FAILURE);
}
assert(VSL_Arg(vsl, 'i', scratch) > 0);
LOG_Log(LOG_INFO, "Reading SHM tags: %s", scratch);
#if 0
if (!EMPTY(config.cformat))
cb_flag |= VSL_S_CLIENT;
if (!EMPTY(config.bformat))
cb_flag |= VSL_S_BACKEND;
if (!EMPTY(config.zformat))
z_flag = 1;
#endif
if ((errnum = DATA_Init()) != 0) {
LOG_Log(LOG_ALERT, "Cannot init data table: %s\n",
strerror(errnum));
exit(EXIT_FAILURE);
}
fd_tbl = (fd_t *) calloc(config.max_fd, sizeof(fd_t));
if (fd_tbl == NULL) {
LOG_Log(LOG_ALERT, "Cannot init fd table: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
for (int k = 0; k < config.max_fd; k++) {
fd_tbl[k].magic = FD_MAGIC;
fd_tbl[k].ll = NULL;
fd_tbl[k].state = FD_EMPTY;
}
AZ(pthread_cond_init(&data_ready_cond, NULL));
AZ(pthread_mutex_init(&data_ready_lock, NULL));
AZ(pthread_cond_init(&spscq_ready_cond, NULL));
AZ(pthread_mutex_init(&spscq_ready_lock, NULL));
if (config.monitor_interval > 0)
MON_Start();
else
LOG_Log0(LOG_INFO, "Monitoring thread not running");
if ((errnum = WRT_Init()) != 0) {
LOG_Log(LOG_ALERT, "Cannot init writer thread: %s\n", strerror(errnum));
exit(EXIT_FAILURE);
}
rdr_free = DATA_Take_Freelist(&rdr_tx_freelist);
assert(!VSTAILQ_EMPTY(&rdr_tx_freelist));
assert(rdr_free == config.max_data);
WRT_Start();
/* XXX: configure wrt_waits and sleep interval? */
int wrt_waits = 0;
while (!WRT_Running()) {
if (wrt_waits++ > 10) {
LOG_Log0(LOG_ALERT, "Writer thread not running, giving up");
exit(EXIT_FAILURE);
}
#if 0
TIM_sleep(1);
#endif
}
/* Main loop */
term = 0;
/* XXX: TERM not noticed until request received */
while (VSLQ_Dispatch(vslq, event, NULL) >= 0)
if (term || finite)
break;
else if (reopen) {
WRT_Reopen();
reopen = 0;
}
else
LOG_Log0(LOG_WARNING, "Log read interrupted, continuing");
if (term)
LOG_Log0(LOG_INFO, "Termination signal received");
else if (!finite)
LOG_Log0(LOG_WARNING, "Varnish log closed");
WRT_Halt();
SPSCQ_Shutdown();
MON_Shutdown();
#if 0
FMT_Shutdown();
#endif
AZ(pthread_cond_destroy(&data_ready_cond));
AZ(pthread_mutex_destroy(&data_ready_lock));
AZ(pthread_cond_destroy(&spscq_ready_cond));
AZ(pthread_mutex_destroy(&spscq_ready_lock));
LOG_Log0(LOG_INFO, "Exiting");
LOG_Close();
exit(EXIT_SUCCESS);
}
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved.
* Use only with permission
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <limits.h>
#include <signal.h>
#include <sys/time.h>
#include "vapi/vsl.h"
#include "vqueue.h"
#include "vsb.h"
#define C(spec) ((spec) & VSL_S_CLIENT)
#define B(spec) ((spec) & VSL_S_BACKEND)
#define Z(spec) ((spec) == 0)
/* Defaults from Varnish 3.0.3 */
#define DEFAULT_MAX_RECLEN 255 /* shm_reclen */
#define DEFAULT_MAX_HEADERS 64 /* http_max_hdr */
#define DEFAULT_MAX_FD 1024
#define DEFAULT_MAX_DATA 4096
#define DEFAULT_PID_FILE "/var/run/varnishevent.pid"
#define DEFAULT_HOUSEKEEP_INTERVAL 10
#define DEFAULT_TTL 120
#define MAX_VSL_TAG 256
#define DEFAULT_CFORMAT \
"%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\""
#define ALT_CFORMAT \
"%{X-Forwarded-For}i %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\""
struct sigaction default_action;
typedef enum {
DATA_EMPTY = 0,
DATA_DONE,
} data_state_e;
typedef enum {
TX_EMPTY = 0,
TX_DONE,
} tx_state_e;
typedef struct {
unsigned magic;
#define RECORD_MAGIC 0xdf4399b1
char *data;
unsigned len;
} record_t;
typedef struct {
record_t *record;
unsigned nrec;
} hdr_t;
typedef struct chunk_t {
unsigned magic;
#define CHUNK_MAGIC 0x676e0d19
char *data;
VSTAILQ_ENTRY(chunk_t) freelist;
VSTAILQ_ENTRY(chunk_t) chunklist;
} chunk_t;
VSTAILQ_HEAD(chunkhead_s, chunk_t);
chunk_t *chunks;
typedef struct logline_t {
unsigned magic;
#define LOGLINE_MAGIC 0xf427a374
enum VSL_tag_e tag;
data_state_e state;
struct chunkhead_s *chunks;
unsigned len;
VSTAILQ_ENTRY(logline_t) freelist;
VSTAILQ_ENTRY(logline_t) linelist;
} logline_t;
typedef VSTAILQ_HEAD(linehead_s, logline_t) linehead_t;
typedef struct tx_t {
unsigned magic;
#define TX_MAGIC 0xff463e42
tx_state_e state;
char spec; /* 'b'/'c'/'-' */
linehead_t *lines;
VSTAILQ_ENTRY(tx_t) freelist;
VSTAILQ_ENTRY(tx_t) spscq;
} tx_t;
tx_t *txn;
VSTAILQ_HEAD(txhead_s, tx_t);
unsigned data_open;
unsigned data_done;
unsigned data_occ_hi;
int tag2idx[MAX_VSL_TAG];
enum VSL_tag_e idx2tag[MAX_VSL_TAG];
VSTAILQ_HEAD(freehead_s, logline_t);
struct txhead_s freetxhead;
unsigned global_nfree;
/* Reader waits for this condition when the freelist is exhausted.
Writer signals the condition after returning space to the freelist. */
pthread_cond_t data_ready_cond;
pthread_mutex_t data_ready_lock;
/* Writer (consumer) waits for this condition when the SPSC queue is empty.
Reader (producer) signals the condition after enqueue. */
pthread_cond_t spscq_ready_cond;
pthread_mutex_t spscq_ready_lock;
struct config {
char pid_file[BUFSIZ];
/* VSL 'n' argument */
char varnish_name[BUFSIZ];
char log_file[BUFSIZ];
char output_file[PATH_MAX];
unsigned append;
struct timeval output_timeout;
/* VSL 'r' argument */
char varnish_bindump[BUFSIZ];
/* zformat is for fd 0 (neither 'c' nor 'b') */
/* XXX: better if these weren't limited to fixed buffer sizes, but the
* length of a configurable string is limited by the length of lines
* read by CONF_ReadFile(), currently BUFSIZ
*/
char cformat[BUFSIZ];
char bformat[BUFSIZ];
char zformat[BUFSIZ];
int syslog_facility;
char syslog_facility_name[BUFSIZ];
char syslog_ident[BUFSIZ];
unsigned monitor_interval;
/* varnishd param shm_reclen */
unsigned max_reclen;
/* varnishd param http_max_hdr */
unsigned max_headers;
unsigned max_vcl_log;
unsigned max_vcl_call;
unsigned max_fd;
unsigned chunk_size;
unsigned max_data;
unsigned housekeep_interval;
unsigned ttl;
size_t output_bufsiz;
char user_name[BUFSIZ];
uid_t uid;
gid_t gid;
} config;
/* varnishevent.c */
int RDR_Waiting(void);
void RDR_Stats(void);
/* config.c */
void CONF_Init(void);
int CONF_Add(const char *lval, const char *rval);
int CONF_ReadFile(const char *file);
void CONF_Dump(void);
/* log.c */
#define EMPTY(s) (s[0] == '\0')
typedef void log_log_t(int level, const char *msg, ...);
typedef void log_setlevel_t(int level);
typedef void log_close_t(void);
struct logconf {
log_log_t *log;
log_setlevel_t *setlevel;
log_close_t *close;
FILE *out;
int level;
} logconf;
int LOG_Open(const char *progname);
/* XXX: __VA_ARGS__ can't be empty ... */
#define LOG_Log0(level, msg) logconf.log(level, msg)
#define LOG_Log(level, msg, ...) logconf.log(level, msg, __VA_ARGS__)
#define LOG_SetLevel(level) logconf.setlevel(level)
#define LOG_Close() logconf.close()
/* data.c */
int DATA_Init(void);
void DATA_Clear_Logline(tx_t *tx);
unsigned DATA_Take_Freelist(struct txhead_s *dst);
void DATA_Return_Freelist(struct txhead_s *returned, unsigned nreturned);
void DATA_Dump(void);
/* writer.c */
int WRT_Init(void);
void WRT_Start(void);
void WRT_Stats(void);
int WRT_Running(void);
int WRT_Waiting(void);
void WRT_Reopen(void);
void WRT_Halt(void);
void WRT_Shutdown(void);
/* spscq.c */
void SPSCQ_Enq(tx_t *ptr);
tx_t *SPSCQ_Deq(void);
unsigned SPSCQ_Len(void);
void SPSCQ_Stats(void);
void SPSCQ_Shutdown(void);
/* monitor.c */
typedef enum {
/* "Ending" VSL tag seen */
STATS_DONE,
/* Log line written */
STATS_WRITTEN,
} stats_update_t;
void MON_Start(void);
void MON_Shutdown(void);
void MON_StatsUpdate(stats_update_t update);
void MON_Output(void);
#if 0
/* format.c */
int FMT_Init(char *err);
char *FMT_Get_i_Arg(void);
int FMT_Get_nTags(void);
int FMT_Read_Hdr(enum VSL_tag_e tag);
void FMT_Format(logline_t *ll, struct vsb *os);
void FMT_Shutdown(void);
/* handler.c */
void HNDL_Init(const char *a0);
void HNDL_Abort(int sig);
#endif
/*-
* Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
* Copyright (c) 2013 Otto Gmbh & Co KG
* All rights reserved
* Use only with permission
*
* Authors: Geoffrey Simmons <geoffrey.simmons@uplex.de>
* Nils Goroll <nils.goroll@uplex.de>
*
* 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.
*
*/
#include <pthread.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include "varnishevent.h"
#include "vas.h"
#include "miniobj.h"
#include "vsb.h"
typedef enum {
WRT_NOTSTARTED = 0,
WRT_INITIALIZING,
WRT_RUNNING,
WRT_WAITING,
WRT_SHUTTINGDOWN,
WRT_EXITED,
WRT_STATE_E_LIMIT
} wrt_state_e;
static const char* statename[WRT_STATE_E_LIMIT] = {
[WRT_NOTSTARTED] = "not started",
[WRT_INITIALIZING] = "initializing",
[WRT_RUNNING] = "running",
[WRT_WAITING] = "waiting",
[WRT_SHUTTINGDOWN] = "shutting down",
[WRT_EXITED] = "exited"
};
/* Single writer thread, consumer for the SPSC queue. */
pthread_t writer;
/* local freelist - return space in chunks */
static struct txhead_s wrt_freelist;
static unsigned wrt_nfree;
static struct vsb *os;
static FILE *fo;
int fd;
fd_set set;
struct timeval to;
struct timeval *timeout = NULL;
static char *obuf = NULL;
static pthread_mutex_t reopen_lock = PTHREAD_MUTEX_INITIALIZER;
/* stats */
static unsigned long deqs = 0;
static unsigned long bytes = 0;
static unsigned long waits = 0;
static unsigned long writes = 0;
static unsigned long errors = 0;
static unsigned long timeouts = 0;
typedef struct writer_data_s {
unsigned magic;
#define WRITER_DATA_MAGIC 0xd8eef137
unsigned status; /* exit status */
wrt_state_e state;
} writer_data_t;
static writer_data_t wrt_data;
static unsigned run, reopen = 0;
static int
set_fdset(void)
{
errno = 0;
fd = fileno(fo);
if (fd == -1)
return errno;
FD_ZERO(&set);
FD_SET(fd, &set);
return 0;
}
static int
open_log(void)
{
if (EMPTY(config.output_file))
fo = stdout;
else if ((fo = fopen(config.output_file, config.append ? "a" : "w"))
== NULL)
return errno;
if (set_fdset() != 0)
return errno;
if (obuf != NULL)
free(obuf);
obuf = (char *) malloc(config.output_bufsiz);
if (obuf == NULL)
return errno;
if (setvbuf(fo, obuf, _IOFBF, config.output_bufsiz) != 0)
return errno;
return 0;
}
static inline void
wrt_return_freelist(void)
{
DATA_Return_Freelist(&wrt_freelist, wrt_nfree);
LOG_Log(LOG_DEBUG, "Writer: returned %u to free list", wrt_nfree);
wrt_nfree = 0;
assert(VSTAILQ_EMPTY(&wrt_freelist));
if (RDR_Waiting()) {
AZ(pthread_mutex_lock(&data_ready_lock));
AZ(pthread_cond_signal(&data_ready_cond));
AZ(pthread_mutex_unlock(&data_ready_lock));
}
}
static inline void
wrt_write(tx_t *tx)
{
int errnum;
CHECK_OBJ_NOTNULL(tx, TX_MAGIC);
assert(tx->state == TX_DONE);
AZ(pthread_mutex_lock(&reopen_lock));
if (reopen && fo != stdout) {
if (fflush(fo) != 0)
LOG_Log(LOG_ALERT, "Cannot flush to %s, DATA DISCARDED: %s",
config.output_file, strerror(errno));
if (fclose(fo) != 0) {
LOG_Log(LOG_ALERT, "Cannot close %s, exiting: %s",
config.output_file, strerror(errno));
exit(EXIT_FAILURE);
}
if ((errnum = open_log()) != 0) {
LOG_Log(LOG_ALERT, "Cannot reopen %s, exiting: %s",
config.output_file, strerror(errnum));
exit(EXIT_FAILURE);
}
reopen = 0;
}
AZ(pthread_mutex_unlock(&reopen_lock));
VSB_clear(os);
#if 0
FMT_Format(tx, os);
#endif
VSB_finish(os);
if (timeout != NULL)
to = config.output_timeout;
if ((errnum = select(fd + 1, NULL, &set, NULL, timeout)) == -1) {
LOG_Log(LOG_ALERT,
"Error waiting for ready output %d (%s), DATA DISCARDED: %s",
errno, strerror(errno), VSB_data(os));
errors++;
if (set_fdset() != 0) {
LOG_Log(LOG_ALERT,
"Cannot reset fd set after select() error, exiting: %s",
strerror(errno));
exit(EXIT_FAILURE);
}
}
else if (errnum == 0) {
LOG_Log(LOG_ALERT,
"Timeout waiting for ready output, DATA DISCARDED: %s",
VSB_data(os));
timeouts++;
if (set_fdset() != 0) {
LOG_Log(LOG_ALERT, "Cannot reset fd set after timeout, exiting: %s",
strerror(errno));
exit(EXIT_FAILURE);
}
}
else if (errnum != 1)
WRONG("More than one ready file descriptor for output");
else if (!FD_ISSET(fd, &set))
WRONG("Wrong file descriptor found ready for output");
else if (fprintf(fo, "%s", VSB_data(os)) < 0) {
LOG_Log(LOG_ALERT, "Output error %d (%s), DATA DISCARDED: %s",
errno, strerror(errno), VSB_data(os));
errors++;
}
else {
writes++;
bytes += VSB_len(os);
}
MON_StatsUpdate(STATS_WRITTEN);
/* clean up */
DATA_Clear_Logline(tx);
VSTAILQ_INSERT_TAIL(&wrt_freelist, tx, freelist);
wrt_nfree++;
if (global_nfree < (config.max_data >> 1) || RDR_Waiting())
wrt_return_freelist();
}
static void
*wrt_main(void *arg)
{
writer_data_t *wrt = (writer_data_t *) arg;
tx_t *tx;
LOG_Log0(LOG_INFO, "Writer thread starting");
CHECK_OBJ_NOTNULL(wrt, WRITER_DATA_MAGIC);
wrt->state = WRT_INITIALIZING;
VSTAILQ_INIT(&wrt_freelist);
wrt_nfree = 0;
wrt->state = WRT_RUNNING;
while (run) {
tx = SPSCQ_Deq();
if (tx != NULL) {
deqs++;
CHECK_OBJ(tx, TX_MAGIC);
wrt_write(tx);
continue;
}
/*
* wait until data are available, or quit is signaled.
* flush ouput and return space before sleeping
*/
if (fflush(fo) != 0) {
LOG_Log(LOG_ALERT, "Output flush failed, error %d (%s)",
errno, strerror(errno));
errors++;
}
if (wrt_nfree > 0)
wrt_return_freelist();
wrt->state = WRT_WAITING;
AZ(pthread_mutex_lock(&spscq_ready_lock));
/*
* run is guaranteed to be fresh after the lock
*/
if (run && !RDR_Waiting()) {
waits++;
AZ(pthread_cond_wait(&spscq_ready_cond, &spscq_ready_lock));
}
wrt->state = WRT_RUNNING;
AZ(pthread_mutex_unlock(&spscq_ready_lock));
}
wrt->state = WRT_SHUTTINGDOWN;
/* Prepare to exit, drain the queue */
while ((tx = SPSCQ_Deq()) != NULL) {
deqs++;
CHECK_OBJ(tx, TX_MAGIC);
wrt_write(tx);
}
if (fflush(fo) != 0) {
LOG_Log(LOG_ALERT, "Output flush failed, error %d (%s)",
errno, strerror(errno));
errors++;
}
wrt->status = EXIT_SUCCESS;
LOG_Log0(LOG_INFO, "Writer thread exiting");
wrt->state = WRT_EXITED;
pthread_exit((void *) wrt);
}
int
WRT_Init(void)
{
int err;
if ((err = open_log()) != 0)
return err;
wrt_data.magic = WRITER_DATA_MAGIC;
wrt_data.state = WRT_NOTSTARTED;
/* XXX: fixed size? */
os = VSB_new_auto();
if (config.output_timeout.tv_sec != 0
|| config.output_timeout.tv_usec != 0) {
to = config.output_timeout;
timeout = &to;
}
run = 1;
return 0;
}
void
WRT_Start(void)
{
AZ(pthread_create(&writer, NULL, wrt_main, &wrt_data));
}
void
WRT_Stats(void)
{
if (!run) return;
LOG_Log(LOG_INFO,
"Writer (%s): seen=%lu writes=%lu bytes=%lu errors=%lu timeouts=%lu"
" waits=%lu free=%u",
statename[wrt_data.state], deqs, writes, bytes, errors, timeouts, waits,
wrt_nfree);
}
int
WRT_Running(void)
{
return wrt_data.state > WRT_INITIALIZING
&& wrt_data.state < WRT_EXITED;
}
int
WRT_Waiting(void)
{
return wrt_data.state == WRT_WAITING;
}
void
WRT_Reopen(void)
{
AZ(pthread_mutex_lock(&reopen_lock));
reopen = 1;
AZ(pthread_mutex_unlock(&reopen_lock));
}
void
WRT_Halt(void)
{
writer_data_t *wrt;
AZ(pthread_mutex_lock(&spscq_ready_lock));
run = 0;
AZ(pthread_cond_signal(&spscq_ready_cond));
AZ(pthread_mutex_unlock(&spscq_ready_lock));
AZ(pthread_join(writer, (void **) &wrt));
CHECK_OBJ_NOTNULL(wrt, WRITER_DATA_MAGIC);
if (wrt->status != EXIT_SUCCESS)
LOG_Log0(LOG_ERR, "Writer thread returned failure status");
}
void
WRT_Shutdown(void)
{
/* WRT_Halt() must always be called first */
AZ(run);
fclose(fo);
free(obuf);
VSB_delete(os);
AZ(pthread_mutex_destroy(&reopen_lock));
}
#ifdef TEST_DRIVER
#include "minunit.h"
int tests_run = 0;
static char errmsg[BUFSIZ];
#define THRESHOLD 1000
int
RDR_Waiting(void)
{
return 0;
}
void
RDR_Stats(void)
{}
static char
*test_timeout(void)
{
logline_t ll;
printf("... testing write timeouts\n");
strcpy(config.cformat, "");
MAZ(FMT_Init(&errmsg[0]));
strcpy(config.log_file, "-");
MAZ(LOG_Open("test_writer"));
config.output_timeout.tv_sec = 1;
config.output_timeout.tv_usec = 0;
MAZ(WRT_Init());
VSTAILQ_INIT(&wrt_freelist);
MASSERT(VSTAILQ_EMPTY(&wrt_freelist));
for (int i = 0; i < THRESHOLD; i++) {
ll.magic = LOGLINE_MAGIC;
ll.state = DATA_DONE;
ll.spec = VSL_S_CLIENT;
ll.rx_headers = NULL;
ll.tx_headers = NULL;
ll.vcl_log = NULL;
ll.vcl_call = NULL;
wrt_write(&ll);
MAZ(to.tv_sec);
MASSERT(1e6 - to.tv_usec < THRESHOLD);
}
return NULL;
}
static const char
*all_tests(void)
{
mu_run_test(test_timeout);
return NULL;
}
TEST_RUNNER
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment