1
0
Fork 0

Adding upstream version 11.74.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-03-24 13:01:01 +01:00
parent fb45dd789e
commit 24ecce9d56
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
92 changed files with 43156 additions and 0 deletions

49
tools/src/Makefile.am Normal file
View file

@ -0,0 +1,49 @@
# $Id: Makefile.am 2486 2018-09-03 20:22:17Z pbuchbinder $
bin_PROGRAMS=qsfilter2 qslog qspng qsrotate qssign qstail qshead qsgrep qsexec qscheck qsgeo qslogger qsdt qsrespeed qsre
qsfilter2_SOURCES= \
qsfilter2.c qs_util.c
qslog_SOURCES= \
qslog.c qs_util.c
qspng_SOURCES= \
qspng.c qs_util.c
qsrotate_SOURCES= \
qsrotate.c qs_util.c
qssign_SOURCES= \
qssign.c qs_util.c qs_apo.c
qstail_SOURCES= \
qstail.c qs_util.c
qshead_SOURCES= \
qshead.c qs_util.c
qsgrep_SOURCES= \
qsgrep.c qs_util.c
qsexec_SOURCES= \
qsexec.c qs_util.c
qscheck_SOURCES= \
qscheck.c qs_util.c
qsgeo_SOURCES= \
qsgeo.c qs_util.c
qslogger_SOURCES= \
qslogger.c qs_util.c
qsdt_SOURCES= \
qsdt.c qs_util.c
qsrespeed_SOURCES= \
qsrespeed.c qs_util.c
qsre_SOURCES= \
qsre.c qs_util.c

734
tools/src/Makefile.in Normal file
View file

@ -0,0 +1,734 @@
# Makefile.in generated by automake 1.15 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
# $Id: Makefile.am 2486 2018-09-03 20:22:17Z pbuchbinder $
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
bin_PROGRAMS = qsfilter2$(EXEEXT) qslog$(EXEEXT) qspng$(EXEEXT) \
qsrotate$(EXEEXT) qssign$(EXEEXT) qstail$(EXEEXT) \
qshead$(EXEEXT) qsgrep$(EXEEXT) qsexec$(EXEEXT) \
qscheck$(EXEEXT) qsgeo$(EXEEXT) qslogger$(EXEEXT) \
qsdt$(EXEEXT) qsrespeed$(EXEEXT) qsre$(EXEEXT)
subdir = src
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
PROGRAMS = $(bin_PROGRAMS)
am_qscheck_OBJECTS = qscheck.$(OBJEXT) qs_util.$(OBJEXT)
qscheck_OBJECTS = $(am_qscheck_OBJECTS)
qscheck_LDADD = $(LDADD)
am_qsdt_OBJECTS = qsdt.$(OBJEXT) qs_util.$(OBJEXT)
qsdt_OBJECTS = $(am_qsdt_OBJECTS)
qsdt_LDADD = $(LDADD)
am_qsexec_OBJECTS = qsexec.$(OBJEXT) qs_util.$(OBJEXT)
qsexec_OBJECTS = $(am_qsexec_OBJECTS)
qsexec_LDADD = $(LDADD)
am_qsfilter2_OBJECTS = qsfilter2.$(OBJEXT) qs_util.$(OBJEXT)
qsfilter2_OBJECTS = $(am_qsfilter2_OBJECTS)
qsfilter2_LDADD = $(LDADD)
am_qsgeo_OBJECTS = qsgeo.$(OBJEXT) qs_util.$(OBJEXT)
qsgeo_OBJECTS = $(am_qsgeo_OBJECTS)
qsgeo_LDADD = $(LDADD)
am_qsgrep_OBJECTS = qsgrep.$(OBJEXT) qs_util.$(OBJEXT)
qsgrep_OBJECTS = $(am_qsgrep_OBJECTS)
qsgrep_LDADD = $(LDADD)
am_qshead_OBJECTS = qshead.$(OBJEXT) qs_util.$(OBJEXT)
qshead_OBJECTS = $(am_qshead_OBJECTS)
qshead_LDADD = $(LDADD)
am_qslog_OBJECTS = qslog.$(OBJEXT) qs_util.$(OBJEXT)
qslog_OBJECTS = $(am_qslog_OBJECTS)
qslog_LDADD = $(LDADD)
am_qslogger_OBJECTS = qslogger.$(OBJEXT) qs_util.$(OBJEXT)
qslogger_OBJECTS = $(am_qslogger_OBJECTS)
qslogger_LDADD = $(LDADD)
am_qspng_OBJECTS = qspng.$(OBJEXT) qs_util.$(OBJEXT)
qspng_OBJECTS = $(am_qspng_OBJECTS)
qspng_LDADD = $(LDADD)
am_qsre_OBJECTS = qsre.$(OBJEXT) qs_util.$(OBJEXT)
qsre_OBJECTS = $(am_qsre_OBJECTS)
qsre_LDADD = $(LDADD)
am_qsrespeed_OBJECTS = qsrespeed.$(OBJEXT) qs_util.$(OBJEXT)
qsrespeed_OBJECTS = $(am_qsrespeed_OBJECTS)
qsrespeed_LDADD = $(LDADD)
am_qsrotate_OBJECTS = qsrotate.$(OBJEXT) qs_util.$(OBJEXT)
qsrotate_OBJECTS = $(am_qsrotate_OBJECTS)
qsrotate_LDADD = $(LDADD)
am_qssign_OBJECTS = qssign.$(OBJEXT) qs_util.$(OBJEXT) \
qs_apo.$(OBJEXT)
qssign_OBJECTS = $(am_qssign_OBJECTS)
qssign_LDADD = $(LDADD)
am_qstail_OBJECTS = qstail.$(OBJEXT) qs_util.$(OBJEXT)
qstail_OBJECTS = $(am_qstail_OBJECTS)
qstail_LDADD = $(LDADD)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(qscheck_SOURCES) $(qsdt_SOURCES) $(qsexec_SOURCES) \
$(qsfilter2_SOURCES) $(qsgeo_SOURCES) $(qsgrep_SOURCES) \
$(qshead_SOURCES) $(qslog_SOURCES) $(qslogger_SOURCES) \
$(qspng_SOURCES) $(qsre_SOURCES) $(qsrespeed_SOURCES) \
$(qsrotate_SOURCES) $(qssign_SOURCES) $(qstail_SOURCES)
DIST_SOURCES = $(qscheck_SOURCES) $(qsdt_SOURCES) $(qsexec_SOURCES) \
$(qsfilter2_SOURCES) $(qsgeo_SOURCES) $(qsgrep_SOURCES) \
$(qshead_SOURCES) $(qslog_SOURCES) $(qslogger_SOURCES) \
$(qspng_SOURCES) $(qsre_SOURCES) $(qsrespeed_SOURCES) \
$(qsrotate_SOURCES) $(qssign_SOURCES) $(qstail_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host_alias = @host_alias@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
qsfilter2_SOURCES = \
qsfilter2.c qs_util.c
qslog_SOURCES = \
qslog.c qs_util.c
qspng_SOURCES = \
qspng.c qs_util.c
qsrotate_SOURCES = \
qsrotate.c qs_util.c
qssign_SOURCES = \
qssign.c qs_util.c qs_apo.c
qstail_SOURCES = \
qstail.c qs_util.c
qshead_SOURCES = \
qshead.c qs_util.c
qsgrep_SOURCES = \
qsgrep.c qs_util.c
qsexec_SOURCES = \
qsexec.c qs_util.c
qscheck_SOURCES = \
qscheck.c qs_util.c
qsgeo_SOURCES = \
qsgeo.c qs_util.c
qslogger_SOURCES = \
qslogger.c qs_util.c
qsdt_SOURCES = \
qsdt.c qs_util.c
qsrespeed_SOURCES = \
qsrespeed.c qs_util.c
qsre_SOURCES = \
qsre.c qs_util.c
all: all-am
.SUFFIXES:
.SUFFIXES: .c .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu src/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
$(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p \
; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n;h' \
-e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
{ d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
if ($$2 == $$4) files[d] = files[d] " " $$1; \
else { print "f", $$3 "/" $$4, $$1; } } \
END { for (d in files) print "f", d, files[d] }' | \
while read type dir files; do \
if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
test -z "$$files" || { \
echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
$(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
} \
; done
uninstall-binPROGRAMS:
@$(NORMAL_UNINSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-e 's/$$/$(EXEEXT)/' \
`; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && rm -f $$files
clean-binPROGRAMS:
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
qscheck$(EXEEXT): $(qscheck_OBJECTS) $(qscheck_DEPENDENCIES) $(EXTRA_qscheck_DEPENDENCIES)
@rm -f qscheck$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qscheck_OBJECTS) $(qscheck_LDADD) $(LIBS)
qsdt$(EXEEXT): $(qsdt_OBJECTS) $(qsdt_DEPENDENCIES) $(EXTRA_qsdt_DEPENDENCIES)
@rm -f qsdt$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qsdt_OBJECTS) $(qsdt_LDADD) $(LIBS)
qsexec$(EXEEXT): $(qsexec_OBJECTS) $(qsexec_DEPENDENCIES) $(EXTRA_qsexec_DEPENDENCIES)
@rm -f qsexec$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qsexec_OBJECTS) $(qsexec_LDADD) $(LIBS)
qsfilter2$(EXEEXT): $(qsfilter2_OBJECTS) $(qsfilter2_DEPENDENCIES) $(EXTRA_qsfilter2_DEPENDENCIES)
@rm -f qsfilter2$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qsfilter2_OBJECTS) $(qsfilter2_LDADD) $(LIBS)
qsgeo$(EXEEXT): $(qsgeo_OBJECTS) $(qsgeo_DEPENDENCIES) $(EXTRA_qsgeo_DEPENDENCIES)
@rm -f qsgeo$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qsgeo_OBJECTS) $(qsgeo_LDADD) $(LIBS)
qsgrep$(EXEEXT): $(qsgrep_OBJECTS) $(qsgrep_DEPENDENCIES) $(EXTRA_qsgrep_DEPENDENCIES)
@rm -f qsgrep$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qsgrep_OBJECTS) $(qsgrep_LDADD) $(LIBS)
qshead$(EXEEXT): $(qshead_OBJECTS) $(qshead_DEPENDENCIES) $(EXTRA_qshead_DEPENDENCIES)
@rm -f qshead$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qshead_OBJECTS) $(qshead_LDADD) $(LIBS)
qslog$(EXEEXT): $(qslog_OBJECTS) $(qslog_DEPENDENCIES) $(EXTRA_qslog_DEPENDENCIES)
@rm -f qslog$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qslog_OBJECTS) $(qslog_LDADD) $(LIBS)
qslogger$(EXEEXT): $(qslogger_OBJECTS) $(qslogger_DEPENDENCIES) $(EXTRA_qslogger_DEPENDENCIES)
@rm -f qslogger$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qslogger_OBJECTS) $(qslogger_LDADD) $(LIBS)
qspng$(EXEEXT): $(qspng_OBJECTS) $(qspng_DEPENDENCIES) $(EXTRA_qspng_DEPENDENCIES)
@rm -f qspng$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qspng_OBJECTS) $(qspng_LDADD) $(LIBS)
qsre$(EXEEXT): $(qsre_OBJECTS) $(qsre_DEPENDENCIES) $(EXTRA_qsre_DEPENDENCIES)
@rm -f qsre$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qsre_OBJECTS) $(qsre_LDADD) $(LIBS)
qsrespeed$(EXEEXT): $(qsrespeed_OBJECTS) $(qsrespeed_DEPENDENCIES) $(EXTRA_qsrespeed_DEPENDENCIES)
@rm -f qsrespeed$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qsrespeed_OBJECTS) $(qsrespeed_LDADD) $(LIBS)
qsrotate$(EXEEXT): $(qsrotate_OBJECTS) $(qsrotate_DEPENDENCIES) $(EXTRA_qsrotate_DEPENDENCIES)
@rm -f qsrotate$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qsrotate_OBJECTS) $(qsrotate_LDADD) $(LIBS)
qssign$(EXEEXT): $(qssign_OBJECTS) $(qssign_DEPENDENCIES) $(EXTRA_qssign_DEPENDENCIES)
@rm -f qssign$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qssign_OBJECTS) $(qssign_LDADD) $(LIBS)
qstail$(EXEEXT): $(qstail_OBJECTS) $(qstail_DEPENDENCIES) $(EXTRA_qstail_DEPENDENCIES)
@rm -f qstail$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(qstail_OBJECTS) $(qstail_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qs_apo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qs_util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qscheck.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsdt.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsexec.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsfilter2.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsgeo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsgrep.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qshead.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qslog.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qslogger.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qspng.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsre.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsrespeed.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsrotate.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qssign.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qstail.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS)
installdirs:
for dir in "$(DESTDIR)$(bindir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-binPROGRAMS
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \
distclean distclean-compile distclean-generic distclean-tags \
distdir dvi dvi-am html html-am info info-am install \
install-am install-binPROGRAMS install-data install-data-am \
install-dvi install-dvi-am install-exec install-exec-am \
install-html install-html-am install-info install-info-am \
install-man install-pdf install-pdf-am install-ps \
install-ps-am install-strip installcheck installcheck-am \
installdirs maintainer-clean maintainer-clean-generic \
mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
ps ps-am tags tags-am uninstall uninstall-am \
uninstall-binPROGRAMS
.PRECIOUS: Makefile
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

539
tools/src/char.h Normal file
View file

@ -0,0 +1,539 @@
/**
* Utilities for the quality of service module mod_qos.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#define S_W_MAX 6
#define S_H_MAX 7
static int s_0[S_H_MAX][S_W_MAX] = {
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_1[S_H_MAX][S_W_MAX] = {
{ 0,0,0,1,0,0},
{ 0,0,1,1,0,0},
{ 0,1,0,1,0,0},
{ 0,0,0,1,0,0},
{ 0,0,0,1,0,0},
{ 0,0,0,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_2[S_H_MAX][S_W_MAX] = {
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 0,0,0,1,0,0},
{ 0,0,1,0,0,0},
{ 0,1,0,0,0,0},
{ 1,1,1,1,1,0},
{ 0,0,0,0,0,0}
};
static int s_3[S_H_MAX][S_W_MAX] = {
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 0,0,1,1,0,0},
{ 0,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_4[S_H_MAX][S_W_MAX] = {
{ 0,0,0,1,0,0},
{ 0,0,1,0,0,0},
{ 0,1,0,0,0,0},
{ 1,0,0,1,0,0},
{ 1,1,1,1,1,0},
{ 0,0,0,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_5[S_H_MAX][S_W_MAX] = {
{ 1,1,1,1,1,0},
{ 1,0,0,0,0,0},
{ 1,1,1,1,0,0},
{ 0,0,0,0,1,0},
{ 0,0,0,0,1,0},
{ 1,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_6[S_H_MAX][S_W_MAX] = {
{ 0,1,1,1,0,0},
{ 1,0,0,0,0,0},
{ 1,0,1,1,0,0},
{ 1,1,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_7[S_H_MAX][S_W_MAX] = {
{ 1,1,1,1,1,0},
{ 0,0,0,0,1,0},
{ 0,0,0,1,0,0},
{ 0,0,1,0,0,0},
{ 0,1,0,0,0,0},
{ 0,1,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_8[S_H_MAX][S_W_MAX] = {
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_9[S_H_MAX][S_W_MAX] = {
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 0,0,0,0,1,0},
{ 0,0,0,0,1,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
/* ----------------------------------------------- */
static int s_a[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 0,0,0,0,0,0}
};
static int s_b[S_H_MAX][S_W_MAX] = {
{ 1,0,0,0,0,0},
{ 1,0,0,0,0,0},
{ 1,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_c[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,0,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_d[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,1,0},
{ 0,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 0,0,0,0,0,0}
};
static int s_e[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 1,1,1,1,1,0},
{ 1,0,0,0,0,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_f[S_H_MAX][S_W_MAX] = {
{ 0,0,1,1,0,0},
{ 0,1,0,0,0,0},
{ 1,1,1,0,0,0},
{ 0,1,0,0,0,0},
{ 0,1,0,0,0,0},
{ 0,1,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_g[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,1,1,1,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 0,0,0,0,1,0},
{ 0,1,1,1,0,0}
};
static int s_h[S_H_MAX][S_W_MAX] = {
{ 1,0,0,0,0,0},
{ 1,0,0,0,0,0},
{ 1,0,1,1,0,0},
{ 1,1,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,0,0,0,0,0}
};
static int s_i[S_H_MAX][S_W_MAX] = {
{ 0,0,1,0,0,0},
{ 0,0,0,0,0,0},
{ 0,1,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_j[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,1,0},
{ 0,0,0,0,0,0},
{ 0,0,0,1,1,0},
{ 0,0,0,0,1,0},
{ 0,0,0,0,1,0},
{ 0,0,0,0,1,0},
{ 0,0,1,1,0,0}
};
static int s_k[S_H_MAX][S_W_MAX] = {
{ 1,0,0,0,0,0},
{ 1,0,0,0,0,0},
{ 1,0,1,1,0,0},
{ 1,1,0,0,0,0},
{ 1,0,1,0,0,0},
{ 1,0,0,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_l[S_H_MAX][S_W_MAX] = {
{ 0,1,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_m[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,1,0,1,0,0},
{ 1,0,1,0,1,0},
{ 1,0,1,0,1,0},
{ 1,0,1,0,1,0},
{ 1,0,1,0,1,0},
{ 0,0,0,0,0,0}
};
static int s_n[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,0,1,1,0,0},
{ 1,1,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,0,0,0,0,0}
};
static int s_o[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_p[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,1,1,1,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,1,1,1,0,0},
{ 1,0,0,0,0,0},
{ 1,0,0,0,0,0}
};
static int s_q[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,1,1,1,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 0,0,0,0,1,0},
{ 0,0,0,0,1,0}
};
static int s_r[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,0,1,1,0,0},
{ 1,1,0,0,1,0},
{ 1,0,0,0,0,0},
{ 1,0,0,0,0,0},
{ 1,0,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_s[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,1,1,1,1,0},
{ 1,0,0,0,0,0},
{ 0,1,1,1,0,0},
{ 0,0,0,0,1,0},
{ 1,1,1,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_t[S_H_MAX][S_W_MAX] = {
{ 0,0,1,0,0,0},
{ 0,1,1,1,0,0},
{ 0,0,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,1,0,1,0},
{ 0,0,0,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_u[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 0,0,0,0,0,0}
};
static int s_v[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,0,1,0,0},
{ 0,0,1,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_w[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,1,0,1,0},
{ 1,1,0,1,1,0},
{ 1,0,0,0,1,0},
{ 0,0,0,0,0,0}
};
static int s_x[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,0,0,0,1,0},
{ 0,1,0,1,0,0},
{ 0,0,1,0,0,0},
{ 0,1,0,1,0,0},
{ 1,0,0,0,1,0},
{ 0,0,0,0,0,0}
};
static int s_y[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,1,1,1,1,0},
{ 0,0,0,0,1,0},
{ 1,1,1,1,0,0}
};
static int s_z[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 1,1,1,1,1,0},
{ 0,0,0,1,1,0},
{ 0,0,1,0,0,0},
{ 1,1,0,0,0,0},
{ 1,1,1,1,1,0},
{ 0,0,0,0,0,0}
};
static int s_BRO[S_H_MAX][S_W_MAX] = {
{ 0,0,0,1,0,0},
{ 0,0,1,0,0,0},
{ 0,1,0,0,0,0},
{ 0,1,0,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,0,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_BRC[S_H_MAX][S_W_MAX] = {
{ 0,0,1,0,0,0},
{ 0,0,0,1,0,0},
{ 0,0,0,0,1,0},
{ 0,0,0,0,1,0},
{ 0,0,0,1,0,0},
{ 0,0,1,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_MI[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 1,1,1,1,1,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_LT[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,0,0,1,0,0},
{ 0,1,1,0,0,0},
{ 1,0,0,0,0,0},
{ 0,1,1,0,0,0},
{ 0,0,0,1,0,0},
{ 0,0,0,0,0,0}
};
static int s_GT[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,1,0,0,0,0},
{ 0,0,1,1,0,0},
{ 0,0,0,0,1,0},
{ 0,0,1,1,0,0},
{ 0,1,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_SP[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_US[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 1,1,1,1,1,0},
{ 0,0,0,0,0,0}
};
static int s_M[S_H_MAX][S_W_MAX] = {
{ 1,0,0,0,1,0},
{ 1,1,0,1,1,0},
{ 1,0,1,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 1,0,0,0,1,0},
{ 0,0,0,0,0,0}
};
static int s_DT[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,1,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_CM[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,1,0,0,0},
{ 0,1,0,0,0,0}
};
static int s_SC[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,1,0,0,0},
{ 0,1,0,0,0,0}
};
static int s_CO[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_SL[S_H_MAX][S_W_MAX] = {
{ 0,0,0,0,0,1},
{ 0,0,0,0,1,0},
{ 0,0,0,1,0,0},
{ 0,0,1,0,0,0},
{ 0,1,0,0,0,0},
{ 1,0,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_SQ[S_H_MAX][S_W_MAX] = {
{ 0,0,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,1,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0},
{ 0,0,0,0,0,0}
};
static int s_X[S_H_MAX][S_W_MAX] = {
{ 1,1,1,1,1,0},
{ 1,1,1,1,1,0},
{ 1,1,1,1,1,0},
{ 1,1,1,1,1,0},
{ 1,1,1,1,1,0},
{ 1,1,1,1,1,0},
{ 0,0,0,0,0,0}
};

135
tools/src/qs_apo.c Normal file
View file

@ -0,0 +1,135 @@
/**
* Utilities for the quality of service module mod_qos.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qs_apo.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
/* apr/apr-util */
#include <apr.h>
#include <apr_base64.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include <apr_thread_proc.h>
#include <apr_file_io.h>
#include <apr_time.h>
#include "qs_util.h"
#include "qs_apo.h"
static apr_table_t *qs_args(apr_pool_t *pool, const char *line) {
char *last = apr_pstrdup(pool, line);
apr_table_t* table = apr_table_make(pool, 10);
char *val;
while((val = apr_strtok(NULL, " ", &last))) {
apr_table_addn(table, val, "");
}
return table;
}
static void qs_failedexec(const char *msg, const char *cmd, apr_status_t status) {
char buf[MAX_LINE];
apr_strerror(status, buf, sizeof(buf));
fprintf(stderr, "ERROR %s '%s': '%s'\n", msg, cmd, buf);
exit(1);
}
/**
* Reads a passphrase using the defined passphrase getter (executes
* the program and reads the passphras from stdout).
*
* @param pool To allocate memory
* @param prg Path of the program to exectue
* @return The passphrase
*/
char *qs_readpwd(apr_pool_t *pool, const char *prg) {
apr_status_t status;
apr_proc_t proc;
const char **args;
apr_table_entry_t *entry;
char *last;
char *copy = apr_pstrdup(pool, prg);
char *cmd = apr_strtok(copy, " ", &last);
apr_table_t *a = qs_args(pool, prg);
int i;
apr_procattr_t *attr;
apr_size_t len = MAX_LINE;
char *buf = apr_pcalloc(pool, len);
args = apr_pcalloc(pool, (apr_table_elts(a)->nelts + 1) * sizeof(const char *));
entry = (apr_table_entry_t *) apr_table_elts(a)->elts;
for(i = 0; i < apr_table_elts(a)->nelts; i++) {
args[i] = entry[i].key;
}
args[i] = NULL;
if(cmd == NULL) {
qs_failedexec("can't read password, invalid executable", prg, APR_EGENERAL);
}
if((status = apr_procattr_create(&attr, pool)) != APR_SUCCESS) {
qs_failedexec("while reading password from executable", prg, status);
}
if((status = apr_procattr_cmdtype_set(attr, APR_PROGRAM_PATH)) != APR_SUCCESS) {
qs_failedexec("while reading password from executable", prg, status);
}
if((status = apr_procattr_detach_set(attr, 0)) != APR_SUCCESS) {
qs_failedexec("while reading password from executable", prg, status);
}
if((status = apr_procattr_io_set(attr, APR_FULL_BLOCK, APR_FULL_BLOCK, APR_NO_PIPE)) != APR_SUCCESS) {
qs_failedexec("while reading password from executable", prg, status);
}
if((status = apr_proc_create(&proc, cmd, args, NULL, attr, pool)) != APR_SUCCESS) {
qs_failedexec("could not execute program", prg, status);
} else {
char *e;
status = apr_proc_wait(&proc, NULL, NULL, APR_WAIT);
if(status != APR_CHILD_DONE && status != APR_SUCCESS) {
qs_failedexec("while reading password from executable", prg, status);
}
status = apr_file_read(proc.out, buf, &len);
if(status != APR_SUCCESS) {
qs_failedexec("failed to read password from program", prg, status);
}
e = buf;
while(e && e[0]) {
if((e[0] == LF) || (e[0] == CR)) {
e[0] = '\0';
} else {
e++;
}
}
}
return buf;
}

31
tools/src/qs_apo.h Normal file
View file

@ -0,0 +1,31 @@
/**
* Utilities for the quality of service module mod_qos.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef QS_APO_H
#define QS_APO_H
char *qs_readpwd(apr_pool_t *pool, const char *prg);
#endif

409
tools/src/qs_util.c Normal file
View file

@ -0,0 +1,409 @@
/**
* Utilities for the quality of service module mod_qos.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
static const char revision[] = "$Id: qs_util.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
typedef pcre2_match_data* match_data_pt;
typedef size_t* match_vector_pt;
#include "qs_util.h"
/* mutex for counter access */
static pthread_mutex_t m_qs_lock_cs;
/* online/offline mode */
static int m_qs_offline = 0;
/* internal clock for offline analysis
* stores time in seconds */
static time_t m_qs_virtualSystemTime = 0;
/* ----------------------------------
* functions
* ---------------------------------- */
/**
* man:
* - escape special chars, like "\" and "-"
* - wipe leading spaces
* - wipe tailing LF
*/
void qs_man_print(int man, const char *fmt, ...) {
char bufin[4096];
char bufout[4096];
va_list args;
int i = 0;
int j = 0;
memset(bufin, 0, 4096);
va_start(args, fmt);
vsprintf(bufin, fmt, args);
if(man) {
// wipe leading spaces
// while(bufin[i] == ' ' && bufin[i+1] == ' ') {
while(bufin[i] == ' ') {
i++;
}
}
while(bufin[i] && j < 4000) {
// escape "\\" and "-" for man page
if(man && (bufin[i] == '\\' || bufin[i] == '-')) {
bufout[j] = '\\';
j++;
}
if(bufin[i] == '\n') {
if(man) {
// skip LF for man page
i++;
} else {
// keep LF
bufout[j] = bufin[i];
i++;
j++;
}
} else {
// standard char
bufout[j] = bufin[i];
i++;
j++;
}
}
bufout[j] = '\0';
printf("%s", bufout);
if(man) {
printf(" ");
}
}
// escape only
void qs_man_println(int man, const char *fmt, ...) {
char bufin[4096];
char bufout[4096];
va_list args;
int i = 0;
int j = 0;
memset(bufin, 0, 4096);
va_start(args, fmt);
vsprintf(bufin, fmt, args);
while(bufin[i] && j < 4000) {
// escape "\\" and "-" for man page
if(man && (bufin[i] == '\\' || bufin[i] == '-')) {
bufout[j] = '\\';
j++;
}
// standard char
bufout[j] = bufin[i];
i++;
j++;
}
bufout[j] = '\0';
printf("%s", bufout);
}
char *qs_CMD(const char *cmd) {
char *buf = calloc(1024, 1);
int i = 0;
while(cmd[i] && i < 1023) {
buf[i] = toupper(cmd[i]);
i++;
}
buf[i] = '\0';
return buf;
}
/* io --------------------------------------------------------- */
/*
* reads a line from stdin
*
* @param s Buffer to write line to
* @param n Length of the buffer
* @return 0 on EOF, or 1 if there is more data to read
*/
int qs_getLine(char *s, int n) {
int i = 0;
while (1) {
s[i] = (char)getchar();
if(s[i] == EOF) return 0;
if (s[i] == CR) {
s[i] = getchar();
}
if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
s[i] = '\0';
return 1;
}
++i;
}
}
/*
* reads a line from file
*
* @param s Buffer to write line to
* @param n Length of the buffer
* @return 0 on EOF, or 1 if there is more data to read
*/
int qs_getLinef(char *s, int n, FILE *f) {
register int i = 0;
while (1) {
s[i] = (char) fgetc(f);
if (s[i] == CR) {
s[i] = fgetc(f);
}
if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
s[i] = '\0';
return (feof(f) ? 1 : 0);
}
++i;
}
}
/* time ------------------------------------------------------- */
/*
* We implement our own time which is either
* the system time (real time) or the time from
* the access log lines (offline) if m_qs_offline
* has been set (use qs_set2OfflineMode() to enable
* the offline mode).
*
* @param tme Set to the time since the Epoch in seconds.
*/
void qs_time(time_t *tme) {
if(m_qs_offline) {
/* use virtual time from the access log */
*tme = m_qs_virtualSystemTime;
} else {
time(tme);
}
}
/**
* Sets time measurement (qs_time()) to offline mode.
*/
void qs_set2OfflineMode() {
m_qs_offline = 1;
}
/*
* Updates the virtual time.
*/
void qs_setTime(time_t tme) {
m_qs_virtualSystemTime = tme;
}
/* synchronisation -------------------------------------------- */
/*
* locks all counter
*/
void qs_csLock() {
pthread_mutex_lock(&m_qs_lock_cs);
}
/*
* unlocks all counter
*/
void qs_csUnLock() {
pthread_mutex_unlock(&m_qs_lock_cs);
}
/*
* init locks
*/
void qs_csInitLock() {
pthread_mutex_init(&m_qs_lock_cs, NULL);
}
/* logs ------------------------------------------------------- */
/**
* Keeps only the specified number of files
*
* @param file_name Absolute file name
* @param generations Number of files to keep
*/
void qs_deleteOldFiles(const char *file_name, int generations) {
DIR *dir;
char dirname[QS_HUGE_STR];
char *p;
memset(dirname, 0, QS_HUGE_STR);
if(strlen(file_name) > (QS_HUGE_STR - 12)) {
// invalid file length
return;
}
if(strrchr(file_name, '/') == NULL) {
sprintf(dirname, "./%s", file_name);
} else {
strcpy(dirname, file_name);
}
p = strrchr(dirname, '/');
p[0] = '\0'; p++;
dir = opendir(dirname);
if(dir) {
int num = 0;
struct dirent *de;
char filename[QS_HUGE_STR];
snprintf(filename, sizeof(filename), "%s.20", p);
/* determine how many files to delete */
while((de = readdir(dir)) != 0) {
if(de->d_name && (strncmp(de->d_name, filename, strlen(filename)) == 0)) {
num++;
}
}
/* delete the oldest files (assumes they are ordered by their creation date) */
while(num > generations) {
char old[QS_HUGE_STR];
old[0] = '\0';
rewinddir(dir);
while((de = readdir(dir)) != 0) {
if(de->d_name && (strncmp(de->d_name, filename, strlen(filename)) == 0)) {
if(strcmp(old, de->d_name) > 0) {
snprintf(old, sizeof(old), "%s", de->d_name);
} else {
if(old[0] == '\0') {
snprintf(old, sizeof(old), "%s", de->d_name);
}
}
}
}
{
/* build abs path and delete it */
char unl[QS_HUGE_STR];
snprintf(unl, sizeof(unl), "%s/%s", dirname, old);
unlink(unl);
}
num--;
}
closedir(dir);
}
}
/* user ------------------------------------------------------- */
void qs_setuid(const char *username, const char *cmd) {
if(username && getuid() == 0) {
struct passwd *pwd = getpwnam(username);
uid_t uid, gid;
if(pwd == NULL) {
fprintf(stderr, "[%s] failed to switch user: unknown user id '%s'\n", cmd, username);
exit(1);
}
uid = pwd->pw_uid;
gid = pwd->pw_gid;
setgid(gid);
setuid(uid);
if(getuid() != uid) {
fprintf(stderr, "[%s] setuid failed (%s,%d)\n", cmd, username, uid);
exit(1);
}
if(getgid() != gid) {
fprintf(stderr, "[%s] setgid failed (%d)\n", cmd, gid);
exit(1);
}
}
}
/* pcre ------------------------------------------------------- */
int qs_pregfree(void *p) {
qs_regfree((qs_regex_t *)p);
return 0;
}
void qs_regfree(qs_regex_t *preg) {
if(preg->state == 1) {
pcre2_code_free(preg->re_pcre);
}
}
int qs_regcomp(qs_regex_t *preg, const char *pattern, int cflags) {
unsigned int capcount;
size_t erroffset;
int errcode = 0;
int options = cflags;
preg->state = 0;
preg->re_pcre = pcre2_compile((const unsigned char *)pattern,
PCRE2_ZERO_TERMINATED, options, &errcode,
&erroffset, NULL);
if (preg->re_pcre == NULL) {
return 1;
}
pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
PCRE2_INFO_CAPTURECOUNT, &capcount);
preg->re_nsub = capcount;
preg->state = 1;
return 0;
}
int qs_regexec_len(const qs_regex_t *preg, const char *buff,
unsigned int len, unsigned int nmatch,
qs_regmatch_t *pmatch, int eflags) {
int rc;
int options = 0;
match_vector_pt ovector = NULL;
unsigned int ncaps = (unsigned int)preg->re_nsub + 1;
match_data_pt data = pcre2_match_data_create(ncaps, NULL);
if (!data) {
return -1;
}
options = eflags;
rc = pcre2_match((const pcre2_code *)preg->re_pcre,
(const unsigned char *)buff, len,
0, options, data, NULL);
ovector = pcre2_get_ovector_pointer(data);
if (rc >= 0) {
unsigned int n = rc, i;
if (n == 0 || n > nmatch)
rc = n = nmatch; /* All capture slots were filled in */
for (i = 0; i < n; i++) {
pmatch[i].rm_so = ovector[i * 2];
pmatch[i].rm_eo = ovector[i * 2 + 1];
}
for (; i < nmatch; i++) {
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
}
pcre2_match_data_free(data);
return rc;
}
else {
pcre2_match_data_free(data);
return -1;
}
}

98
tools/src/qs_util.h Normal file
View file

@ -0,0 +1,98 @@
/**
* Utilities for the quality of service module mod_qos.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef QS_UTIL_H
#define QS_UTIL_H
/* ----------------------------------
* version info
* ---------------------------------- */
static const char man_version[] = "11.74";
static const char man_date[] = "May 2023";
/* ----------------------------------
* definitions
* ---------------------------------- */
/* huge (128kb) buffer supporting very long lines (twice as
much as Apache's rotatelogs uses */
#define MAX_LINE_BUFFER 131072
/* smaller buffer, e.g. for qslog */
#define MAX_LINE 32768
#define QS_HUGE_STR 2048
#define CR 13
#define LF 10
/* ----------------------------------
* functions
* ---------------------------------- */
char *qs_CMD(const char *cmd);
void qs_man_print(int man, const char *fmt, ...);
void qs_man_println(int man, const char *fmt, ...);
/* io */
int qs_getLine(char *s, int n);
int qs_getLinef(char *s, int n, FILE *f);
/* time */
void qs_time(time_t *tme);
void qs_set2OfflineMode();
void qs_setTime(time_t tme);
/* synchronisation */
void qs_csInitLock();
void qs_csLock();
void qs_csUnLock();
/* log */
void qs_deleteOldFiles(const char *file_name, int generations);
/* user */
void qs_setuid(const char *username, const char *cmd);
/* pcre */
#define QS_MAX_REG_MATCH 10
typedef struct {
int rm_so;
int rm_eo;
} qs_regmatch_t;
typedef struct {
void *re_pcre;
int re_nsub;
int state;
} qs_regex_t;
int qs_pregfree(void *p);
void qs_regfree(qs_regex_t *preg);
int qs_regcomp(qs_regex_t *preg, const char *regex, int cflags);
int qs_regexec_len(const qs_regex_t *preg, const char *buff,
unsigned int len, unsigned int nmatch,
qs_regmatch_t *pmatch, int eflags);
#endif

413
tools/src/qscheck.c Normal file
View file

@ -0,0 +1,413 @@
/**
* Utilities for the quality of service module mod_qos.
*
* qscheck.c: Monitor testing tcp connectivity to servers used by mod_proxy.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qscheck.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <arpa/inet.h>
#include "qs_util.h"
//#include <config.h>
#define CR 13
#define LF 10
#define QS_TIMEOUT 2
#define QS_PROXYP "proxypass "
#define QS_PROXYP_TAB "proxypass\t"
#define QS_PROXYPR "proxypassreverse "
#define QS_PROXYPR_TAB "proxypassreverse\t"
#define QS_PROXYR "proxyremote "
#define QS_PROXYR_TAB "proxyremote\t"
#define QS_INCLUDE "nclude "
#define QS_INCLUDE_TAB "nclude\t"
#define QS_SERVERROOT "ServerRoot "
#define QS_SERVERROOT_TAB "ServerRoot\t"
static int m_verbose = 0;
static char ServerRoot[1024];
static char *checkedHosts = NULL;
/**
* Prints usage text
*/
static void usage(char *cmd) {
printf("\n");
printf("Monitor program testing the TCP connectivity to servers.\n");
printf("\n");
printf("Usage: %s -c <httpd.conf> [-v]\n", cmd);
printf("\n");
printf("Verifies the connectivity to the server referred either\n");
printf("by the ProxyPass, ProxyPassReverse, or ProxyReverse\n");
printf("directive used by mod_proxy.\n");
printf("\n");
printf("You may alternatively use \"%s -i <hostname>:<port>\" if\n", cmd);
printf("you want to check the TCP connectivity to a single host.\n");
printf("\n");
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
exit(1);
}
/**
* Opens a tcp connection
*/
static int ping(unsigned long address, int port) {
int status = 0;
struct sockaddr_in addr;
int skt;
addr.sin_addr.s_addr = address;
addr.sin_port = htons(port);
addr.sin_family = PF_INET;
skt = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(skt != -1) {
int sflags = fcntl(skt,F_GETFL,0);
if(sflags >=0) {
/* set non blocking socket */
if(fcntl(skt,F_SETFL,sflags|O_NONBLOCK) >=0) {
/* this connect returns immediately */
int ret = connect(skt, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
if(fcntl(skt,F_SETFL,sflags) >=0) {
socklen_t lon = sizeof(int);
int valopt;
fd_set fd_w;
struct timeval tme;
tme.tv_sec = QS_TIMEOUT;
tme.tv_usec = 0;
FD_ZERO(&fd_w);
FD_SET(skt, &fd_w);
/* select returns -1 on timeout, else 1 (connected or refused) */
if(select(FD_SETSIZE, NULL, &fd_w, NULL, &tme) > 0) {
/* check the status of the socket in order to distinguish between
connected or refused */
if(getsockopt(skt, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) >= 0) {
if(!valopt) {
/* UP ! */
status = 1;
}
}
}
}
}
}
}
return status;
}
/**
* resolves host address
*/
static unsigned long getAddress(const char *hostname) {
int ip = 1;
int i = 0;
unsigned long address = 0L;
struct hostent *hoste;
for(i = 0; i < (int) strlen(hostname); i++) {
if((!isdigit((int) hostname[i])) && (hostname[i] != '.')) {
ip = 0;
break;
}
}
if (ip) {
address = inet_addr(hostname);
if(address == -1) {
return 0L;
}
} else {
hoste = gethostbyname(hostname);
if (!hoste || !hoste->h_addr_list[ 0 ]) {
/* can't resolve host name */
return 0L;
}
address = ((struct in_addr*)hoste->h_addr_list[ 0 ])->s_addr;
}
return address;
}
/*
* Checks a single host (parse host string, resolve address, ping).
*/
static int checkHost(const char *cmd, const char *filename, int ln, char *abs_url) {
int status = 1;
char *schema = abs_url;
char *host = NULL;
char *ports = NULL;
int port = 0;
char hp[1024];
unsigned long address;
char *x = strstr(abs_url, "://");
if(x == NULL) {
if(m_verbose) {
fprintf(stderr,"[%s]: ERROR, wrong syntax <%s> in %s on line %d\n",
cmd, abs_url, filename, ln);
}
return 0;
}
x[0] = '\0'; x = x + strlen("://");
host = x;
ports = strchr(x, ':');
if(ports != NULL) {
ports[0] = '\0'; ports++;
x = strchr(ports, '/');
if(x == NULL) {
int i;
x = ports;
for(i=0;(x[i] != ' ') && (x[i] != '\t') && (x[i] != '\0'); i++);
x[i] = '\0';
} else {
x[0] = '\0';
}
port = atoi(ports);
} else {
ports = strchr(x, '/');
if(ports == NULL) {
int i;
for(i=0;(x[i] != ' ') && (x[i] != '\t') && (x[i] != '\0'); i++);
x[i] = '\0';
} else {
ports[0] = '\0';
}
if(strcmp(schema, "http") == 0) {
port = 80;
} else {
port = 443;
}
}
/* check each host only once */
snprintf(hp, sizeof(hp), "#%s:%d#", host, port);
if(checkedHosts && strstr(checkedHosts, hp) != NULL) {
/* already checked */
return 1;
}
if(checkedHosts == NULL) {
checkedHosts = calloc(1, strlen(hp) + 1);
strcpy(checkedHosts, hp);
} else {
int pl = strlen(checkedHosts) +strlen(hp) + 1;
char *p = calloc(1, pl);
snprintf(p, pl, "%s%s", checkedHosts, hp);
free(checkedHosts);
checkedHosts = p;
}
/* resolve address */
address = getAddress(host);
if(address == 0L) {
fprintf(stderr,"[%s]: ERROR, could not resolve hostname %s\n", cmd, host);
return -1;
}
/* check connection */
if(ping(address, port)) {
if(m_verbose) {
printf("[%s]: %s:%d Up\n", cmd, host, port);
}
return 1;
} else {
printf("[%s]: %s:%d Down\n", cmd, host, port);
return 0;
}
}
/**
* Open file and check every ProxyPass* or ProxyR* entry.
* - follows include ... directive
* - determines serverroot
*/
static int checkFile(const char *cmd, const char *filename) {
int status = 1;
int ln = 0;
char line[1024];
FILE *f = fopen(filename, "r");
if(f == NULL) {
if(ServerRoot[0] != '\0') {
char fqfile[2048];
snprintf(fqfile, sizeof(fqfile), "%s/%s", ServerRoot, filename);
f = fopen(fqfile, "r");
}
}
if(f == NULL) {
fprintf(stderr,"[%s]: ERROR, could not open file %s\n", cmd, filename);
return 0;
}
while(!qs_getLinef(line, sizeof(line), f)) {
char *command = NULL;
int cmd_len = 0;
int to = 0;
while(line[to]) {
line[to] = tolower(line[to]);
to++;
}
ln++;
command = strstr(line, QS_PROXYP);
cmd_len = strlen(QS_PROXYP);
if(command == NULL) command = strstr(line, QS_PROXYP_TAB);
if(command == NULL) {
command = strstr(line, QS_PROXYPR);
cmd_len = strlen(QS_PROXYPR);
}
if(command == NULL) command = strstr(line, QS_PROXYPR_TAB);
if(command == NULL) {
command = strstr(line, QS_PROXYR);
cmd_len = strlen(QS_PROXYR);
}
if(command == NULL) command = strstr(line, QS_PROXYR_TAB);
if(command && strchr(line, '#') == 0) {
/* command = cmd url schema://host[:port]/url */
char *abs_url = &command[cmd_len];
int i, j;
/* get the url */
for(i=0;(abs_url[i] == ' ') || (abs_url[i] == '\t'); i++);
abs_url = &abs_url[i];
/* skip url */
for(i=0;(abs_url[i] != ' ') && (abs_url[i] != '\t') && (abs_url[i] != '\0'); i++);
abs_url = &abs_url[i];
/* get schema://host[:port]/url */
for(i=0;(abs_url[i] == ' ') || (abs_url[i] == '\t'); i++);
abs_url = &abs_url[i];
/* ping */
if(abs_url && abs_url[0] != '\0' && abs_url[0] != '!') {
status = status & checkHost(cmd, filename, ln, abs_url);
}
} else {
/* include commands */
command = strstr(line, QS_INCLUDE);
if(command == NULL) command = strstr(line, QS_INCLUDE_TAB);
if(command && strchr(line, '#') == 0) {
char *file = &command[strlen(QS_INCLUDE)];
int i, j;
/* get the value */
for(i=0;(file[i] == ' ') || (file[i] == '\t'); i++);
/* delete spaces at the end of the value */
if(&file[i] != '\0') {
for(j=i+1;(file[j] != ' ') && (file[j] != '\t') && (file[j] != '\0'); j++);
file[j] = '\0';
}
file = &file[i];
status = status & checkFile(cmd, file);
} else {
/* server root */
command = strstr(line, QS_SERVERROOT);
if(command == NULL) command = strstr(line, QS_SERVERROOT_TAB);
if(command && strchr(line, '#') == 0) {
char *sr = &command[strlen(QS_SERVERROOT)];
int i, j;
/* get the value */
for(i=0;(sr[i] == ' ') || (sr[i] == '\t'); i++);
/* delete spaces at the end of the value */
if(&sr[i] != '\0') {
for(j=i+1;(sr[j] != ' ') && (sr[j] != '\t') && (sr[j] != '\0'); j++);
sr[j] = '\0';
}
strcpy(ServerRoot, &sr[i]);
}
}
}
}
fclose(f);
return status;
}
int main(int argc, char **argv) {
char *config = NULL;
char *cmd = strrchr(argv[0], '/');
char *single = NULL;
int status = 1;
if(cmd == NULL) {
cmd = argv[0];
} else {
cmd++;
}
ServerRoot[0] = '\0';
while(argc >= 1) {
if(strcmp(*argv,"-c") == 0) {
if (--argc >= 1) {
config = *(++argv);
}
} else if(strcmp(*argv,"-i") == 0) {
if (--argc >= 1) {
single = *(++argv);
}
} else if(strcmp(*argv,"-v") == 0) {
m_verbose = 1;
}
argc--;
argv++;
}
if(single) {
char *hostName = single;
char *portNumber = strchr(single, ':');
if(portNumber) {
unsigned long addr;
int prt;
portNumber[0] = '\0';
portNumber++;
addr = getAddress(hostName);
prt = atoi(portNumber);
if(addr && prt) {
if(ping(addr, prt)) {
if(m_verbose) {
printf("[%s]: %s:%d Up\n", cmd, hostName, prt);
}
status = 1;
} else {
printf("[%s]: %s:%d Down\n", cmd, hostName, prt);
status = 0;
}
} else {
// could not resolve
fprintf(stderr,"[%s]: ERROR, unknown host/port\n", cmd);
status = 0;
}
} else {
// invalid input
fprintf(stderr,"[%s]: ERROR, invalid format\n", cmd);
status = 0;
}
} else {
if(config == NULL) {
usage(cmd);
}
status = checkFile(cmd, config);
}
if(status == 0) {
fprintf(stderr,"[%s]: ERROR, check failed\n", cmd);
exit(1);
}
printf("[%s]: OK, check successful\n", cmd);
return 0;
}

336
tools/src/qsdt.c Normal file
View file

@ -0,0 +1,336 @@
/**
* Utility for the quality of service module mod_qos.
*
* qsdt.c: simple tool to measure the elapse time between
* related log messages
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <regex.h>
#include <apr.h>
#include <apr_portable.h>
#include <apr_strings.h>
#include "qs_util.h"
#define MAX_REG_MATCH 10
#define TIMESTR "%H:%M:%S"
#define TIMEEX "([0-9]{2}:[0-9]{2}:[0-9]{2})[.,]([0-9]{3})"
typedef struct {
time_t seconds;
int milliseconds;
char *id;
} entry_t;
static void usage(const char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s calculates the elapsed time between two related log messages.\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s [-t <regex>] -i <regex> -s <regex> -e <regex> [-v] [<path>]\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "%s is a simple tool to search two different messages\n", cmd);
qs_man_print(man, "in a log file and calculates the elapsed time between these\n");
qs_man_print(man, "lines. The two log messages need a common identifier such an\n");
qs_man_print(man, "unique request id (UNIQUE_ID), a thread id, or a transaction\n");
qs_man_print(man, "code.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " -t <regex>\n");
if(man) printf("\n");
qs_man_print(man, " Defines a pattern (regular expression) matching the log line's\n");
qs_man_print(man, " timestamp. The pattern must include two sub-expressions, one matching\n");
qs_man_print(man, " hours, minutes and seconds the other matching the milliseconds.\n");
qs_man_print(man, " Default pattern is "TIMEEX"\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -i <regex>\n");
if(man) printf("\n");
qs_man_print(man, " Pattern (regular expression) matching the identifier which the two\n");
qs_man_print(man, " messages have in common. The sub-expression defines the part which\n");
qs_man_print(man, " needs to be extracted from the matching string. Note: You can also\n");
qs_man_print(man, " use the start (-s) and end (-e) pattern to define the sub-expression\n");
qs_man_print(man, " matching this identifier.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -s <regex>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the pattern (regular expression or literal string)\n");
qs_man_print(man, " identifying the first (start) of the two messages.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -e <regex>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the pattern (regular expression or literal string)\n");
qs_man_print(man, " identifying the second (end) of the two messages.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -v\n");
if(man) printf("\n");
qs_man_print(man, " Verbose mode.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " <path>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the input file to process. %s reads from\n", cmd);
qs_man_print(man, " from standard input if this parameter is omitted.\n");
printf("\n");
if(man) {
printf(".SH EXAMPLE\n");
printf("Sample command line arguments:\n");
printf("\n");
} else {
printf(" Sample arguments:\n");
}
qs_man_println(man, " -i ' ([a-z0-9]+) [A-Z]+ ' -s 'Received Request' -e 'Received Response'\n");
printf("\n");
qs_man_println(man, " matching those sample log messages:\n");
qs_man_println(man, " 2018-03-12 16:34:08.653 threadid23 INFO Received Request\n");
qs_man_println(man, " 2018-03-13 16:35:09.891 threadid23 DEBUG MessageHandler Received Response\n");
printf("\n");
if(man) {
printf(".SH NOTE\n");
} else {
printf("Notes:\n");
}
qs_man_println(man, "The four patterns (t,i,s,e) are concatenated into two search patterns:\n");
qs_man_println(man, " first (start): [t (HH:MM:SS)(SSS) ].*[i (id) ].*[s ]\n");
qs_man_println(man, " second (end): [t (HH:MM:SS)(SSS) ].*[i (id) ].*[e ]\n");
printf("\n");
qs_man_print(man, "And the three sub-expression are used to extract the timestamp and the\n");
qs_man_print(man, "unique identifier that the start and end message have in common.\n");
qs_man_print(man, "This means that you could specify the sub-expression for the unique\n");
qs_man_print(man, "identifier in the start (-s) or end (-e) pattern alternatively, e.g. in\n");
qs_man_print(man, "case the identifier is at the end of the log line.\n");
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
int main(int argc, const char *const argv[]) {
FILE *file;
char line[MAX_LINE];
int verbose = 0;
const char *cmd = strrchr(argv[0], '/');
apr_pool_t *pool;
apr_table_t *inmsg;
regmatch_t ma[MAX_REG_MATCH];
regex_t pregstart;
regex_t pregend;
const char *timeex = TIMEEX;
const char *idex = NULL;
const char *startex = NULL;
const char *endex = NULL;
const char *filename = NULL;
char *regexStr;
apr_app_initialize(&argc, &argv, NULL);
apr_pool_create(&pool, NULL);
inmsg = apr_table_make(pool, 100);
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv,"-t") == 0) {
if (--argc >= 1) {
timeex = *(++argv);
}
} else if(strcmp(*argv,"-i") == 0) {
if (--argc >= 1) {
idex = *(++argv);
}
} else if(strcmp(*argv,"-s") == 0) {
if (--argc >= 1) {
startex = *(++argv);
}
} else if(strcmp(*argv,"-e") == 0) {
if (--argc >= 1) {
endex = *(++argv);
}
} else if(strcmp(*argv,"-v") == 0) {
verbose = 1;
} else if(strcmp(*argv,"-h") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
} else {
filename = *argv;
}
argc--;
argv++;
}
if(idex == NULL || startex == NULL || endex == NULL) {
usage(cmd, 0);
}
if(filename) {
file = fopen(filename, "r");
if(!file) {
fprintf(stderr, "ERROR, failed to open the log file '%s'\n", filename);
exit(1);
}
} else {
file = stdin;
}
regexStr = apr_psprintf(pool, "%s.*%s.*%s", timeex, idex, startex);
if(verbose) {
fprintf(stderr, "start pattern: %s\n", regexStr);
}
if(regcomp(&pregstart, regexStr, REG_EXTENDED)) {
fprintf(stderr, "ERROR, could not compile %s\n", regexStr);
exit(1);
};
regexStr = apr_psprintf(pool, "%s.*%s.*%s", timeex, idex, endex);
if(verbose) {
fprintf(stderr, "end pattern: %s\n", regexStr);
}
if(regcomp(&pregend, regexStr, REG_EXTENDED)) {
fprintf(stderr, "ERROR, could not compile %s\n", regexStr);
exit(1);
};
while(fgets(line, MAX_LINE-1, file) != NULL) {
char *hms;
char *ms;
char *id;
if(regexec(&pregstart, line, MAX_REG_MATCH, ma, 0) == 0) {
entry_t *entry = calloc(1, sizeof(entry_t));
struct tm tm;
if(ma[3].rm_so == -1) {
fprintf(stderr, "ERROR, invalid regular expression (missing sub-expression in pattern)\n");
exit(1);
}
hms = &line[ma[1].rm_so];
ms = &line[ma[2].rm_so];
id = &line[ma[3].rm_so];
line[ma[1].rm_eo] = '\0';
line[ma[2].rm_eo] = '\0';
line[ma[3].rm_eo] = '\0';
strptime(hms, TIMESTR, &tm);
entry->seconds = mktime(&tm);
entry->milliseconds = atoi(ms);
entry->id = calloc(strlen(id)+1, sizeof(char));
sprintf(entry->id, "%s", id);
if(verbose) {
fprintf(stderr, "START [%s][%s][%s] %lu %d\n",
hms, ms, id, entry->seconds, entry->milliseconds);
}
apr_table_setn(inmsg, entry->id, (char *)entry);
} else if(regexec(&pregend, line, MAX_REG_MATCH, ma, 0) == 0) {
entry_t entry;
entry_t *start;
struct tm tm;
if(ma[3].rm_so == -1) {
fprintf(stderr, "ERROR, invalid regular expression (missing sub-expression in pattern)\n");
exit(1);
}
hms = &line[ma[1].rm_so];
ms = &line[ma[2].rm_so];
id = &line[ma[3].rm_so];
line[ma[1].rm_eo] = '\0';
line[ma[2].rm_eo] = '\0';
line[ma[3].rm_eo] = '\0';
strptime(hms, TIMESTR, &tm);
entry.seconds = mktime(&tm);
entry.milliseconds = atoi(ms);
if(verbose) {
fprintf(stderr, "END [%s][%s][%s] %lu %d\n",
hms, ms, id, entry.seconds, entry.milliseconds);
}
start = (entry_t *)apr_table_get(inmsg, id);
if(start) {
printf("@%s %s %10lu [ms]\n",
line,
id,
(entry.seconds-start->seconds)*1000 + entry.milliseconds-start->milliseconds);
apr_table_unset(inmsg, id);
free(start->id);
free(start);
}
}
}
fclose(file);
return 0;
}

376
tools/src/qsexec.c Normal file
View file

@ -0,0 +1,376 @@
/* -*-mode: c; indent-tabs-mode: nil; c-basic-offset: 2; -*-
*/
/**
* Command line execution utility for the quality of service module mod_qos.
*
* See http://mod-qos.sourceforge.net/ for further details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qsexec.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
/* system */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
/* apr */
#include <apr.h>
#include <apr_strings.h>
#include <apr_file_io.h>
#include <apr_time.h>
#include <apr_getopt.h>
#include <apr_general.h>
#include <apr_lib.h>
#include <apr_portable.h>
#include <apr_support.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "qs_util.h"
#ifndef POSIX_MALLOC_THRESHOLD
#define POSIX_MALLOC_THRESHOLD (10)
#endif
/* same as APR_SIZE_MAX which doesn't appear until APR 1.3 */
#define QSUTIL_SIZE_MAX (~((apr_size_t)0))
typedef struct {
int rm_so;
int rm_eo;
} regmatch_t;
static void usage(char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\n", qs_CMD(cmd), man_date, man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
printf("%s %s- parses the data received via stdin and executes the defined command on a pattern match.\n",
cmd, man ? "\\" : "");
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s -e <pattern> [-t <number>:<sec>] [-c <pattern> [<command string>]]\n", man ? "" : "Usage: ", cmd);
qs_man_print(man, " [-p] [-u <user>] <command string>\n");
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "%s reads log lines from stdin and searches for the defined pattern.\n", cmd);
qs_man_print(man, "It executes the defined command string on pattern match.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " -e <pattern>\n");
if(man) printf("\n");
qs_man_print(man, " Specifies the search pattern causing an event which shall trigger the\n");
qs_man_print(man, " command.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -t <number>:<sec>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the number of pattern match within the the defined number of\n");
qs_man_print(man, " seconds in order to trigger the command execution. By default, every\n");
qs_man_print(man, " pattern match causes a command execution.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -c <pattern> [<command string>]\n");
if(man) printf("\n");
qs_man_print(man, " Pattern which clears the event counter. Executes optionally a command\n");
qs_man_print(man, " if an event command has been executed before.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -p\n");
if(man) printf("\n");
qs_man_print(man, " Writes data also to stdout (for piped logging).\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -u <name>\n");
if(man) printf("\n");
qs_man_print(man, " Become another user, e.g. www-data.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " <command string>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the event command string where $0-$9 are substituted by the\n");
qs_man_print(man, " submatches of the regular expression.\n");
printf("\n");
if(man) {
printf(".SH EXAMPLE\n");
} else {
printf("Example:\n");
}
qs_man_print(man, "Executes the deny.sh script providing the IP address of the\n");
qs_man_print(man, "client causing a mod_qos(031) messages whenever the log message\n");
qs_man_print(man, "appears 10 times within at most one minute:\n");
if(man) printf("\n");
qs_man_println(man, " ErrorLog \"|/usr/bin/%s -e \\'mod_qos\\(031\\).*, c=([0-9a-zA-Z:.]*)\\' -t 10:60 \\'/usr/local/bin/deny.sh $1\\'\"\n", cmd);
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
/*
* Substitutes for $0-$9 within the matching string.
* See ap_pregsub().
*/
char *qs_pregsub(apr_pool_t *pool, const char *input,
const char *source, size_t nmatch,
qs_regmatch_t pmatch[]) {
const char *src = input;
char *dest, *dst;
char c;
size_t no;
int len;
if(!source) {
return NULL;
}
if(!nmatch) {
return apr_pstrdup(pool, src);
}
/* First pass, find the size */
len = 0;
while((c = *src++) != '\0') {
if(c == '&')
no = 0;
else if (c == '$' && apr_isdigit(*src))
no = *src++ - '0';
else
no = 10;
if (no > 9) { /* Ordinary character. */
if (c == '\\' && (*src == '$' || *src == '&'))
src++;
len++;
}
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
if(QSUTIL_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so) {
fprintf(stderr, "ERROR, integer overflow or out of memory condition");
return NULL;
}
len += pmatch[no].rm_eo - pmatch[no].rm_so;
}
}
dest = dst = apr_pcalloc(pool, len + 1);
/* Now actually fill in the string */
src = input;
while ((c = *src++) != '\0') {
if (c == '&')
no = 0;
else if (c == '$' && apr_isdigit(*src))
no = *src++ - '0';
else
no = 10;
if (no > 9) { /* Ordinary character. */
if (c == '\\' && (*src == '$' || *src == '&'))
c = *src++;
*dst++ = c;
}
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
len = pmatch[no].rm_eo - pmatch[no].rm_so;
memcpy(dst, source + pmatch[no].rm_so, len);
dst += len;
}
}
*dst = '\0';
return dest;
}
int main(int argc, const char * const argv[]) {
const char *username = NULL;
int nr = 0;
char *line = calloc(1, MAX_LINE_BUFFER+1);
apr_pool_t *pool;
char *cmd = strrchr(argv[0], '/');
const char *command = NULL;
const char *pattern = NULL;
const char *clearcommand = NULL;
const char *clearpattern = NULL;
int executed = 0;
qs_regex_t *preg;
qs_regex_t *clearpreg;
qs_regmatch_t regm[QS_MAX_REG_MATCH];
time_t sec = 0;
int threshold = 0;
int counter = 0;
time_t countertime;
static int pass = 0;
apr_app_initialize(&argc, &argv, NULL);
apr_pool_create(&pool, NULL);
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv,"-e") == 0) {
if (--argc >= 1) {
pattern = *(++argv);
}
} else if(strcmp(*argv,"-u") == 0) {
if (--argc >= 1) {
username = *(++argv);
}
} else if(strcmp(*argv,"-c") == 0) {
if (--argc >= 1) {
clearpattern = *(++argv);
if (argc >=1 && *argv[0] != '-') {
clearcommand = *(++argv);
argc--;
}
}
} else if(argc >= 1 && strcmp(*argv,"-t") == 0) {
if (--argc >= 1) {
char *str = apr_pstrdup(pool, *(++argv));
char *tme = strchr(str, ':');
if(tme == NULL) {
fprintf(stderr,"[%s]: ERROR, invalid number:sec format\n", cmd);
exit(1);
}
tme[0] = '\0';
tme++;
threshold = atoi(str);
sec = atol(tme);
if(threshold == 0 || sec == 0) {
fprintf(stderr,"[%s]: ERROR, invalid number:sec format\n", cmd);
exit(1);
}
}
} else if(argc >= 1 && strcmp(*argv,"-p") == 0) {
pass = 1;
} else if(strcmp(*argv,"-h") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
} else {
command = *argv;
}
argc--;
argv++;
}
if(pattern == NULL || command == NULL) {
usage(cmd, 0);
}
qs_setuid(username, cmd);
preg = apr_palloc(pool, sizeof(qs_regex_t));
if(qs_regcomp(preg, pattern, PCRE2_DOTALL) != 0) {
fprintf(stderr, "ERROR, could not compile '%s'\n", pattern);
exit(1);
}
apr_pool_pre_cleanup_register(pool, preg, qs_pregfree);
if(clearpattern) {
clearpreg = apr_palloc(pool, sizeof(qs_regex_t));
if(qs_regcomp(clearpreg, clearpattern, PCRE2_DOTALL) != 0) {
fprintf(stderr, "ERROR, could not compile '%s'\n", clearpattern);
exit(1);
}
apr_pool_pre_cleanup_register(pool, clearpattern, qs_pregfree);
}
while(fgets(line, MAX_LINE_BUFFER, stdin) != NULL) {
size_t len;
nr++;
if(pass) {
printf("%s", line);
fflush(stdout);
}
len = strlen(line);
if(clearpattern && (qs_regexec_len(clearpreg, line, len, QS_MAX_REG_MATCH, regm, 0) >= 0)) {
apr_pool_t *subpool;
apr_pool_create(&subpool, pool);
counter = 0;
countertime = 0;
if(clearcommand && executed) {
char *replaced = qs_pregsub(subpool, clearcommand, line, QS_MAX_REG_MATCH, regm);
if(!replaced) {
fprintf(stderr, "[%s]: ERROR, failed to substitute"
" submatches '%s' in (%s)\n", cmd, clearcommand, line);
} else {
int rc = system(replaced);
}
executed = 0;
}
apr_pool_destroy(subpool);
} else if(qs_regexec_len(preg, line, len, QS_MAX_REG_MATCH, regm, 0) >= 0) {
apr_pool_t *subpool;
char *replaced;
apr_pool_create(&subpool, pool);
replaced = qs_pregsub(subpool, command, line, QS_MAX_REG_MATCH, regm);
if(!replaced) {
fprintf(stderr, "[%s]: ERROR, failed to substitute"
" submatches '%s' in (%s)\n", cmd, command, line);
} else {
counter++;
if(counter == 1) {
countertime = time(NULL);
}
if(counter >= threshold) {
if(countertime + sec >= time(NULL)) {
int rc = system(replaced);
executed = 1;
}
countertime = 0;
counter = 0;
}
}
apr_pool_destroy(subpool);
}
}
apr_pool_destroy(pool);
return 0;
}

1826
tools/src/qsfilter2.c Normal file

File diff suppressed because it is too large Load diff

607
tools/src/qsgeo.c Normal file
View file

@ -0,0 +1,607 @@
/* -*-mode: c; indent-tabs-mode: nil; c-basic-offset: 2; -*-
*/
/**
* Utilities for the quality of service module mod_qos.
*
* qsgeo.c: resolves the country codes of IP addresses
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qsgeo.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <regex.h>
/* apr */
#include <apr.h>
#include <apr_strings.h>
#include <apr_file_io.h>
#include <apr_time.h>
#include <apr_lib.h>
#include <apr_portable.h>
#include <apr_support.h>
#include <apr_base64.h>
#include "qs_util.h"
#define MAX_REG_MATCH 10
// "3758096128","3758096383","AU"
#define QS_GEO_PATTERN "\"([0-9]+)\",\"([0-9]+)\",\"([A-Z0-9]{2}|-)\""
// "3758096128","3758096383","AU","Australia"
#define QS_GEO_PATTERN_D "\"([0-9]+)\",\"([0-9]+)\",\"([A-Z0-9]{2})\",\"(.*)\""
// "192.83.198.0","192.83.198.255","3226715648","3226715903","AU","Australia"
#define QS_GEO_PATTERN_EXT "\"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\",\"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\",\"([0-9]+)\",\"([0-9]+)\",\"([A-Z0-9]{2})\""
// 182.12.34.23
#define IPPATTERN "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})[\"'\x0d\x0a, ]+"
#define IPPATTERN2 "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})[\"'\x0d\x0a,; ]+"
static int m_inject = 0;
static int m_verbose = 0;
typedef struct {
unsigned long start;
char *c;
} qos_inj_t;
static const qos_inj_t m_inj[] = {
{ 167772160, "\"10.0.0.0\",\"10.255.255.255\",\"167772160\",\"184549375\",\"PV\",\"private network\"" },
{ 2130706432, "\"127.0.0.0\",\"127.255.255.255\",\"2130706432\",\"2147483647\",\"LO\",\"local loopback\"" },
{ 2886729728, "\"172.16.0.0\",\"172.31.255.255\",\"2886729728\",\"2887778303\",\"PV\",\"private network\"" },
{ 3232235520, "\"192.168.0.0\",\"192.168.255.255\",\"3232235520\",\"3232301055\",\"PV\",\"private network\"" },
{ 0, NULL }
};
typedef struct {
unsigned long start;
unsigned long end;
char country[3];
char c[500];
} qos_geo_t;
typedef struct {
int num;
char *c;
} qos_geo_stat_t;
static int qos_is_num(const char *num) {
int i = 0;
while(num[i]) {
if(!isdigit(num[i])) {
return 0;
}
i++;
}
return 1;
}
/**
* Converts an IPv4 address string to it's numeric value.
* w.x.y.z results in 16777216*w + 65536*x + 256*y + z
*
* @param pool To make a copy of the address to parse
* @param ip
* @return The address or 0 on error
*/
static unsigned long qos_geo_str2long(apr_pool_t *pool, const char *ip) {
char *p;
char *i = apr_pstrdup(pool, ip);
unsigned long addr = 0;
p = strchr(i, '.');
if(!p) return 0;
p[0] = '\0';
if(!qos_is_num(i)) return 0;
addr += (atol(i) * 16777216);
i = p;
i++;
p = strchr(i, '.');
if(!p) return 0;
p[0] = '\0';
if(!qos_is_num(i)) return 0;
addr += (atol(i) * 65536);
i = p;
i++;
p = strchr(i, '.');
if(!p) return 0;
p[0] = '\0';
if(!qos_is_num(i)) return 0;
addr += (atol(i) * 256);
i = p;
i++;
if(!qos_is_num(i)) return 0;
addr += (atol(i));
return addr;
}
static void qos_geo_long2str(char *buf, unsigned long ip) {
int a,b,c,d;
a = ip % 256;
ip = ip / 256;
b = ip % 256;
ip = ip / 256;
c = ip % 256;
ip = ip / 256;
d = ip % 256;
sprintf(buf, "%d.%d.%d.%d", d, c, b, a);
}
/**
* Usage message (text or manpage format).
*/
static void usage(const char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s - an utility to lookup a client's country code.\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s -d <path> [-l] [-s] [-ip <ip>]\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "Use this utility to resolve the country codes of IP addresses\n");
qs_man_print(man, "within existing log files. The utility reads the log file data\n");
qs_man_print(man, "from stdin and writes them, with the injected country code, to\n");
qs_man_print(man, "stdout.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf("\n.TP\n");
qs_man_print(man, " -d <path>\n");
if(man) printf("\n");
qs_man_print(man, " Specifies the path to the geographical database files (CSV\n");
qs_man_print(man, " file containing IP address ranges and country codes).\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -s\n");
if(man) printf("\n");
qs_man_print(man, " Writes a summary of the requests per country only.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -l\n");
if(man) printf("\n");
qs_man_print(man, " Writes the database to stdout (ignoring stdin) inserting\n");
qs_man_print(man, " local (127.*) and private (10.*, 172.16*, 192.168.*)\n");
qs_man_print(man, " network addresses.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -ip <ip>\n");
if(man) printf("\n");
qs_man_print(man, " Resolves a single IP address instead of processing a log file.\n");
printf("\n");
if(man) {
printf(".SH EXAMPLE\n");
printf("Reading the file access.log and adding the country code to the IP address field:\n");
printf("\n");
} else {
printf("Example reading the file access.log and adding the country code to\n");
printf("the IP address field:\n");
}
qs_man_println(man, " cat access.log | %s -d GeoIPCountryWhois.csv\n", cmd);
printf("\n");
if(man) {
printf("Reading the file access.log and showing a summary only:\n");
printf("\n");
} else {
printf("Example reading the file access.log and showing a summary only:\n");
}
qs_man_println(man, " cat access.log | %s -d GeoIPCountryWhois.csv -s\n", cmd);
printf("\n");
if(man) {
printf("Resolving a single IP address:\n");
printf("\n");
} else {
printf("Example resolving a single IP address:\n");
}
qs_man_println(man, " %s -d GeoIPCountryWhois.csv -ip 192.84.12.23\n", cmd);
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
/**
* Comperator to search entries using bsearch.
*/
static int qos_geo_comp(const void *_pA, const void *_pB) {
unsigned long *pA = (unsigned long *)_pA;
qos_geo_t *pB = (qos_geo_t *)_pB;
unsigned long search = *pA;
if((search >= pB->start) && (search <= pB->end)) return 0;
if(search > pB->start) return 1;
if(search < pB->start) return -1;
return -1; // error
}
/**
* Loads the (sorted) CSV file into the memory.
*
* @param pool
* @param db Path to the db file
* @param size Returns the size f the db (elements in the array)
* @param msg Error message if something got wrong
* @return Array with all entries from the CSV file (or NULL on error)
*/
static qos_geo_t *qos_loadgeo(apr_pool_t *pool, const char *db, int *size, char **msg, int *errors) {
regmatch_t ma[MAX_REG_MATCH];
regex_t preg;
regex_t pregd;
regex_t pregext;
qos_geo_t *geo = NULL;
qos_geo_t *g = NULL;
qos_geo_t *last = NULL;
int lines = 0;
char line[HUGE_STRING_LEN];
char buf[HUGE_STRING_LEN];
FILE *file;
const qos_inj_t *inj = m_inj;
*size = 0;
if(regcomp(&preg, QS_GEO_PATTERN, REG_EXTENDED)) {
// internal error
*msg = apr_pstrdup(pool, "failed to compile regular expression "QS_GEO_PATTERN);
(*errors)++;
return NULL;
}
if(regcomp(&pregd, QS_GEO_PATTERN_D, REG_EXTENDED)) {
// internal error
*msg = apr_pstrdup(pool, "failed to compile regular expression "QS_GEO_PATTERN_D);
(*errors)++;
return NULL;
}
if(regcomp(&pregext, QS_GEO_PATTERN_EXT, REG_EXTENDED)) {
// internal error
*msg = apr_pstrdup(pool, "failed to compile regular expression "QS_GEO_PATTERN_EXT);
(*errors)++;
return NULL;
}
file = fopen(db, "r");
if(!file) {
(*errors)++;
return NULL;
}
while(fgets(line, sizeof(line), file) != NULL) {
if(strlen(line) > 0) {
if(regexec(&preg, line, 0, NULL, 0) == 0) {
lines++;
} else {
*msg = apr_psprintf(pool, "invalid entry in database: '%s'", line);
(*errors)++;
if(m_verbose) {
char *p = *msg;
while(p[0]) {
if(p[0] < 32) {
p[0] = '.';
}
p++;
}
fprintf(stderr, "line %d: %s\n", lines, *msg);
}
}
}
}
*size = lines;
geo = apr_pcalloc(pool, sizeof(qos_geo_t) * lines);
g = geo;
fseek(file, 0, SEEK_SET);
lines = 0;
while(fgets(line, sizeof(line), file) != NULL) {
lines++;
if(strlen(line) > 0) {
int plus = 0;
if(m_inject) {
strcpy(buf, line);
}
if(regexec(&pregd, line, MAX_REG_MATCH, ma, 0) == 0) {
plus = 1;
}
if(plus || regexec(&preg, line, MAX_REG_MATCH, ma, 0) == 0) {
int missingAddr = 0;
if(regexec(&pregext, line, 0, NULL, 0) != 0) {
missingAddr = 1;
}
line[ma[1].rm_eo] = '\0';
line[ma[2].rm_eo] = '\0';
line[ma[3].rm_eo] = '\0';
g->start = atoll(&line[ma[1].rm_so]);
g->end = atoll(&line[ma[2].rm_so]);
g->c[0] = '\0';
if(m_inject) {
if(inj->start && (g->start > inj->start)) {
while(inj->start && (g->start > inj->start)) {
printf("%s\n", inj->c);
inj++;
}
} else if(g->start != inj->start) {
if(missingAddr) {
/* some databases do not include IP address
representation (but number only) */
char bs[128];
char be[128];
qos_geo_long2str(bs, g->start);
qos_geo_long2str(be, g->end);
printf("\"%s\",\"%s\",%s", bs, be, buf);
}
}
if(!missingAddr) {
printf("%s", buf);
}
}
strncpy(g->country, &line[ma[3].rm_so], 2);
if(last) {
if(g->start < last->start) {
*msg = apr_psprintf(pool, "wrong order/lines not sorted (line %d)", lines);
(*errors)++;
if(m_verbose) {
fprintf(stderr, "line %d: wrong order/lines not sorted\n", lines);
}
}
}
if(plus) {
line[ma[4].rm_eo] = '\0';
strncpy(g->c, &line[ma[4].rm_so], 500);
}
last = g;
g++;
}
}
}
fclose(file);
return geo;
}
int main(int argc, const char * const argv[]) {
int errors = 0;
int rc;
int stat = 0;
const char *ip = NULL;
char *msg = NULL;
qos_geo_t *geo;
int size;
const char *db = NULL;
apr_table_t *entries;
apr_pool_t *pool;
const char *cmd = strrchr(argv[0], '/');
apr_app_initialize(&argc, &argv, NULL);
apr_pool_create(&pool, NULL);
entries = apr_table_make(pool, 100);
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv, "-d") == 0) {
if (--argc >= 1) {
db = *(++argv);
}
} else if(strcmp(*argv, "-ip") == 0) {
if (--argc >= 1) {
ip = *(++argv);
}
} else if(strcmp(*argv, "-s") == 0) {
stat = 1;
} else if(strcmp(*argv, "-l") == 0) {
m_inject = 1;
} else if(strcmp(*argv, "-v") == 0) {
m_verbose = 1;
} else if(strcmp(*argv,"-h") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
} else {
usage(cmd, 0);
}
argc--;
argv++;
}
if(db == NULL) {
usage(cmd, 0);
}
rc = nice(10);
if(rc == -1) {
fprintf(stderr, "ERROR, failed to change nice value: %s\n", strerror(errno));
}
geo = qos_loadgeo(pool, db, &size, &msg, &errors);
if(geo == NULL || msg != NULL) {
if(msg) {
char *p = msg;
while(p[0]) {
if(p[0] < 32) {
p[0] = '.';
}
p++;
}
}
fprintf(stderr, "failed to load database: %s (total %d errors)\n",
msg ? msg : "-", errors);
exit(1);
}
if(m_inject) {
exit(0);
}
if(ip) {
qos_geo_t *pB;
unsigned long search = qos_geo_str2long(pool, ip);
printf("search %lu: ", search);
pB = bsearch(&search,
geo,
size,
sizeof(qos_geo_t),
qos_geo_comp);
if(pB) {
printf("%s\n", pB->country);
} else {
printf("n/a\n");
}
return 0;
}
// start reading from stdin
{
char prev;
qos_geo_t *pB;
apr_pool_t *tmp;
char *line = calloc(1, MAX_LINE_BUFFER+1);
regex_t preg;
regex_t preg2;
regmatch_t ma[MAX_REG_MATCH];
apr_pool_create(&tmp, NULL);
if(regcomp(&preg, IPPATTERN, REG_EXTENDED)) {
exit(1);
}
regcomp(&preg2, IPPATTERN2, REG_EXTENDED);
while(fgets(line, MAX_LINE_BUFFER, stdin) != NULL) {
int match = regexec(&preg, line, MAX_REG_MATCH, ma, 0);
if(match != 0) {
char *dx = strchr(line, ';');
if(dx && ((dx - line) <= 15)) {
// file starts probably with <ip>; => a qslog -pc file?
match = regexec(&preg2, line, MAX_REG_MATCH, ma, 0);
}
}
if(match == 0) {
unsigned long search;
prev = line[ma[1].rm_eo];
line[ma[1].rm_eo] = '\0';
search = qos_geo_str2long(tmp, &line[ma[1].rm_so]);
apr_pool_clear(tmp);
pB = bsearch(&search,
geo,
size,
sizeof(qos_geo_t),
qos_geo_comp);
if(stat) {
/* creates a single statistic entry for each country (used to collect
requests per source country) */
if(pB) {
qos_geo_stat_t *s = (qos_geo_stat_t *)apr_table_get(entries, pB->country);
if(s == NULL) {
s = apr_pcalloc(pool, sizeof(qos_geo_stat_t));
s->num = 0;
s->c = pB->c;
apr_table_addn(entries, apr_pstrdup(pool, pB->country), (char *)s);
}
s->num++;
}
} else {
/* modifies each log line inserting the country code
*/
char cr = prev;
char delw[2];
char delx[2];
delw[1] = '\0';
delw[0] = ' ';
delx[1] = '\0';
delx[0] = ' ';
if(line[ma[1].rm_eo+1] == ' ') {
delx[0] = '\0';
}
if(line[ma[1].rm_eo+1] == ';') {
delx[0] = ';';
}
if(prev <= CR) {
prev = ' ';
}
if(prev == ' ') {
delw[0] = '\0';
}
if(prev == ';') {
delw[0] = '\0';
delx[0] = ';';
}
if(pB) {
printf("%s%c%s%s%s%s", line, prev,
delw,
pB->country,
delx,
&line[ma[1].rm_eo+1]);
} else {
printf("%s%c%s--%s%s", line, prev,
delw,
delx,
&line[ma[1].rm_eo+1]);
}
if(cr <= CR) {
printf("\n");
}
}
} else {
printf("%s", line);
}
fflush(stdout);
}
if(stat) {
int i;
apr_table_entry_t *entry = (apr_table_entry_t *)apr_table_elts(entries)->elts;
for(i = 0; i < apr_table_elts(entries)->nelts; i++) {
qos_geo_stat_t *s = (qos_geo_stat_t *)entry[i].val;
printf("%7.d %s %s\n", s->num, entry[i].key, s->c ? s->c : "");
}
}
}
return 0;
}

301
tools/src/qsgrep.c Normal file
View file

@ -0,0 +1,301 @@
/* -*-mode: c; indent-tabs-mode: nil; c-basic-offset: 2; -*-
*/
/**
* Filter utility for the quality of service module mod_qos.
*
* qsgrep.c: simple tool to search patterns within files
*
* See http://mod-qos.sourceforge.net/ for further details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qsgrep.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
/* system */
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
/* apr */
#include <apr.h>
#include <apr_strings.h>
#include <apr_file_io.h>
#include <apr_time.h>
#include <apr_getopt.h>
#include <apr_general.h>
#include <apr_lib.h>
#include <apr_portable.h>
#include <apr_support.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "qs_util.h"
#ifndef POSIX_MALLOC_THRESHOLD
#define POSIX_MALLOC_THRESHOLD (10)
#endif
/* same as APR_SIZE_MAX which doesn't appear until APR 1.3 */
#define QSUTIL_SIZE_MAX (~((apr_size_t)0))
typedef struct {
int rm_so;
int rm_eo;
} regmatch_t;
static void usage(char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s - prints matching patterns within a file.\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s -e <pattern> -o <sub string> [<path>]\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "%s is a simple tool to search patterns within files.\n", cmd);
qs_man_print(man, "It uses regular expressions to find patterns and prints the\n");
qs_man_print(man, "submatches within a pre-defined format string.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " -e <pattern>\n");
if(man) printf("\n");
qs_man_print(man, " Specifies the search pattern.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -o <string>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the output string where $0-$9 are substituted by the\n");
qs_man_print(man, " submatches of the regular expression.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " <path>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the input file to process. %s reads from\n", cmd);
qs_man_print(man, " from standard input if this parameter is omitted.\n");
printf("\n");
printf("\n");
if(man) {
printf(".SH EXAMPLE\n");
qs_man_println(man, "Shows the IP addresses of clients causing mod_qos(031) messages):\n");
printf("\n");
} else {
printf("Example (shows the IP addresses of clients causing mod_qos(031) messages):\n");
}
qs_man_println(man, " %s -e 'mod_qos\\(031\\).*, c=([a-zA-Z0-9:.]*)' -o 'ip=$1' error_log\n", cmd);
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
/*
* Substitutes for $0-$9 within the matching string.
* See ap_pregsub().
*/
char *qs_pregsub(apr_pool_t *pool, const char *input,
const char *source, size_t nmatch,
qs_regmatch_t pmatch[]) {
const char *src = input;
char *dest, *dst;
char c;
size_t no;
int len;
if(!source) {
return NULL;
}
if(!nmatch) {
return apr_pstrdup(pool, src);
}
/* First pass, find the size */
len = 0;
while((c = *src++) != '\0') {
if(c == '&')
no = 0;
else if (c == '$' && apr_isdigit(*src))
no = *src++ - '0';
else
no = 10;
if (no > 9) { /* Ordinary character. */
if (c == '\\' && (*src == '$' || *src == '&'))
src++;
len++;
}
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
if(QSUTIL_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so) {
fprintf(stderr, "ERROR, integer overflow or out of memory condition");
return NULL;
}
len += pmatch[no].rm_eo - pmatch[no].rm_so;
}
}
dest = dst = apr_pcalloc(pool, len + 1);
/* Now actually fill in the string */
src = input;
while ((c = *src++) != '\0') {
if (c == '&')
no = 0;
else if (c == '$' && apr_isdigit(*src))
no = *src++ - '0';
else
no = 10;
if (no > 9) { /* Ordinary character. */
if (c == '\\' && (*src == '$' || *src == '&'))
c = *src++;
*dst++ = c;
}
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
len = pmatch[no].rm_eo - pmatch[no].rm_so;
memcpy(dst, source + pmatch[no].rm_so, len);
dst += len;
}
}
*dst = '\0';
return dest;
}
int main(int argc, const char * const argv[]) {
unsigned long nr = 0;
char line[32768];
FILE *file = 0;
apr_pool_t *pool;
char *cmd = strrchr(argv[0], '/');
const char *out = NULL;
const char *pattern = NULL;
const char *filename = NULL;
qs_regex_t *preg;
qs_regmatch_t regm[QS_MAX_REG_MATCH];
apr_app_initialize(&argc, &argv, NULL);
apr_pool_create(&pool, NULL);
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv,"-e") == 0) {
if (--argc >= 1) {
pattern = *(++argv);
}
} else if(strcmp(*argv,"-o") == 0) {
if (--argc >= 1) {
out = *(++argv);
}
} else if(strcmp(*argv,"-h") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
} else {
filename = *argv;
}
argc--;
argv++;
}
if(pattern == NULL || out == NULL) {
usage(cmd, 0);
}
if(nice(10) == -1) {
fprintf(stderr, "ERROR, failed to change nice value: %s\n", strerror(errno));
}
preg = apr_palloc(pool, sizeof(qs_regex_t));
if(qs_regcomp(preg, pattern, PCRE2_DOTALL) != 0) {
fprintf(stderr, "ERROR, could not compile '%s\n", pattern);
exit(1);
}
apr_pool_pre_cleanup_register(pool, preg, qs_pregfree);
if(filename) {
file = fopen(filename, "r");
if(!file) {
fprintf(stderr, "ERROR, could not open file\n");
exit(1);
}
} else {
file = stdin;
}
while(fgets(line, sizeof(line), file) != NULL) {
size_t len = strlen(line);
nr++;
if(qs_regexec_len(preg, line, len, QS_MAX_REG_MATCH, regm, 0) >= 0) {
apr_pool_t *subpool;
char *replaced;
apr_pool_create(&subpool, pool);
replaced = qs_pregsub(subpool, out, line, QS_MAX_REG_MATCH, regm);
if(!replaced) {
fprintf(stderr, "ERROR, failed to substitute submatches (line=%lu)\n", nr);
} else {
printf("%s\n", replaced);
fflush(stdout);
}
apr_pool_destroy(subpool);
}
}
if(filename) {
fclose(file);
}
apr_pool_destroy(pool);
return 0;
}

132
tools/src/qshead.c Normal file
View file

@ -0,0 +1,132 @@
/* -*-mode: c; indent-tabs-mode: nil; c-basic-offset: 2; -*-
*/
/**
* Utilities for the quality of service module mod_qos.
*
* qshead.c: Shows the beginning of a log file stopping at the provided pattern.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qshead.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include "qs_util.h"
static void usage(char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s - an utility reading from stdin and printing all"
" lines to stdout until"
" reaching the defined pattern.\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s -p <pattern>\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, " %s reads lines from stdin and prints them to stdout until a line contains\n", cmd);
qs_man_print(man, " the specified pattern (literal string).\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " -p <pattern>\n");
if(man) printf("\n");
qs_man_print(man, " Search pattern (literal string).\n");
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1) qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
int main(int argc, const char * const argv[]) {
char line[32768];
const char *pattern = NULL;
char *cmd = strrchr(argv[0], '/');
int status = 0;
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv,"-p") == 0) {
if (--argc >= 1) {
pattern = *(++argv);
}
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
}
argc--;
argv++;
}
if(pattern == NULL) {
usage(cmd, 0);
}
while(fgets(line, sizeof(line), stdin) != NULL) {
printf("%s", line);
if(strstr(line, pattern)) {
return status;
}
}
return status;
}

2681
tools/src/qslog.c Normal file

File diff suppressed because it is too large Load diff

423
tools/src/qslogger.c Normal file
View file

@ -0,0 +1,423 @@
/* -*-mode: c; indent-tabs-mode: nil; c-basic-offset: 2; -*-
*/
/**
* Utilities for the quality of service module mod_qos.
*
* qslogger.c: Piped logging forwarding log data to syslog
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qslogger.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <regex.h>
#include <syslog.h>
#include <apr.h>
#include <apr_lib.h>
#include "qs_util.h"
// [Wed Mar 28 22:40:41 2012] [warn]
#define QS_DEFAULTPATTERN "^\\[[0-9a-zA-Z :]+\\] \\[([a-z]+)\\] "
#define QS_MAX_PATTERN_MA 2
static int m_default_severity = LOG_NOTICE;
/**
* Similar to standard strstr() but case insensitive and length limitation
* (string which is not 0 terminated).
*
* @param s1 String to search in
* @param s2 Pattern to ind
* @param len Length of s1
* @return pointer to the beginning of the substring s2 within s1, or NULL
* if the substring is not found
*/
static const char *qs_strncasestr(const char *s1, const char *s2, int len) {
const char *e1 = &s1[len-1];
char *p1, *p2;
if (*s2 == '\0') {
/* an empty s2 */
return((char *)s1);
}
while(1) {
for ( ; (*s1 != '\0') && (s1 <= e1) && (apr_tolower(*s1) != apr_tolower(*s2)); s1++);
if (*s1 == '\0' || s1 > e1) {
return(NULL);
}
/* found first character of s2, see if the rest matches */
p1 = (char *)s1;
p2 = (char *)s2;
for (++p1, ++p2; (apr_tolower(*p1) == apr_tolower(*p2)) && (p1 <= e1); ++p1, ++p2) {
if((p1 > e1) && (*p2 != '\0')) {
// reached the end without match
return NULL;
}
if (*p2 == '\0') {
/* both strings ended together */
return((char *)s1);
}
}
if (*p2 == '\0') {
/* second string ended, a match */
break;
}
/* didn't find a match here, try starting at next character in s1 */
s1++;
}
return((char *)s1);
}
/**
* Rerurns the priority value
*
* @param priorityname Part of the log message to search the priority in
* @param len Length of the priority string
* @return Priority, LOG_NOTICE (see m_default_severity) if provided name is not recognized.
*/
static int qsgetprio(const char *priorityname, int len) {
int p = m_default_severity;
if(!priorityname) {
return p;
}
if(qs_strncasestr(priorityname, "alert", len)) {
p = LOG_ALERT;
} else if(qs_strncasestr(priorityname, "crit", len)) {
p = LOG_CRIT;
} else if(qs_strncasestr(priorityname, "debug", len)) {
p = LOG_DEBUG;
} else if(qs_strncasestr(priorityname, "emerg", len)) {
p = LOG_EMERG;
} else if(qs_strncasestr(priorityname, "err", len)) {
p = LOG_ERR;
} else if(qs_strncasestr(priorityname, "info", len)) {
p = LOG_INFO;
} else if(qs_strncasestr(priorityname, "notice", len)) {
p = LOG_NOTICE;
} else if(qs_strncasestr(priorityname, "panic", len)) {
p = LOG_EMERG;
} else if(qs_strncasestr(priorityname, "warn", len)) {
p = LOG_WARNING;
}
return p;
}
/**
* Extracts the severity of the message using the provided
* regular expression and determinest the priofity using
* qsgetprio().
*
* @param preg Regular expression to extract the severity
* @param line Log fline to extract the severity from
* @return Level or LOG_NOTICE (see m_default_severity) if level could not be determined.
*/
static int qsgetlevel(regex_t preg, const char *line) {
int level = m_default_severity;
regmatch_t ma[QS_MAX_PATTERN_MA];
if(regexec(&preg, line, QS_MAX_PATTERN_MA, ma, 0) == 0) {
int len = ma[1].rm_eo - ma[1].rm_so;
level = qsgetprio(&line[ma[1].rm_so], len);
}
return level;
}
/* entry within the facility table */
typedef struct {
const char* name;
int f;
} qs_f_t;
/**
* Table of known facilities, see sys/syslog.h.
*/
static const qs_f_t qs_facilities[] = {
#ifdef LOG_AUTHPRIV
{ "authpriv", LOG_AUTHPRIV },
#endif
{ "auth", LOG_AUTH },
{ "cron", LOG_CRON },
{ "daemon", LOG_DAEMON },
#ifdef LOG_FTP
{ "ftp", LOG_FTP },
#endif
{ "kern", LOG_KERN },
{ "lpr", LOG_LPR },
{ "mail", LOG_MAIL },
{ "news", LOG_NEWS },
{ "security", LOG_AUTH },
{ "syslog", LOG_SYSLOG },
{ "user", LOG_USER },
{ "uucp", LOG_UUCP },
{ "local0", LOG_LOCAL0 },
{ "local1", LOG_LOCAL1 },
{ "local2", LOG_LOCAL2 },
{ "local3", LOG_LOCAL3 },
{ "local4", LOG_LOCAL4 },
{ "local5", LOG_LOCAL5 },
{ "local6", LOG_LOCAL6 },
{ "local7", LOG_LOCAL7 },
{ NULL, -1 }
};
/**
* Determines the facility (user input).
*
* @param facilityname
* @return The facility id or LOG_DAEMON if the provided
* string is unknown.
*/
static int qsgetfacility(const char *facilityname) {
int f = LOG_DAEMON;
const qs_f_t *facilities = qs_facilities;
if(!facilityname) {
return f;
}
while(facilities->name) {
if(strcasecmp(facilityname, facilities->name) == 0) {
f = facilities->f;
break;
}
facilities++;
}
return f;
}
/**
* Usage message (or man page)
*
* @param cmd
* @param man
*/
static void usage(const char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s - another shell command interface to the system log module (syslog).\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s [-t <tag>] [-f <facility>] [-l <level>] [-x <prefix>] [-r <expression>] [-d <level>] [-u <name>] [-p]\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "Use this utility to forward log messages to the systems syslog\n");
qs_man_print(man, "facility, e.g., to forward the messages to a remote host.\n");
qs_man_print(man, "It reads data from stdin.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf("\n.TP\n");
qs_man_print(man, " -t <tag>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the tag name which shall be used to define the origin\n");
qs_man_print(man, " of the messages, e.g. 'httpd'.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -f <facility>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the syslog facility. Default is 'daemon'.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -u <name>\n");
if(man) printf("\n");
qs_man_print(man, " Becomes another user, e.g. www-data.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -l <level>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the minimal severity a message must have in order to\n");
qs_man_print(man, " be forwarded. Default is 'DEBUG' (forwarding everything).\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -x <prefix>\n");
if(man) printf("\n");
qs_man_print(man, " Allows you to add a prefix (literal string) to every message.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -r <expression>\n");
if(man) printf("\n");
qs_man_print(man, " Specifies a regular expression which shall be used to\n");
qs_man_print(man, " determine the severity (syslog level) for each log line.\n");
qs_man_print(man, " The default pattern '"QS_DEFAULTPATTERN"' can\n");
qs_man_print(man, " be used for Apache error log messages but you may configure\n");
qs_man_print(man, " your own pattern matching other log formats. Use brackets\n");
qs_man_print(man, " to define the pattern enclosing the severity string.\n");
qs_man_print(man, " Default level (if severity can't be determined) is defined by the\n");
qs_man_print(man, " option '-d' (see below).\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -d <level>\n");
if(man) printf("\n");
qs_man_print(man, " The default severity if the specified pattern (-r) does not\n");
qs_man_print(man, " match and the message's severity can't be determined. Default\n");
qs_man_print(man, " is 'NOTICE'.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -p\n");
if(man) printf("\n");
qs_man_print(man, " Writes data also to stdout (for piped logging).\n");
printf("\n");
if(man) {
printf(".SH EXAMPLE\n");
} else {
printf("Example:\n");
}
qs_man_println(man, " ErrorLog \"|/usr/bin/%s -t apache -f local7\"\n", cmd);
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
int main(int argc, const char * const argv[]) {
int line_len;
char *line = calloc(1, MAX_LINE_BUFFER+1);
const char *cmd = strrchr(argv[0], '/');
int pass = 0;
const char *tag = NULL;
int facility = LOG_DAEMON;
int severity = LOG_DEBUG;
int level = LOG_INFO;
const char *regexpattern = QS_DEFAULTPATTERN;
const char *username = NULL;
const char *prefix = NULL;
regex_t preg;
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv, "-p") == 0) {
pass = 1;
} else if(strcmp(*argv, "-f") == 0) {
if (--argc >= 1) {
const char *facilityname = *(++argv);
facility = qsgetfacility(facilityname);
}
} else if(strcmp(*argv, "-l") == 0) {
if (--argc >= 1) {
const char *severityname = *(++argv);
severity = qsgetprio(severityname, strlen(severityname));
}
} else if(strcmp(*argv, "-x") == 0) {
if (--argc >= 1) {
prefix = *(++argv);
}
} else if(strcmp(*argv,"-u") == 0) { /* switch user id */
if (--argc >= 1) {
username = *(++argv);
}
} else if(strcmp(*argv, "-d") == 0) {
if (--argc >= 1) {
const char *severityname = *(++argv);
m_default_severity = qsgetprio(severityname, strlen(severityname));
}
} else if(strcmp(*argv, "-t") == 0) {
if (--argc >= 1) {
tag = *(++argv);
}
} else if(strcmp(*argv, "-r") == 0) {
if (--argc >= 1) {
regexpattern = *(++argv);
}
} else if(strcmp(*argv,"-h") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
} else {
usage(cmd, 0);
}
argc--;
argv++;
}
if(regcomp(&preg, regexpattern, REG_EXTENDED)) {
fprintf(stderr, "[%s] failed to compile pattern %s", cmd, regexpattern);
exit(1);
}
qs_setuid(username, cmd);
openlog(tag ? tag : getlogin(), 0, facility);
// start reading from stdin
while(fgets(line, MAX_LINE_BUFFER, stdin) != NULL) {
line_len = strlen(line) - 1;
while(line_len > 0) { // cut tailing CR/LF
if(line[line_len] >= ' ') {
break;
}
line[line_len] = '\0';
line_len--;
}
// severity is determined using the regular expression provided by the user
level = qsgetlevel(preg, line);
if(level <= severity) {
// send message
if(prefix) {
syslog(level, "%s%s", prefix, line);
} else {
syslog(level, "%s", line);
}
}
if(pass) {
printf("%s\n", line);
fflush(stdout);
}
}
free(line);
closelog();
return 0;
}

784
tools/src/qspng.c Normal file
View file

@ -0,0 +1,784 @@
/**
* Utilities for the quality of service module mod_qos.
*
* qspng.c: Tool to draw graph from qslog output.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qspng.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <png.h>
//#include <config.h>
#include "qs_util.h"
#include "char.h"
#define HUGE_STRING_LEN 1024
#define X_SAMPLE_RATE 3
/* width */
#define X_COUNTS 60 * 24 / X_SAMPLE_RATE // 24 hours, every 3th sample
/* height */
#define Y_COUNTS 100
/* border */
#define XY_BORDER 20
typedef struct {
const char* param;
const char* name;
int r;
int g;
int b;
} qs_png_elt_t;
/* known graph types */
static const qs_png_elt_t qs_png_elts[] = {
{ "r/s", "requests per second", 20, 30, 130, },
{ "req", "requests per minute", 20, 30, 130, },
{ "b/s", "bytes per second (out)", 30, 45, 130 },
{ "ib/s", "bytes per second (in)", 30, 45, 125 },
{ "esco", "established connections per minute", 40, 95, 140 },
{ "av", "average response time", 40, 95, 140 },
{ "avms", "average response time in milliseconds", 45, 95, 135 },
{ "0-49ms", "requests duration 0-49ms", 45, 100, 180 },
{ "50-99ms", "requests duration 50-99ms", 45, 100, 180 },
{ "100-499ms", "requests duration 100-499ms", 45, 100, 180 },
{ "500-999ms", "requests duration 500-999ms", 45, 100, 180 },
{ "<1s", "requests faster than 1 second", 35, 95, 180 },
{ "1s", "requests faster or equal than 1 second", 35, 90, 180 },
{ "2s", "requests with 2 seconds response time", 30, 85, 180 },
{ "3s", "requests with 3 seconds response time", 25, 90, 180 },
{ "4s", "requests with 4 seconds response time", 25, 95, 180 },
{ "5s", "requests with 5 seconds response time", 15, 90, 180 },
{ ">5s","requests slower than 5 seconds", 35, 90, 185 },
{ "1xx","requests with HTTP status 1xx", 50, 70, 150 },
{ "2xx","requests with HTTP status 2xx", 50, 70, 150 },
{ "3xx","requests with HTTP status 3xx", 50, 70, 150 },
{ "4xx","requests with HTTP status 4xx", 50, 70, 150 },
{ "5xx","requests with HTTP status 5xx", 50, 70, 150 },
{ "ip", "IP addresses", 55, 60, 150 },
{ "usr","active users", 55, 66, 150 },
{ "qV", "created VIP sessions", 55, 50, 155 },
{ "qS", "session pass", 55, 75, 160 },
{ "qD", "access denied", 55, 70, 170 },
{ "qK", "connection closed", 55, 60, 145 },
{ "qT", "dynamic keep-alive", 55, 55, 153 },
{ "qL", "slow down", 55, 65, 140 },
{ "qA", "connection aborts", 55, 50, 175 },
{ "qs", "serialization", 55, 40, 175 },
{ "qu", "start user tracking", 55, 45, 175 },
{ "sl", "system load", 25, 60, 175 },
{ "m", "free memory", 35, 90, 185 },
{ NULL, NULL, 0, 0, 0 }
};
typedef struct qs_png_conf_st {
char *path;
char *param;
} qs_png_conf;
/************************************************************************
* Functions
***********************************************************************/
/**
* Read the stat_log data line by line
*
* @param s IN buffer to store line to
* @param n IN buffer size
* @param f IN file descriptor
*
* @return 1 on EOF, else 0
*/
static int qs_png_getline(char *s, int n, FILE *f) {
register int i = 0;
while (1) {
s[i] = (char) fgetc(f);
if (s[i] == CR) {
s[i] = fgetc(f);
}
if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
s[i] = '\0';
return (feof(f) ? 1 : 0);
}
++i;
}
}
/* png io callback (should write to buff/bio/bucket when using in apache) */
void lp_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
FILE *f = png_get_io_ptr(png_ptr);
fwrite(data, length, 1, f);
}
/* png io callback (not used) */
void lp_flush_data(png_structp png_ptr) {
png_get_io_ptr(png_ptr);
fprintf(stderr, "flush\n");
}
/**
* Writes a single char to the graph
*
* @param x IN x position
* @param y IN y position
* @param row_pointers IN start pointer (0/0)
* @param n IN char to write
*/
static void qs_png_write_char(int x, int y, png_bytep *row_pointers, char n) {
int ix, iy;
int *f = &s_X[0][0];
switch(n) {
case 'a': f = &s_a[0][0]; break;
case 'b': f = &s_b[0][0]; break;
case 'c': f = &s_c[0][0]; break;
case 'd': f = &s_d[0][0]; break;
case 'e': f = &s_e[0][0]; break;
case 'f': f = &s_f[0][0]; break;
case 'g': f = &s_g[0][0]; break;
case 'h': f = &s_h[0][0]; break;
case 'i': f = &s_i[0][0]; break;
case 'j': f = &s_j[0][0]; break;
case 'k': f = &s_k[0][0]; break;
case 'l': f = &s_l[0][0]; break;
case 'm': f = &s_m[0][0]; break;
case 'n': f = &s_n[0][0]; break;
case 'o': f = &s_o[0][0]; break;
case 'p': f = &s_p[0][0]; break;
case 'q': f = &s_q[0][0]; break;
case 'r': f = &s_r[0][0]; break;
case 's': f = &s_s[0][0]; break;
case 't': f = &s_t[0][0]; break;
case 'u': f = &s_u[0][0]; break;
case 'v': f = &s_v[0][0]; break;
case 'w': f = &s_w[0][0]; break;
case 'x': f = &s_x[0][0]; break;
case 'y': f = &s_y[0][0]; break;
case 'z': f = &s_z[0][0]; break;
case ' ': f = &s_SP[0][0]; break;
case '_': f = &s_US[0][0]; break;
case '(': f = &s_BRO[0][0]; break;
case ')': f = &s_BRC[0][0]; break;
case '<': f = &s_LT[0][0]; break;
case '>': f = &s_GT[0][0]; break;
case '-': f = &s_MI[0][0]; break;
case '/': f = &s_SL[0][0]; break;
case ';': f = &s_SC[0][0]; break;
case ',': f = &s_CM[0][0]; break;
case ':': f = &s_CO[0][0]; break;
case '.': f = &s_DT[0][0]; break;
case '\'': f = &s_SQ[0][0]; break;
case 'A': f = &s_a[0][0]; break;
case 'B': f = &s_b[0][0]; break;
case 'C': f = &s_c[0][0]; break;
case 'D': f = &s_d[0][0]; break;
case 'E': f = &s_e[0][0]; break;
case 'F': f = &s_f[0][0]; break;
case 'G': f = &s_g[0][0]; break;
case 'H': f = &s_h[0][0]; break;
case 'I': f = &s_i[0][0]; break;
case 'J': f = &s_j[0][0]; break;
case 'K': f = &s_k[0][0]; break;
case 'L': f = &s_l[0][0]; break;
case 'M': f = &s_M[0][0]; break;
case 'N': f = &s_n[0][0]; break;
case 'O': f = &s_o[0][0]; break;
case 'P': f = &s_p[0][0]; break;
case 'Q': f = &s_q[0][0]; break;
case 'R': f = &s_r[0][0]; break;
case 'S': f = &s_s[0][0]; break;
case 'T': f = &s_t[0][0]; break;
case 'U': f = &s_u[0][0]; break;
case 'V': f = &s_v[0][0]; break;
case 'W': f = &s_w[0][0]; break;
case 'X': f = &s_x[0][0]; break;
case 'Y': f = &s_y[0][0]; break;
case 'Z': f = &s_z[0][0]; break;
case '0': f = &s_0[0][0]; break;
case '1': f = &s_1[0][0]; break;
case '2': f = &s_2[0][0]; break;
case '3': f = &s_3[0][0]; break;
case '4': f = &s_4[0][0]; break;
case '5': f = &s_5[0][0]; break;
case '6': f = &s_6[0][0]; break;
case '7': f = &s_7[0][0]; break;
case '8': f = &s_8[0][0]; break;
case '9': f = &s_9[0][0]; break;
}
/* print the char matrix */
for(iy = 0; iy < S_H_MAX; iy++) {
png_byte* row = row_pointers[y+iy];
for(ix = 0; ix < S_W_MAX; ix++) {
png_byte* ptr = &(row[(x+ix)*4]);
if(f[iy*S_W_MAX + ix] == 1) {
/* foreground */
ptr[0] = 0;
ptr[1] = 0;
ptr[2] = 0;
} else {
/* background */
ptr[0] = 250;
ptr[1] = 250;
ptr[2] = 255;
}
}
}
}
/**
* Writes a single digit 0..9.
* You should normally use either qs_png_write_int() or qs_png_write_int().
*
* @param x IN x position
* @param y IN y position
* @param row_pointers IN start pointer (0/0)
* @param n IN number to write
*/
static void qs_png_write_digit(int x, int y, png_bytep *row_pointers, int n) {
char f = 'X';
if(n == 0) f = '0';
if(n == 1) f = '1';
if(n == 2) f = '2';
if(n == 3) f = '3';
if(n == 4) f = '4';
if(n == 5) f = '5';
if(n == 6) f = '6';
if(n == 7) f = '7';
if(n == 8) f = '8';
if(n == 9) f = '9';
qs_png_write_char(x, y, row_pointers, f);
}
/**
* Writes a string to the graph.
*
* @param x IN x position
* @param y IN y position
* @param row_pointers IN start pointer (0/0)
* @param n IN string to write
*/
static void qs_png_write_string(int x, int y, png_bytep *row_pointers, const char *n) {
int i = 0;
int offset = 0;
while(n[i] != '\0') {
qs_png_write_char(x+offset, y, row_pointers, n[i]);
i++;
offset = offset + S_W_MAX;
}
}
/**
* Writes a number (int) to the graph (1:1).
*
* @param x IN x position
* @param y IN y position
* @param row_pointers IN start pointer (0/0)
* @param n IN number to write
*/
static void qs_png_write_int(int x, int y, png_bytep *row_pointers, int n) {
char num_str[HUGE_STRING_LEN];
snprintf(num_str, sizeof(num_str), "%d", n);
qs_png_write_string(x, y, row_pointers, num_str);
}
/**
* Writes a number (long) to the graph using k,M for big numbers.
*
* @param x IN x position
* @param y IN y position
* @param row_pointers IN start pointer (0/0)
* @param n IN string to write
*/
static void qs_png_write_long(int x, int y, png_bytep *row_pointers, long n) {
char num_str[HUGE_STRING_LEN];
snprintf(num_str, sizeof(num_str), "%ld", n);
if(n >= 1000) {
snprintf(num_str, sizeof(num_str), "%ldk", n/1000);
}
if(n >= 1000000) {
snprintf(num_str, sizeof(num_str), "%ldM", n/1000000);
}
qs_png_write_string(x, y, row_pointers, num_str);
}
/**
* Labels the graph (min,max,title).
*
* @param width IN size (x axis) of the graph
* @param height IN size (y axis) of the graph
* @param border IN border size around the graph
* @param row_pointers IN start pointer (0/0)
* @param max IN max y value
* @param name IN title
*/
static void qs_png_label(int width, int height, int border,
png_bytep *row_pointers, long max,
const char *name) {
/* MAX */
int i;
int step = height/5;
int c = 5;
for(i = 0; i < height; i = i + step) {
qs_png_write_long(1, border - (S_W_MAX/2) + i, row_pointers, max/5*c);
c--;
}
/* MIN */
qs_png_write_int(1, height + border - (S_W_MAX/2), row_pointers, 0);
/* title */
{
char buf[HUGE_STRING_LEN];
snprintf(buf, sizeof(buf), "%s", name);
qs_png_write_string(XY_BORDER, XY_BORDER/2-S_H_MAX/2, row_pointers, buf);
}
}
static void lp_init(int width, int height, int border, png_bytep **start) {
png_bytep *row_pointers;
int b_width = width + (2 * border);
int b_height = height + (2 * border);
int x, y;
/* alloc memory */
row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * b_height);
for(y=0; y<b_height; y++) {
row_pointers[y] = (png_byte*) malloc(b_width * 4);
}
/* background */
for(y=0; y<b_height; y++) {
png_byte* row = row_pointers[y];
for(x=0; x<b_width; x++) {
png_byte* ptr = &(row[x*4]);
ptr[0] = 250;
ptr[1] = 250;
ptr[2] = 255;
ptr[3] = 250;
}
}
for(y=border; y<b_height-border; y++) {
png_byte* row = row_pointers[y];
for(x=border; x<b_width-border; x++) {
png_byte* ptr = &(row[x*4]);
ptr[0] = 245;
ptr[1] = 245;
ptr[2] = 250;
}
}
*start = row_pointers;
}
/**
* "Main" png function:
* - reads the data from the file
* - draws the curve
* - labels the x axis
*
* @param width IN size (x axis) of the graph
* @param height IN size (y axis) of the graph
* @param border IN border size around the graph
* @param row_pointers IN start pointer (0/0)
* @param stat_log IN file descriptor to the input file
* @param name IN title
* @param c_r IN color red (0..255)
* @param c_g IN color green (0..255)
* @param c_b IN color blue (0..255)
*/
static long qs_png_draw(int width, int height, int border,
png_bytep *row_pointers, FILE *stat_log, const char *name,
int c_r, int c_g, int c_b) {
int x, y;
long req[width]; // values
long max_req[width]; // values
int hours[width]; // time marks on x axis
long tmp[X_SAMPLE_RATE]; // used to build average over multiple samples
int sample = 1; // sample rate counter (1 to X_SAMPLE_RATE)
int i = 0;
char line[HUGE_STRING_LEN];
long peak = 0; // max of all values
double scale = 1; // scaling factor (height x scale = unit)
int hour = -1; // detect "new" hour
char date_str[32] = ""; // string storing the first day (if fist value is at 00h)
long ret;
for(x=0; x<width; x++) hours[x] = 0;
/* reads the file and resample measure points to width of the graph */
while(!qs_png_getline(line, sizeof(line), stat_log) && i < width) {
char *p = strstr(line, name);
req[i] = 0;
max_req[i] = 0;
if(p && ((p - line) > 8)) {
char *e;
p=p+strlen(name);
e = strchr(p,';');
if(e) e[0] = '\0';
e = strchr(p, '.'); /** sl uses fp value */
if(e) e[0] = '\0';
tmp[sample-1] = atol(p);
} else {
tmp[sample-1] = 0;
}
/* hour (stat_log time format: %d.%m.%Y %H:%M:%S (19 char)) */
p = strchr(line, ';');
if(p && (p-line == 19 )) {
p = p - 6;
p[0] = '\0';
p = p - 2;
hours[i] = atoi(p);
}
/* use the defined sample rate */
if(sample == X_SAMPLE_RATE) {
int j;
int max_value = 0;
for(j = 0; j < X_SAMPLE_RATE; j++) {
req[i] = req[i] + tmp[j];
if(max_value < tmp[j]) {
max_value = tmp[j];
}
}
max_req[i] = max_value;
if(max_req[i] > peak) peak = max_req[i];
/* build average */
req[i] = req[i] / X_SAMPLE_RATE;
sample = 1;
i++;
/* and store the current date (%d.%m.%Y (10 char)) if the
first value is at 00h */
if(hours[i] == 0 && i == 1) {
p = strchr(line, ' ');
if(p && (p-line == 10)) {
p[0] = '\0';
strcpy(date_str, line);
}
}
} else {
sample++;
}
}
/* calculate y axis scaling (1:1 are height pixels) */
if(peak < 10) {
scale = 0.1;
} else {
while((peak / scale) > height) {
if(scale < 8) {
scale = scale * 2;
} else {
if(scale == 8) {
scale = 10;
} else {
scale = scale * 10;
}
}
}
}
/* draw the curve */
for(x=0; x<i; x++) {
/* max */
for(y=0; y<(max_req[x]/scale); y++) {
png_byte* row = row_pointers[height-y-1+border];
png_byte* ptr = &(row[x*4+(4*border)]);
ptr[0] = c_r + 75;
ptr[1] = c_g + 75;
ptr[2] = c_b + 75;
}
/* average */
for(y=0; y<(req[x]/scale); y++) {
png_byte* row = row_pointers[height-y-1+border];
png_byte* ptr = &(row[x*4+(4*border)]);
ptr[0] = c_r;
ptr[1] = c_g;
ptr[2] = c_b;
}
/* label the x axis */
if(hour != hours[x]) {
hour = hours[x];
for(y=0; y<(height); y=y+3) {
png_byte* row = row_pointers[y+border];
png_byte* ptr = &(row[x*4+(4*border)]);
ptr[0] = 50;
ptr[1] = 50;
ptr[2] = 50;
}
if(hour%2 == 0) {
qs_png_write_digit(x-S_W_MAX+border, height + border + 1, row_pointers, hour/10);
qs_png_write_digit(x-S_W_MAX+border+S_W_MAX, height + border + 1, row_pointers, hour%10);
qs_png_write_char(x-S_W_MAX+border+2*S_W_MAX, height + border + 1, row_pointers, 'h');
}
}
}
/* print date */
qs_png_write_string(border, height+border+2+S_H_MAX, row_pointers, date_str);
/* horizontal lines every 1/4 height */
for(y=(height/5); y<height; y=y+height/5) {
png_byte* row = row_pointers[y+border];
for(x=0; x<i; x=x+3) {
png_byte* ptr = &(row[x*4+(4*border)]);
ptr[0] = 50;
ptr[1] = 50;
ptr[2] = 50;
}
}
ret = scale * height;
return ret;
}
static void usage(char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
qs_man_print(man, "%s - an utility to draw a png graph from qslog(1) output data.\n", cmd);
} else {
qs_man_print(man, "Utility to draw a png graph from qslog output data.\n");
}
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s -i <stat_log_file> -p <parameter> -o <out_file> [-10]\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "%s is a tool to generate png (portable network graphics)\n", cmd);
qs_man_print(man, "raster images files from semicolon separated data generated by the\n");
qs_man_print(man, "qslog utility. It reads up to the first 1440 entries (24 hours)\n");
qs_man_print(man, "and prints a graph using the values defined by the 'parameter' \n");
qs_man_print(man, "name.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " -i <stats_log_file>\n");
if(man) printf("\n");
qs_man_print(man, " Input file to read data from.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -p <parameter>\n");
if(man) printf("\n");
qs_man_print(man, " Parameter name, e.g. r/s or usr.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -o <out_file>\n");
if(man) printf("\n");
qs_man_print(man, " Output file name, e.g. stat.png.\n");
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslogger(1), qslog(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("\n");
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
int main(int argc, char **argv) {
int y;
int width, height, b_width, b_height;
png_byte color_type;
png_byte bit_depth;
int scale;
png_structp png_ptr;
png_infop info_ptr;
png_bytep *row_pointers;
char *infile = NULL;
FILE *f;
FILE *stat_log;
char *cmd = strrchr(argv[0], '/');
const char *param = NULL;
const char *name = "";
char *out = NULL;
int c_r = 20;
int c_g = 50;
int c_b = 175;
const qs_png_elt_t* elt;
if(cmd == NULL) {
cmd = argv[0];
} else {
cmd++;
}
while(argc >= 1) {
if(strcmp(*argv,"-i") == 0) {
if (--argc >= 1) {
infile = *(++argv);
}
} else if(strcmp(*argv,"-p") == 0) {
if (--argc >= 1) {
param = *(++argv);
name = param;
}
} else if(strcmp(*argv,"-o") == 0) {
if (--argc >= 1) {
out = *(++argv);
}
} else if(strcmp(*argv,"-h") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
}
argc--;
argv++;
}
if(infile == NULL || param == NULL || out == NULL) usage(cmd, 0);
for(elt = qs_png_elts; elt->param != NULL ; ++elt) {
if(strcmp(elt->param, param) == 0) {
name = elt->name;
c_r = elt->r;
c_g = elt->g;
c_b = elt->b;
}
}
stat_log = fopen(infile, "r");
if(stat_log == NULL) {
fprintf(stderr,"[%s]: ERROR, could not open input file <%s>\n", cmd, infile);
exit(1);
}
f = fopen(out, "wb");
if(f == NULL) {
fprintf(stderr,"[%s]: ERROR, could not open output file <%s>\n", cmd, out);
exit(1);
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(png_ptr == NULL) {
fprintf(stderr,"[%s]: ERROR, could not create png struct\n", cmd);
exit(1);
}
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL) {
fprintf(stderr,"[%s]: ERROR, could not create png information struct\n", cmd);
exit(1);
}
if(setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr,"[%s]: ERROR, could not init png struct\n", cmd);
exit(1);
}
png_set_write_fn(png_ptr, f, lp_write_data, NULL);
/* write header */
if(setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr,"[%s]: ERROR, could not write png header\n", cmd);
exit(1);
}
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
bit_depth = 8;
width = X_COUNTS;
height = Y_COUNTS;
b_width = width + (2 * XY_BORDER);
b_height = height + (2 * XY_BORDER);
png_set_IHDR(png_ptr, info_ptr,
b_width, b_height,
bit_depth,
color_type,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
/* write bytes */
if(setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr,"[%s]: ERROR, could not write png data\n", cmd);
exit(1);
}
/* alloc and background */
lp_init(width, height, XY_BORDER, &row_pointers);
/* paint */
{
char buf[HUGE_STRING_LEN];
snprintf(buf, sizeof(buf), ";%s;", param);
scale = qs_png_draw(width, height, XY_BORDER, row_pointers,
stat_log, buf, c_r, c_g, c_b);
}
/* min/max/title label */
qs_png_label(width, height, XY_BORDER, row_pointers, scale,
name);
/* done, write image */
png_write_image(png_ptr, row_pointers);
/* end write */
if(setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr,"[%s]: ERROR, could not write png data\n", cmd);
exit(1);
}
png_write_end(png_ptr, NULL);
/* cleanup heap allocation */
for(y=0; y<height; y++) {
free(row_pointers[y]);
}
free(row_pointers);
fclose(f);
fclose(stat_log);
return 0;
}

226
tools/src/qsre.c Normal file
View file

@ -0,0 +1,226 @@
/**
* qsre.c: pcre expression match test tool
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qsre.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
/* system */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
/* apr */
#include <apr.h>
#include <apr_strings.h>
#include <apr_time.h>
#include <apr_general.h>
#include <apr_lib.h>
#include <apr_portable.h>
#include <apr_support.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "qs_util.h"
#define QS_OVECCOUNT 100
static void usage(const char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s matches a regular expression against test strings.\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s <string>|<path> <pcre>|<path>\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "Regular expression test tool.\n");
qs_man_print(man, "The provided regular expression (pcre, caseless matching, \".\" matches anything\n");
qs_man_print(man, "incl. newline) is appplied against the provided test strings to verify if the\n");
qs_man_print(man, "pattern matches.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " <string>|<path>\n");
if(man) printf("\n");
qs_man_print(man, " The first argument either defines a single test string of a path to\n");
qs_man_print(man, " a file containing either multiple test strings or a test pattern with\n");
qs_man_print(man, " newline characters (text).\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " <pcre>|<path>\n");
if(man) printf("\n");
qs_man_print(man, " The second argument either defines a regular expression or a path to\n");
qs_man_print(man, " a file containing the expression.\n");
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsrespeed(1), qsrotate(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
static int rmatch(const char *line, qs_regex_t *preg) {
qs_regmatch_t regm[QS_MAX_REG_MATCH];
int rc_c = -1;
do {
int rc = qs_regexec_len(preg, line, strlen(line), QS_MAX_REG_MATCH, regm, 0);
if(rc >= 0) {
int ix;
rc_c = 0;
printf("[%.*s]", regm[0].rm_eo - regm[0].rm_so, &line[regm[0].rm_so]);
for(ix = 1; ix < rc; ix++) {
printf(" $%d=%.*s", ix, regm[ix].rm_eo - regm[ix].rm_so, &line[regm[ix].rm_so]);
}
line = &line[regm[0].rm_eo];
} else {
line = NULL;
}
} while(line && line[0]);
return rc_c;
}
int main(int argc, const char *const argv[]) {
const char *errptr = NULL;
int erroffset;
qs_regex_t *preg;
int rc_c = -1;
const char *line;
const char *in;
const char *pattern;
FILE *file;
apr_pool_t *pool;
char *raw = "";
int linenr = 0;
const char *cmd = strrchr(argv[0], '/');
apr_app_initialize(&argc, &argv, NULL);
apr_pool_create(&pool, NULL);
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
argc--;
argv++;
if(argc != 2) {
if(argc == 1 && strcmp(argv[0], "--man") == 0) {
usage(cmd, 1);
} else {
usage(cmd, 0);
}
}
in = argv[0];
pattern = argv[1];
file = fopen(pattern, "r");
if(file) {
char readline[MAX_LINE];
if(fgets(readline, MAX_LINE-1, file) != NULL) {
int len = strlen(readline);
while(len > 0 && readline[len] < 32) {
readline[len] = '\0';
len--;
}
pattern = apr_pstrdup(pool, readline);
}
fclose(file);
}
printf("expression: %s\n", pattern);
preg = apr_palloc(pool, sizeof(qs_regex_t));
if(qs_regcomp(preg, pattern, PCRE2_DOTALL|PCRE2_CASELESS) != 0) {
fprintf(stderr, "ERROR, rule <%s> could not compile regular expression\n", pattern);
exit(1);
}
apr_pool_pre_cleanup_register(pool, preg, qs_pregfree);
file = fopen(in, "r");
if(file) {
char readline[MAX_LINE];
while(fgets(readline, MAX_LINE-1, file) != NULL) {
int len = strlen(readline);
linenr++;
printf("line %.3d: ", linenr);
raw = apr_pstrcat(pool, raw, readline, NULL);
while(len > 0 && readline[len] < 32) {
readline[len] = '\0';
len--;
}
if(readline[0] >= 32 && strlen(readline) > 0) {
line = readline;
rc_c = rmatch(line, preg);
}
printf("\n");
}
fclose(file);
printf("entire content match:\n");
rc_c = rmatch(raw, preg);
printf("\n");
} else {
line = in;
rc_c = rmatch(line, preg);
printf("\n");
}
if(rc_c < 0) {
printf("no match\n");
return 2;
}
return 0;
}

308
tools/src/qsrespeed.c Normal file
View file

@ -0,0 +1,308 @@
/**
* Utility for the quality of service module mod_qos.
*
* qsrespeed.c: tool to measure the processing time
* of regular expressions
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Revision: 2654 $";
/* system */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
/* apr */
#include <apr.h>
#include <apr_strings.h>
#include <apr_time.h>
#include <apr_general.h>
#include <apr_lib.h>
#include <apr_portable.h>
#include <apr_support.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "qs_util.h"
#define LOOPS 100
typedef struct {
qs_regex_t *preg;
} rule_t;
static void usage(const char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "Tool to compare / estimate the processing time for (Perl-compatible)\n");
qs_man_print(man, "regular expressions (PCRE).\n");
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s <path>\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "%s loads regular expressions from the provided file and matches\n", cmd);
qs_man_print(man, "them against a built-in set of strings measuring the time needed to\n");
qs_man_print(man, "process them. It's a benchmark too to judge the expressions you have\n");
qs_man_print(man, "defined regarding the potential CPU consumption.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " <path>\n");
if(man) printf("\n");
qs_man_print(man, " Defines the input file to process. The file consists a list of\n");
qs_man_print(man, " (separated by a newline character) regular expressions to test\n");
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrotate(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
typedef struct {
const char* string;
int len;
} qs_r_t;
int main(int argc, const char *const argv[]) {
int datalen=0;
qs_r_t data[] = {
{ "Emma", 0 },
{ "Buchschacher", 0 },
{ "Schafhauserstrasse 60, 8000 Zürich", 0 },
{ "128128127136178267893209807237276365235", 0 },
{ "/get/application/data/index/list/all/data", 0 },
{ "05.03.1978", 0 },
{ "888 888-888-777", 0 },
{ "name=value&id=kAfBFLJaBQB-AAABBQAAAAZgAAAA9--DwnTUWct-AAA2&host=me.main.org&key=121213122aaaaaaaaaaMMMM123&tex=emb+ed", 0 },
{ "<x:ml><node attribute=\"value\" attr2=\"99999999\">text shows_this!</node></x:ml>", 0 },
{ "<html lang=\"en\"><meta charset=\"utf-8\"><meta property=\"og:type\" content=\"website\"></html>", 0 },
{ "lajksdfhjklasdhfaskdjfhklasjdlfaksdhfasjkdflsajkdflkdflhdjklfadhfksdjfhklasjdhfskljdfhsklajdhflskjdfhlskjhdflksjdhlfksjdhfjklsdhfklsdhfklsjdhklshlksfhdklfhslkdfhlskhdklsjhdflskfhlsh", 0 },
{ "ajksdfhjklasdhfaskdjfhklasjdlfaksdhfasjkdflsajkdflkdflhdjklfadhfksdjfhklasjdhfskljdfhsklajdhflskjdfhlskjhdflksjdhlfksjdhfjklsdhfklsdhfklsjdhklshlksfljsdahsdznvztbasmuiwmereizfrbizvnsdmovosduvnuztbvzucxzvmpmvdzubtfrmeirmrnbewrJHJSBNUAIMSODMAINBSUDTAZSUDIOASMDNBAGZDTSZBUANIMOINSAUBZDGTZUIOIMSKNABJDHT9807765243567283992039209376526368799230827836526789 ç%&/\"(><<<-.,:;)*=)()(&%\"ç", 0 },
{ "text/html,application/xhtml+xm…plication/xml;q=0.9,*/*;q=0.8", 0 },
{ "Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5", 0},
{ "Hm_lvt_ef1299edab2ff5d2f13e859…d=GA1.2.1594867574.1516566392", 0 },
{ "If-Modified-Since", 0 },
{ "Fri, 05 Jan 2018 02:34:41 GMT", 0 },
{ "[Wed Feb 28 22:08:09 2018] [notice] Apache/2.2.34 (Unix) mod_ssl/2.2.34 OpenSSL/1.0.2g mod_qos/11.52 configured -- resuming normal operations", 0 },
{ "127.0.0.1 - - [28/Feb/2018:21:03:37 +0100] \"GET /console?action=inclimit&address=194.31.217.21&event=QS_Limit HTTP/1.1\" 200 52 \"-\" 0 - - - id=wWkYPUtmBQARFAABEAAAAAAX-yQgC5d2 - - #5230", 0 },
{ "2013/11/07 17:44:07 [error] 4640#0: *55 auth_token_module(014): request not authorized: invalid signature, client: 127.0.0.1, server: localhost, request: \"GET /app/index.html?req=1 HTTP/1.1\", host: \"127.0.0.1:8204\" 000000000002#IPhzBRn7cuuGdqBI7T4OSIjXx7JGliUokCk8dFIU9n0=", 0 },
{ "2010 12 04 20:46:45.118 dispatch IWWWauthCo 07148.4046314384 3-ERROR : AuthsessClient_1_0::execute: no valid 000000000002#5jYHrFBotkZwAs5EyfVQVgNZb3M=", 0 },
{ "2011-09-01 07:37:17,275 main org.apache.catalina.startup.Catalina INFO Server startup in 5770 ms 000000000002#LQ/h2UbJ2HzdZyf8BqnB7TB8LZM=", 0 },
{ "2010-04-14 20:18:37,464 | INFO | org.hibernate.cfg ::getInputStream:1081 resource: /hibernate.cfg.xml 000000000002#9lpZof9jvdMRrIebCM7rbKzJ7aY=", 0 },
{ "http://mod-qos.sourceforge.net/", 0 },
{ "Mozilla/5.0 (X11; Ubuntu; Linu…) Gecko/20100101 Firefox/57.0", 0 },
{ " Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? To die: to sleep; No more; and by a sleep to say we end The heart-ache and the thousand natural shocks That flesh is heir to, 'tis a consummation Devoutly to be wish'd.", 0 },
{ "To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? To die: to sleep; No more; and by a sleep to say we end The heart-ache and the thousand natural shocks That flesh is heir to, 'tis a consummation Devoutly to be wish'd. To die, to sleep; To sleep: perchance to dream: ay, there's the rub; For in that sleep of death what dreams may come When we have shuffled off this mortal coil, Must give us pause: there's the respect That makes calamity of so long life; For who would bear the whips and scorns of time, The oppressor's wrong, the proud man's contumely, The pangs of despised love, the law's delay, The insolence of office and the spurns That patient merit of the unworthy takes, When he himself might his quietus make With a bare bodkin? who would fardels bear, To grunt and sweat under a weary life, But that the dread of something after death, The undiscover'd country from whose bourn No traveller returns, puzzles the will And makes us rather bear those ills we have Than fly to others that we know not of? Thus conscience does make cowards of us all; And thus the native hue of resolution Is sicklied o'er with the pale cast of thought, And enterprises of great pith and moment With this regard their currents turn awry, And lose the name of action.--Soft you now! The fair Ophelia! Nymph, in thy orisonsBe all my sins remember'd.", 0 },
{ "{\n" \
" \"_to\": \"1.2.3.4:5678\",\n" \
" \"_line\": 63546230,\n" \
" \"profile_image_url\": \"http://a3.twimg.com/profile_images/852841481/Untitled_3_normal.jpg\",\n" \
" \"created_at\": \"Sat, 08 May 2010 21:46:23 +0000\",\n" \
" \"from_user\": \"pelchiie\",\n" \
" \"metadata\": {\n" \
" \"result_type\": \"recent\"\n" \
" },\n" \
" \"to_user_id\": null,\n" \
" \"text\": \"twitter is dead today.\",\n" \
" \"id\": 13630378882,\n" \
" \"from_user_id\": 12621761,\n" \
" \"geo\": null,\n" \
" \"iso_language_code\": \"en\",\n" \
" \"source\": \"<a href=\\\"http://twitter.com/\\\">web</a>\"\n" \
"}", 0 },
{ NULL, 0 }
};
int i;
FILE *file;
char readline[MAX_LINE];
const char *cmd = strrchr(argv[0], '/');
apr_pool_t *pool;
apr_table_t *rules;
long long start;
long long end;
struct timeval tv;
static char ver[80];
const char *filename = NULL;
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
apr_app_initialize(&argc, &argv, NULL);
apr_pool_create(&pool, NULL);
rules = apr_table_make(pool, 100);
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv,"-h") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
} else {
filename = *argv;
}
argc--;
argv++;
}
{
// init
qs_r_t *d = data;
while(d->string) {
d->len = strlen(d->string);
datalen += d->len;
d++;
}
}
if(filename == NULL) {
usage(cmd, 0);
}
file = fopen(filename, "r");
if(!file) {
fprintf(stderr, "ERROR, failed to open the log file '%s'\n", filename);
exit(1);
}
while(fgets(readline, MAX_LINE-1, file) != NULL) {
char *p;
int len = strlen(readline);
rule_t *rule = apr_pcalloc(pool, sizeof(rule_t));
while(len > 0 && readline[len] < 32) {
readline[len] = '\0';
len--;
}
if((strlen(readline) > 0) &&
(readline[0] != CR) &&
(readline[0] != LF)) {
p = readline;
rule->preg = apr_palloc(pool, sizeof(qs_regex_t));
if(qs_regcomp(rule->preg, p, PCRE2_DOTALL|PCRE2_CASELESS) != 0) {
printf("failed to compile pattern [%s]\n", p);
exit(1);
}
apr_pool_pre_cleanup_register(pool, rule->preg, qs_pregfree);
apr_table_addn(rules, apr_pstrdup(pool, p), (char *)rule);
}
}
{ // per rule
int k;
apr_table_entry_t *entry = (apr_table_entry_t *)apr_table_elts(rules)->elts;
for(k = 0; k < apr_table_elts(rules)->nelts; k++) {
rule_t* rule = (rule_t *)entry[k].val;
gettimeofday(&tv, NULL);
start = tv.tv_sec * 1000000 + tv.tv_usec;
for(i = 0; i < LOOPS; i++) {
qs_r_t *d = data;
while(d->string) {
qs_regexec_len(rule->preg, d->string, d->len, 0, NULL, 0);
d++;
}
}
gettimeofday(&tv, NULL);
end = tv.tv_sec * 1000000 + tv.tv_usec;
printf("%lld usec for %s\n", (end - start)/LOOPS, entry[k].key);
}
}
// all rules
gettimeofday(&tv, NULL);
start = tv.tv_sec * 1000000 + tv.tv_usec;
for(i = 0; i < LOOPS; i++) {
const qs_r_t *d = data;
while(d->string) {
int k;
apr_table_entry_t *entry = (apr_table_entry_t *)apr_table_elts(rules)->elts;
for(k = 0; k < apr_table_elts(rules)->nelts; k++) {
rule_t* rule = (rule_t *)entry[k].val;
qs_regexec_len(rule->preg, d->string, d->len, 0, NULL, 0);
}
d++;
}
}
gettimeofday(&tv, NULL);
end = tv.tv_sec * 1000000 + tv.tv_usec;
pcre2_config(PCRE2_CONFIG_VERSION, ver);
printf("match all rules (%d) against the test variables (%lu strings, %d characters) took: %lld usec (%s/PCRE %s)\n",
apr_table_elts(rules)->nelts,
sizeof(data)/sizeof(qs_r_t)-1,
datalen,
(end - start) / LOOPS,
revision, ver);
return 0;
}

522
tools/src/qsrotate.c Normal file
View file

@ -0,0 +1,522 @@
/**
* Utilities for the quality of service module mod_qos.
*
* qsrotate.c: Log rotation tool.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
static const char revision[] = "$Id: qsrotate.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <zlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include "qs_util.h"
#define HUGE_STR 1024
//yyyy-mm-dd<sp>hh-mm-ss<sp>
#define TME_STR_LEN 20
/* global variables used by main and support thread */
static int m_force_rotation = 0;
static time_t m_tLogEnd = 0;
static time_t m_tRotation = 86400; /* default are 24h */
static int m_nLogFD = -1;
static int m_generations = -1;
static mode_t m_mode = 0660;
static char *m_file_name = NULL;
static long m_messages = 0;
static char *m_cmd = NULL;
static int m_compress = 0;
static int m_stdout = 0;
static int m_timestamp = 0;
static char time_string[TME_STR_LEN];
static long m_counter = 0;
static long m_limit = 2147483648 - (128 * 1024);
static int m_offset = 0;
static int m_offset_enabled = 0;
static void usage(char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s - a log rotation tool (similar to Apache's rotatelogs).\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s -o <file> [-s <sec> [-t <hours>]] [-b <bytes>] [-f] [-z] [-g <num>] [-u <name>] [-m <mask>] [-p] [-d]\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "%s reads from stdin (piped log) and writes the data to the provided\n", cmd);
qs_man_print(man, "file rotating the file after the specified time.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " -o <file>\n");
if(man) printf("\n");
qs_man_print(man, " Output log file to write the data to (use an absolute path).\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -s <sec>\n");
if(man) printf("\n");
qs_man_print(man, " Rotation interval in seconds, default are 86400 seconds.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -t <hours>\n");
if(man) printf("\n");
qs_man_print(man, " Offset to UTC (enables also DST support), default is 0.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -b <bytes>\n");
if(man) printf("\n");
qs_man_print(man, " File size limitation (default/max. are %ld bytes, min. are 1048576 bytes).\n", m_limit);
if(man) printf("\n.TP\n");
qs_man_print(man, " -f\n");
if(man) printf("\n");
qs_man_print(man, " Forced log rotation at the specified interval even no data is written.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -z\n");
if(man) printf("\n");
qs_man_print(man, " Compress (gzip) the rotated file.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -g <num>\n");
if(man) printf("\n");
qs_man_print(man, " Generations (number of files to keep).\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -u <name>\n");
if(man) printf("\n");
qs_man_print(man, " Become another user, e.g. www-data.\n");
qs_man_print(man, " -m <mask>\n");
if(man) printf("\n");
qs_man_print(man, " File permission which is either 600, 640, 660 (default) or 664.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -p\n");
if(man) printf("\n");
qs_man_print(man, " Writes data also to stdout (for piped logging).\n");
qs_man_print(man, " -d\n");
if(man) printf("\n");
qs_man_print(man, " Line-by-line data reading prefixing every line with a timestamp.\n");
printf("\n");
if(man) {
printf(".SH EXAMPLE\n");
} else {
printf("Example:\n");
}
qs_man_println(man, " TransferLog \"|/usr/bin/%s -f -z -g 3 -o /var/log/apache/access.log -s 86400\"\n", cmd);
printf("\n");
qs_man_print(man, "The name of the rotated file will be /dest/filee.YYYYmmddHHMMSS\n");
qs_man_print(man, "where YYYYmmddHHMMSS is the system time at which the data has been\n");
qs_man_print(man, "rotated.\n");
printf("\n");
if(man) {
printf(".SH NOTE\n");
} else {
printf("Notes:\n");
}
qs_man_println(man, " - Each %s instance must use an individual file.\n", cmd);
qs_man_println(man, " - You may trigger a file rotation manually by sending the signal USR1\n");
qs_man_print(man, " to the process.\n");
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qsre(1), qsrespeed(1), qspng(1), qssign(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
static time_t get_now() {
time_t now = time(NULL);
if(m_offset_enabled) {
struct tm lcl = *localtime(&now);
if(lcl.tm_isdst) {
now += 3600;
}
now += m_offset;
}
return now;
}
static int openFile(const char *cmd, const char *file_name) {
int m_nLogFD = open(file_name, O_WRONLY | O_CREAT | O_APPEND, m_mode);
/* error while opening log file */
if(m_nLogFD < 0) {
fprintf(stderr,"[%s]: ERROR, failed to open file <%s>\n", cmd, file_name);
}
return m_nLogFD;
}
/**
* Compress method called by a child process (forked)
* used to compress the rotated file.
*
* @param cmd Command name (used when logging errors)
* @param arch Path to the file to compress. File gets renamed to <arch>.gz
*/
static void compressThread(const char *cmd, const char *arch) {
gzFile *outfp;
int infp;
char dest[HUGE_STR+20];
char buf[HUGE_STR];
int len;
snprintf(dest, sizeof(dest), "%s.gz", arch);
/* low prio */
if(nice(10) == -1) {
fprintf(stderr, "[%s]: WARNING, failed to change nice value: %s\n", cmd, strerror(errno));
}
if((infp = open(arch, O_RDONLY)) == -1) {
/* failed to open file, can't compress it */
fprintf(stderr,"[%s]: ERROR, could not open file for compression <%s>\n", cmd, arch);
return;
}
if((outfp = gzopen(dest,"wb")) == NULL) {
fprintf(stderr,"[%s]: ERROR, could not open file for compression <%s>\n", cmd, dest);
close(infp);
return;
}
chmod(dest, m_mode);
while((len = read(infp, buf, sizeof(buf))) > 0) {
gzwrite(outfp, buf, len);
}
gzclose(outfp);
close(infp);
/* done, delete the old file */
unlink(arch);
}
void sigchild(int signo) {
pid_t pid;
int stat;
while((pid=waitpid(-1,&stat,WNOHANG)) > 0) {
}
}
void writeTimestamp() {
time_t tm = time(NULL);
struct tm *ptr = localtime(&tm);
strftime(time_string, TME_STR_LEN, "%Y-%m-%d %H:%M:%S ", ptr);
write(m_nLogFD, time_string, TME_STR_LEN);
}
/**
* Rotates a file
*
* @param cmd Command name to be used in log messages
* @param now
* @param file_name Name of the file to rotate (rename)
* @param messages Number of lines/buffers which had been read
*/
static void rotate(const char *cmd, time_t now,
const char *file_name, long *messages) {
int rc;
char arch[HUGE_STR+20];
char tmb[20];
struct tm *ptr = localtime(&now);
strftime(tmb, sizeof(tmb), "%Y%m%d%H%M%S", ptr);
snprintf(arch, sizeof(arch), "%s.%s", file_name, tmb);
/* set next rotation time */
m_tLogEnd = ((now / m_tRotation) * m_tRotation) + m_tRotation;
// reset byte counter
m_counter = 0;
/* rename current file */
if(m_nLogFD >= 0) {
close(m_nLogFD);
rename(file_name, arch);
}
/* open new file */
m_nLogFD = openFile(cmd, file_name);
if(m_nLogFD < 0) {
/* opening a new file has failed!
try to reopen and clear the last file */
char msg[HUGE_STR];
snprintf(msg, sizeof(msg), "ERROR while writing to file, %ld messages lost\n", *messages);
fprintf(stderr,"[%s]: ERROR, while writing to file <%s>\n", cmd, file_name);
rename(arch, file_name);
m_nLogFD = openFile(cmd, file_name);
if(m_nLogFD > 0) {
rc = ftruncate(m_nLogFD, 0);
rc = write(m_nLogFD, msg, strlen(msg));
}
} else {
*messages = 0;
if(m_compress || (m_generations != -1)) {
signal(SIGCHLD,sigchild);
if(fork() == 0) {
if(m_compress) {
compressThread(cmd, arch);
}
if(m_generations != -1) {
qs_deleteOldFiles(file_name, m_generations);
}
exit(0);
}
}
}
}
/**
* Separate thread which initiates file rotation even no
* log data is written.
*
* @param argv (not used)
*/
static void *forcedRotationThread(void *argv) {
time_t now;
time_t n;
while(1) {
qs_csLock();
now = get_now();
if(now > m_tLogEnd) {
rotate(m_cmd, now, m_file_name, &m_messages);
}
qs_csUnLock();
now = get_now();
n = 1 + m_tLogEnd - now;
sleep(n);
}
return NULL;
}
void handle_signal1(int signal) {
rotate(m_cmd, get_now(), m_file_name, &m_messages);
return;
}
int main(int argc, char **argv) {
char *username = NULL;
int rc;
char *buf;
int nRead, nWrite;
time_t now;
struct stat st;
long sizeLimit = 0;
pthread_attr_t *tha = NULL;
pthread_t tid;
struct sigaction sa;
char *cmd = strrchr(argv[0], '/');
sa.sa_handler = &handle_signal1;
sa.sa_flags = SA_RESTART;
if(cmd == NULL) {
cmd = argv[0];
} else {
cmd++;
}
m_cmd = calloc(1, strlen(cmd)+1);
strcpy(m_cmd, cmd); // copy as we can't pass it when forking
while(argc >= 1) {
if(strcmp(*argv,"-o") == 0) {
if (--argc >= 1) {
m_file_name = *(++argv);
}
} else if(strcmp(*argv,"-u") == 0) {
if (--argc >= 1) {
username = *(++argv);
}
} else if(strcmp(*argv,"-s") == 0) {
if (--argc >= 1) {
m_tRotation = atoi(*(++argv));
}
} else if(strcmp(*argv,"-t") == 0) {
if (--argc >= 1) {
m_offset = atoi(*(++argv));
m_offset = m_offset * 3600;
m_offset_enabled = 1;
}
} else if(strcmp(*argv,"-g") == 0) {
if (--argc >= 1) {
m_generations = atoi(*(++argv));
}
} else if(strcmp(*argv,"-b") == 0) {
if (--argc >= 1) {
sizeLimit = atol(*(++argv));
}
} else if(strcmp(*argv,"-m") == 0) {
if (--argc >= 1) {
int mode = atoi(*(++argv));
if(mode == 600) {
m_mode = 0600;
} else if(mode == 640) {
m_mode = 0640;
} else if(mode == 660) {
m_mode = 0660;
} else if(mode == 664) {
m_mode = 0664;
}
}
} else if(strcmp(*argv,"-z") == 0) {
m_compress = 1;
} else if(strcmp(*argv,"-p") == 0) {
m_stdout = 1;
} else if(strcmp(*argv,"-d") == 0) {
m_timestamp = 1;
memset(time_string, 32, TME_STR_LEN);
} else if(strcmp(*argv,"-f") == 0) {
m_force_rotation = 1;
} else if(strcmp(*argv,"-h") == 0) {
usage(m_cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(m_cmd, 0);
} else if(strcmp(*argv,"-?") == 0) {
usage(m_cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(m_cmd, 1);
}
argc--;
argv++;
}
if(m_file_name == NULL) usage(m_cmd, 0);
if(sizeLimit > 0 && sizeLimit < m_limit && sizeLimit >= (1024 * 1024)) {
m_limit = sizeLimit;
} else if(sizeLimit > 0 && sizeLimit < (1024 * 1024)) {
m_limit = 1024 * 1024;
}
if(stat(m_file_name, &st) == 0) {
m_counter = st.st_size;
}
sigaction(SIGUSR1, &sa, NULL);
qs_setuid(username, m_cmd);
/* set next rotation time */
now = get_now();
m_tLogEnd = ((now / m_tRotation) * m_tRotation) + m_tRotation;
/* open file */
m_nLogFD = openFile(m_cmd, m_file_name);
if(m_nLogFD < 0) {
/* startup did not success */
exit(2);
}
if(m_force_rotation) {
qs_csInitLock();
pthread_create(&tid, tha, forcedRotationThread, NULL);
}
buf = calloc(1, MAX_LINE_BUFFER+1);
for(;;) {
if(m_timestamp) {
// low perf line-by-line read
if(fgets(buf, MAX_LINE_BUFFER, stdin) == NULL) {
exit(3);
} else {
nRead = strlen(buf);
if(m_force_rotation) {
qs_csLock(); // >@CTR1
}
m_counter += (nRead + TME_STR_LEN);
now = get_now();
writeTimestamp();
nWrite = write(m_nLogFD, buf, nRead);
}
} else {
// normal/fast buffer read/process
nRead = read(0, buf, MAX_LINE_BUFFER);
if(nRead == 0) exit(3);
if(nRead < 0) if(errno != EINTR) exit(4);
if(m_force_rotation) {
qs_csLock(); // >@CTR1
}
m_counter += nRead;
now = get_now();
/* write data if we have a file handle (else continue but drop log data,
re-try to open the file at next rotation time) */
if(m_nLogFD >= 0) {
do {
nWrite = write(m_nLogFD, buf, nRead);
if(m_stdout) {
printf("%.*s", nRead, buf);
}
} while (nWrite < 0 && errno == EINTR);
}
m_messages++;
if(nWrite != nRead) {
if(m_nLogFD >= 0) {
char msg[HUGE_STR];
snprintf(msg, sizeof(msg), "ERROR while writing to file, %ld messages lost\n", m_messages);
/* error while writing data, try to delete the old file and continue ... */
rc = ftruncate(m_nLogFD, 0);
rc = write(m_nLogFD, msg, strlen(msg));
m_messages = 0;
}
}
}
// end buffer or line read
if((now > m_tLogEnd) || (m_counter > m_limit)) {
/* rotate! */
rotate(m_cmd, now, m_file_name, &m_messages);
}
if(m_force_rotation) {
qs_csUnLock(); // <@CTR1
}
}
memset(buf, 0, MAX_LINE_BUFFER);
free(buf);
return 0;
}

799
tools/src/qssign.c Normal file
View file

@ -0,0 +1,799 @@
/**
* Utilities for the quality of service module mod_qos.
*
* qssign.c: Log data signing tool to ensure data integrity.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
static const char revision[] = "$Id: qssign.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <regex.h>
#include <signal.h>
/* openssl */
#include <openssl/evp.h>
#include <openssl/hmac.h>
/* apr/apr-util */
#define QS_USEAPR 1
#include <apr.h>
#include <apr_base64.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include <apr_thread_proc.h>
#include <apr_file_io.h>
#include <apr_time.h>
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "qs_util.h"
#include "qs_apo.h"
#define SEQDIG "12"
#define QS_END "qssign---end-of-data"
#define QS_START "qssign---------start"
static const char *m_start_fmt = "";
static const char *m_end_fmt = "";
static long m_nr = 1;
static int m_logend = 0;
static void (*m_end)(const char *, int) = NULL;
static int m_end_pos = 0;
static const char *m_sec = NULL;
static const EVP_MD *m_evp;
static qs_regex_t *m_filter = NULL;
typedef struct {
const char* start_fmt;
const char* end_fmt;
const char* pattern;
const char* test;
} qos_p_t;
#define severity "[A-Z]+"
static const qos_p_t pattern[] = {
{
"%s | INFO | "QS_START,
"%s | INFO | "QS_END,
"^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+[|][ ]+"severity"[ ]+[|][ ]+[a-zA-Z0-9]+",
"2010-04-14 20:18:37,464 | INFO | org.hibernate.cfg.Configuration"
},
{
"%s INFO "QS_START,
"%s INFO "QS_END,
"^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+"severity"[ ]+",
"2011-08-30 07:27:22,738 INFO loginId='test'"
},
{
"%s qssign start INFO "QS_START,
"%s qssign end INFO "QS_END,
"^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+[a-zA-Z0-9\\.-]+[ ]+[a-zA-Z0-9\\.-]+[ ]+"severity"[ ]+",
"2011-09-01 07:37:17,275 main org.apache.catalina.startup.Catalina INFO Server"
},
{
"%s INFO "QS_START,
"%s INFO "QS_END,
"^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+",
"2011-08-30 07:27:22,738 "
},
{ NULL, NULL, NULL }
};
/**
* Writes the signed log line to stdout.
*
* @param line Data to sign
* @param line_size Length of the data
* @param sec Secret
* @param sec_len Length of the secret
*/
static void qs_write(char *line, int line_size, const char *sec, int sec_len) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
HMAC_CTX hmac;
HMAC_CTX *hmac_p = &hmac;
#else
HMAC_CTX *hmac_p;
#endif
unsigned char data[HMAC_MAX_MD_CBLOCK];
unsigned int len;
char *m;
int data_len;
sprintf(&line[strlen(line)], " %."SEQDIG"ld", m_nr);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
HMAC_CTX_init(hmac_p);
#else
hmac_p = HMAC_CTX_new();
#endif
HMAC_Init_ex(hmac_p, sec, sec_len, m_evp, NULL);
HMAC_Update(hmac_p, (const unsigned char *)line, strlen(line));
HMAC_Final(hmac_p, data, &len);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
HMAC_CTX_cleanup(hmac_p);
#else
HMAC_CTX_free(hmac_p);
#endif
m = calloc(1, apr_base64_encode_len(len) + 1);
data_len = apr_base64_encode(m, (char *)data, len);
m[data_len] = '\0';
printf("%s#%s\n", line, m);
fflush(stdout);
free(m);
m_nr++;
return;
}
/*
* [Fri Dec 03 07:37:40 2010] [notice] .........
*/
static void qs_end_apache_err(const char *sec, int start) {
int sec_len = strlen(sec);
char line[MAX_LINE];
int dig = atoi(SEQDIG);
/* <data> ' ' <sequence number> '#' <hmac>*/
int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
char time_string[1024];
time_t tm = time(NULL);
struct tm *ptr = localtime(&tm);
strftime(time_string, sizeof(time_string), "%a %b %d %H:%M:%S %Y", ptr);
if(start) {
sprintf(line, "[%s] [notice] "QS_START, time_string);
} else {
sprintf(line, "[%s] [notice] "QS_END, time_string);
}
qs_write(line, line_size, sec, sec_len);
return;
}
/*
* 12.12.12.12 - - [03/Dec/2010:07:36:51 +0100] ...............
*/
static void qs_end_apache_acc(const char *sec, int start) {
int sec_len = strlen(sec);
char line[MAX_LINE];
int dig = atoi(SEQDIG);
/* <data> ' ' <sequence number> '#' <hmac>*/
int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
char time_string[1024];
time_t tm = time(NULL);
struct tm *ptr = localtime(&tm);
char sign;
int timz;
apr_time_exp_t xt;
apr_time_exp_lt(&xt, apr_time_now());
timz = xt.tm_gmtoff;
if(timz < 0) {
timz = -timz;
sign = '-';
} else {
sign = '+';
}
strftime(time_string, sizeof(time_string), "%d/%b/%Y:%H:%M:%S", ptr);
if(start) {
sprintf(line, "0.0.0.0 - - [%s %c%.2d%.2d] "QS_START, time_string, sign, timz / (60*60), (timz % (60*60)) / 60);
} else {
sprintf(line, "0.0.0.0 - - [%s %c%.2d%.2d] "QS_END, time_string, sign, timz / (60*60), (timz % (60*60)) / 60);
}
qs_write(line, line_size, sec, sec_len);
return;
}
/*
* 2010 12 03 17:00:30.425 qssign end 0.0 5-NOTICE: ..............
*/
static void qs_end_nj(const char *sec, int start) {
int sec_len = strlen(sec);
char line[MAX_LINE];
int dig = atoi(SEQDIG);
/* <data> ' ' <sequence number> '#' <hmac>*/
int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
char time_string[1024];
time_t tm = time(NULL);
struct tm *ptr = localtime(&tm);
char buf[1024];
int i;
for(i = 0; i < m_end_pos; i++) {
buf[i] = ' ';
}
buf[i] = '\0';
strftime(time_string, sizeof(time_string), "%Y %m %d %H:%M:%S.000", ptr);
if(start) {
sprintf(line, "%s qssign start 0.0%s 5-NOTICE: "QS_START, time_string, buf);
} else {
sprintf(line, "%s qssign end 0.0%s 5-NOTICE: "QS_END, time_string, buf);
}
qs_write(line, line_size, sec, sec_len);
return;
}
/*
* 2010-04-14 20:18:37,464 ... (using m_fmt)
*/
static void qs_end_lj(const char *sec, int start) {
int sec_len = strlen(sec);
char line[MAX_LINE];
int dig = atoi(SEQDIG);
/* <data> ' ' <sequence number> '#' <hmac>*/
int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
char time_string[1024];
time_t tm = time(NULL);
struct tm *ptr = localtime(&tm);
strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S,000", ptr);
if(start) {
sprintf(line, m_start_fmt, time_string);
} else {
sprintf(line, m_end_fmt, time_string);
}
qs_write(line, line_size, sec, sec_len);
return;
}
/*
* Dec 6 04:00:06 localhost kernel:
*/
static void qs_end_lx(const char *sec, int start) {
char hostname[1024];
int len = sizeof(hostname);
int sec_len = strlen(sec);
char line[MAX_LINE];
int dig = atoi(SEQDIG);
/* <data> ' ' <sequence number> '#' <hmac>*/
int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
char time_string[1024];
time_t tm = time(NULL);
struct tm *ptr = localtime(&tm);
strftime(time_string, sizeof(time_string), "%b %e %H:%M:%S", ptr);
if(gethostname(hostname, len) != 0) {
hostname[0] = '-';
hostname[1] = '\0';
}
if(start) {
sprintf(line, "%s %s qssign: "QS_START, time_string, hostname);
} else {
sprintf(line, "%s %s qssign: "QS_END, time_string, hostname);
}
qs_write(line, line_size, sec, sec_len);
return;
}
/*
* 2013/11/13 17:38:41 [error] 6577#0: *1 open()
*/
static void qs_end_ngx(const char *sec, int start) {
int sec_len = strlen(sec);
char line[MAX_LINE];
int dig = atoi(SEQDIG);
/* <data> ' ' <sequence number> '#' <hmac>*/
int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
char time_string[1024];
time_t tm = time(NULL);
struct tm *ptr = localtime(&tm);
strftime(time_string, sizeof(time_string), "%Y/%m/%d %H:%M:%S", ptr);
if(start) {
sprintf(line, "%s [notice] 0#0: "QS_END, time_string);
} else {
sprintf(line, "%s [notice] 0#0: "QS_END, time_string);
}
qs_write(line, line_size, sec, sec_len);
return;
}
void qs_signal_exit(int e) {
if(m_logend && (m_end != NULL)) {
m_end(m_sec, 0);
}
exit(0);
}
/**
* Tries to find out a suitable log line format which is used
* to log sign end messages (so let the verifier known, that the
* data ends nothing has been cut off).
*
* Sets the format to global variables.
*
* known pattern
* - [Fri Dec 03 07:37:40 2010] [notice] .........
* - 12.12.12.12 - - [03/Dec/2010:07:36:51 +0100] ...............
* - 2010 12 03 17:00:30.425 qssign end 0.0 5-NOTICE: ..............
* 46 <- var -> 63 71
* - Dec 6 04:00:06 localhost kernel:
* - some 2010-12-03 17:00:30,425 ...
*
* @param s
*/
static void qs_set_format(char *s) {
regex_t r_apache_err;
regex_t r_apache_acc;
regex_t r_nj;
regex_t r_lx;
regex_t r_ngx;
if(regcomp(&r_apache_err,
"^\\[[a-zA-Z]{3} [a-zA-Z]{3} [0-9]+ [0-9]+:[0-9]+:[0-9]+ [0-9]+\\] \\[[a-zA-Z]+\\] ",
REG_EXTENDED) != 0) {
fprintf(stderr, "failed to compile regex (err)\n");
exit(1);
}
if(regcomp(&r_apache_acc,
"^[0-9.]+ [a-zA-Z0-9\\@_\\.\\-]+ [a-zA-Z0-9\\@_\\.\\-]+ \\[[0-9]+/[a-zA-Z]{3}/[0-9:]+[0-9\\+ ]+\\] ",
REG_EXTENDED) != 0) {
fprintf(stderr, "failed to compile regex (acc)\n");
exit(1);
}
if(regcomp(&r_nj,
"^[0-9]{4} [0-9]{2} [0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} [a-zA-Z0-9]+[ ]+.*[A-Z]+[ ]*:",
REG_EXTENDED) != 0) {
fprintf(stderr, "failed to compile regex (nj)\n");
exit(1);
}
if(regcomp(&r_lx,
"^[a-zA-Z]{3}[ ]+[0-9]+[ ]+[0-9]{2}:[0-9]{2}:[0-9]{2}[ ]+[a-zA-Z0-9_\\.\\-]+[ ]+[a-zA-Z0-9_\\.\\-]+:",
REG_EXTENDED) != 0) {
fprintf(stderr, "failed to compile regex (lx)\n");
exit(1);
}
if(regcomp(&r_ngx,
"^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \\[[a-z]+\\] [0-9]+#[0-9]+: ",
REG_EXTENDED) != 0) {
fprintf(stderr, "failed to compile regex (ngx)\n");
exit(1);
}
if(regexec(&r_apache_err, s, 0, NULL, 0) == 0) {
m_end = &qs_end_apache_err;
} else if(regexec(&r_apache_acc, s, 0, NULL, 0) == 0) {
m_end = &qs_end_apache_acc;
} else if(regexec(&r_nj, s, 0, NULL, 0) == 0) {
char *dp = strstr(s, ": ");
if(dp) {
/* calculate the "var" size, see comment above */
m_end_pos = dp - s - 47 - 8 - 3;
if((m_end_pos < 0) || (m_end_pos > 1000)) {
m_end_pos = 0;
}
}
m_end = &qs_end_nj;
} else if(regexec(&r_lx, s, 0, NULL, 0) == 0) {
m_end = &qs_end_lx;
} else if(regexec(&r_ngx, s, 0, NULL, 0) == 0) {
m_end = &qs_end_ngx;
}
// search within the generic yyyy-mm-dd hh-mm-ss,mmm patterns
if(!m_end) {
const qos_p_t *p = pattern;
while(p->end_fmt) {
regex_t r_j;
if(regcomp(&r_j, p->pattern, REG_EXTENDED) != 0) {
fprintf(stderr, "failed to compile regex (%s)\n", p->pattern);
exit(1);
}
if(regexec(&r_j, s, 0, NULL, 0) == 0) {
m_start_fmt = p->start_fmt;
m_end_fmt = p->end_fmt;
m_end = &qs_end_lj;
break;
}
p++;
}
}
/* default (apache error log format) */
if(m_end == NULL) {
m_end = &qs_end_apache_err;
}
return;
}
/**
* Process the data from stdin.
*
* @param sec Passphrase
*/
static void qs_sign(const char *sec) {
int sec_len = strlen(sec);
char *line = calloc(1, MAX_LINE_BUFFER+1);
int dig = atoi(SEQDIG);
/* <data> ' ' <sequence number> '#' <hmac>*/
int line_size = MAX_LINE_BUFFER - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
int line_len;
while(fgets(line, MAX_LINE_BUFFER, stdin) != NULL) {
line_len = strlen(line) - 1;
while(line_len > 0) { // cut tailing CR/LF
if(line[line_len] >= ' ') {
break;
}
line[line_len] = '\0';
line_len--;
}
if(m_logend && (m_end == NULL)) {
qs_set_format(line);
m_end(m_sec, 1);
}
if(m_filter != NULL && qs_regexec_len(m_filter, line, line_len, 0, NULL, 0) >= 0) {
printf("%s\n", line);
fflush(stdout);
} else {
qs_write(line, line_size, sec, sec_len);
}
}
return;
}
static int isSpecialLine(const char *line, const char *marker) {
char *se_marker = strstr(line, marker);
if(se_marker != NULL) {
/* QS_END/START + " " + SEQDIG */
int sz = strlen(marker) + 1 + atoi(SEQDIG);
if(sz == (strlen(line) - (se_marker - line))) {
return 1;
}
}
return 0;
}
static long qs_verify(const char *sec) {
int end_seen = 0;
int sec_len = strlen(sec);
long err = 0; // errors
long lineNumber = 0; // line number of the file / input data
char *line = calloc(1, MAX_LINE_BUFFER+1);
int line_size = MAX_LINE_BUFFER;
int line_len;
m_nr = -1; // expected sequence number (start with any)
long nr_alt = -1; // alternatively expected sequence number (if a line was injected)
long nr_alt_lineNumber = -1;
long nr_usr1_lineNumber = -1; // we may have lines written by a prev. qssign binary (while graceful restart)
while(fgets(line, line_size, stdin) != NULL) {
int valid = 0;
long msgSeqNr = 0;
int isOldProcess = 0;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
HMAC_CTX hmac;
HMAC_CTX *hmac_p = &hmac;
#else
HMAC_CTX *hmac_p;
#endif
unsigned char data[HMAC_MAX_MD_CBLOCK];
unsigned int len;
char *m;
int data_len;
char *sig;
char *seq;
line_len = strlen(line) - 1;
while(line_len > 0) { // cut tailing CR/LF
if(line[line_len] >= ' ') {
break;
}
line[line_len] = '\0';
line_len--;
}
sig = strrchr(line, '#');
seq = strrchr(line, ' ');
lineNumber++;
if(seq && sig) {
sig[0] = '\0';
sig++;
/* verify hmac */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
HMAC_CTX_init(hmac_p);
#else
hmac_p = HMAC_CTX_new();
#endif
HMAC_Init_ex(hmac_p, sec, sec_len, m_evp, NULL);
HMAC_Update(hmac_p, (const unsigned char *)line, strlen(line));
HMAC_Final(hmac_p, data, &len);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
HMAC_CTX_cleanup(hmac_p);
#else
HMAC_CTX_free(hmac_p);
#endif
m = calloc(1, apr_base64_encode_len(len) + 1);
data_len = apr_base64_encode(m, (char *)data, len);
m[data_len] = '\0';
if(strcmp(m, sig) != 0) {
err++;
fprintf(stderr, "ERROR on line %ld: invalid signature\n", lineNumber);
/* message may be modified/corrupt or inserted: next line may have
the next sequence number (modified) or the same (inserted) */
nr_alt = m_nr + 1;
nr_alt_lineNumber = lineNumber + 1;
} else {
valid = 1;
}
free(m);
/* verify sequence */
seq++;
msgSeqNr = atol(seq);
if(msgSeqNr == 0) {
err++;
fprintf(stderr, "ERROR on line %ld: invalid sequence\n", lineNumber);
} else {
if(m_nr != -1) {
if(lineNumber == nr_alt_lineNumber) {
// last line was modified
if(m_nr != msgSeqNr) {
// and therefore, we also accept the next sequence number
m_nr = nr_alt;
}
nr_alt = -1;
nr_alt_lineNumber = -1;
}
if(valid && isSpecialLine(line, QS_START)) {
// new start line (graceful restart)
// we expect now msg number 1
// but still acept the old until we get the end marker
nr_usr1_lineNumber = m_nr;
m_nr = 1;
}
if(valid && nr_usr1_lineNumber == msgSeqNr) {
// msg from old process is okay...
nr_usr1_lineNumber++;
isOldProcess = 1;
} else {
if(m_nr != msgSeqNr) {
if(msgSeqNr == 1) {
if(!end_seen) {
err++;
fprintf(stderr, "ERROR on line %ld: wrong sequence, server restart? (expect %."SEQDIG"ld)\n",
lineNumber, m_nr);
}
} else {
err++;
fprintf(stderr, "ERROR on line %ld: wrong sequence (expect %."SEQDIG"ld)\n", lineNumber, m_nr);
}
} else {
// well done - this is the sequence number we expect
}
}
} else if(m_logend) {
// log should (if not rotated) start with message 1
if(msgSeqNr != 1) {
fprintf(stderr, "NOTICE: log starts with sequence %."SEQDIG"ld, log rotation?"
" (expect %."SEQDIG"d)\n", msgSeqNr, 1);
}
}
if(valid && !isOldProcess) {
// adjust
m_nr = msgSeqNr;
}
}
} else {
err++;
fprintf(stderr, "ERROR on line %ld: missing signature/sequence\n", lineNumber);
}
end_seen = 0;
if(valid) {
if(!isOldProcess) {
m_nr++;
}
if(isSpecialLine(line, QS_END)) {
if(nr_usr1_lineNumber == -1) {
end_seen = 1;
} else {
nr_usr1_lineNumber = -1; // no more messages from an old process
}
}
}
}
if(m_logend && !end_seen) {
fprintf(stderr, "NOTICE: no end marker seen, log rotation? (expect %."SEQDIG"ld)\n", m_nr);
}
return err;
}
static void usage(char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s - an utility to sign and verify the integrity of log data.\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s -s|S <secret> [-e] [-v] [-u <name>] [-f <regex>] [-a 'sha1'|'sha256']\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, "%s is a log data integrity check tool. It reads log data\n", cmd);
qs_man_print(man, "from stdin (pipe) and writes the data to stdout adding a sequence\n");
qs_man_print(man, "number and signature to ever log line.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " -s <secret>\n");
if(man) printf("\n");
qs_man_print(man, " Passphrase used to calculate signature.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -S <program>\n");
if(man) printf("\n");
qs_man_print(man, " Specifies a program which writes the passphrase to stdout.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -e\n");
if(man) printf("\n");
qs_man_print(man, " Writes start/end marker when starting/stopping data signing.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -v\n");
if(man) printf("\n");
qs_man_print(man, " Verification mode checking the integrity of signed data.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -u <name>\n");
if(man) printf("\n");
qs_man_print(man, " Becomes another user, e.g. www-data.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -f <regex>\n");
if(man) printf("\n");
qs_man_print(man, " Filter pattern (case sensitive regular expression) for messages\n");
qs_man_print(man, " which do not need to be signed.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -a 'sha1'|'sha256'\n");
if(man) printf("\n");
qs_man_print(man, " Specifies the algorithm to use. Default is sha1.\n");
printf("\n");
if(man) {
printf(".SH EXAMPLE\n");
printf("Sign:\n");
printf("\n");
} else {
printf("Example (sign):\n");
}
qs_man_println(man, " TransferLog \"|/usr/bin/%s -s password -e |/usr/bin/qsrotate -o /var/log/apache/access.log\"\n", cmd);
printf("\n");
if(man) {
printf("\n");
printf("Verify:\n");
printf("\n");
} else {
qs_man_print(man, "Example (verify):\n");
}
qs_man_println(man, " cat access.log | %s -s password -v\n", cmd);
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qstail(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
int main(int argc, const char * const argv[]) {
apr_pool_t *pool;
int verify = 0;
char *cmd = strrchr(argv[0], '/');
const char *username = NULL;
const char *filter = NULL;
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
apr_app_initialize(&argc, &argv, NULL);
apr_pool_create(&pool, NULL);
m_evp = EVP_sha1();
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv,"-s") == 0) {
if (--argc >= 1) {
m_sec = *(++argv);
}
} else if(strcmp(*argv,"-S") == 0) {
if (--argc >= 1) {
m_sec = qs_readpwd(pool, *(++argv));
}
} else if(strcmp(*argv,"-v") == 0) {
verify = 1;
} else if(strcmp(*argv,"-e") == 0) {
m_logend = 1;
} else if(strcmp(*argv,"-u") == 0) { /* switch user id */
if (--argc >= 1) {
username = *(++argv);
}
} else if(strcmp(*argv,"-f") == 0) { /* filter */
if (--argc >= 1) {
filter = *(++argv);
}
} else if(strcmp(*argv,"-a") == 0) { /* set alg */
if (--argc >= 1) {
const char *alg = *(++argv);
if(strcasecmp(alg, "SHA256") == 0) {
m_evp = EVP_sha256();
} else if(strcasecmp(alg, "SHA1") != 0) {
m_evp = NULL;
}
} else {
m_evp = NULL;
}
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
}
argc--;
argv++;
}
if(filter != NULL) {
m_filter = apr_palloc(pool, sizeof(qs_regex_t));
if(qs_regcomp(m_filter, filter, 0) != 0) {
fprintf(stderr, "failed to compile filter pattern <%s>\n", filter);
exit(1);
}
apr_pool_pre_cleanup_register(pool, m_filter, qs_pregfree);
}
if(m_evp == NULL) {
usage(cmd, 0);
}
if(m_sec == NULL) {
usage(cmd, 0);
}
qs_setuid(username, cmd);
if(verify) {
long err = qs_verify(m_sec);
if(err != 0) {
return 1;
}
} else {
if(m_logend) {
signal(SIGTERM, qs_signal_exit);
}
qs_sign(m_sec);
if(m_logend && (m_end != NULL)) {
m_end(m_sec, 0);
}
}
apr_pool_destroy(pool);
return 0;
}

237
tools/src/qstail.c Normal file
View file

@ -0,0 +1,237 @@
/* -*-mode: c; indent-tabs-mode: nil; c-basic-offset: 2; -*-
*/
/**
* Utilities for the quality of service module mod_qos.
*
* qstail.c: Shows the end of a log file beginning at the
* provided pattern.
*
* See http://mod-qos.sourceforge.net/ for further
* details.
*
* Copyright (C) 2023 Pascal Buchbinder
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
static const char revision[] = "$Id: qstail.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include "qs_util.h"
#define BUFFER 2048
static void usage(char *cmd, int man) {
if(man) {
//.TH [name of program] [section number] [center footer] [left footer] [center header]
printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
man_version, cmd);
}
printf("\n");
if(man) {
printf(".SH NAME\n");
}
qs_man_print(man, "%s - an utility printing the end of a log file"
" starting at the specified pattern.\n", cmd);
printf("\n");
if(man) {
printf(".SH SYNOPSIS\n");
}
qs_man_print(man, "%s%s -i <path> -p <pattern>\n", man ? "" : "Usage: ", cmd);
printf("\n");
if(man) {
printf(".SH DESCRIPTION\n");
} else {
printf("Summary\n");
}
qs_man_print(man, " %s shows the end of a log file beginning with the line containing the\n", cmd);
qs_man_print(man, " specified pattern. This may be used to show all lines which has been written\n");
qs_man_print(man, " after a certain event (e.g., server restart) or time stamp.\n");
printf("\n");
if(man) {
printf(".SH OPTIONS\n");
} else {
printf("Options\n");
}
if(man) printf(".TP\n");
qs_man_print(man, " -i <path>\n");
if(man) printf("\n");
qs_man_print(man, " Input file to read the data from.\n");
if(man) printf("\n.TP\n");
qs_man_print(man, " -p <pattern>\n");
if(man) printf("\n");
qs_man_print(man, " Search pattern (literal string).\n");
printf("\n");
if(man) {
printf(".SH SEE ALSO\n");
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1)\n");
printf(".SH AUTHOR\n");
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
} else {
printf("See http://mod-qos.sourceforge.net/ for further details.\n");
}
if(man) {
exit(0);
} else {
exit(1);
}
}
/* search the beginning of the line starting at the provided position */
static void qs_readline(long pos, FILE *f) {
size_t len;
long startpos = pos - BUFFER + 1;
long readlen = BUFFER;
char line[readlen + 1];
if(startpos < 0) {
// we are at the beginning of the file
startpos = 0;
readlen = pos + 1;
}
fseek(f, startpos, SEEK_SET);
len = fread(&line, 1, readlen, f);
if(len > 0) {
char *s = &line[len-1];
line[len] = '\0';
while((s >= line) && (s[0] != CR) && (s[0] != LF)) {
s--;
}
if((s[0] == CR) || (s[0] == LF)) {
s++;
}
printf("%s", s);
}
}
static int qs_tail(const char *cmd, FILE *f, const char *pattern) {
char *cont = NULL;
long search_win_len = (strlen(pattern) * 2) + 32;
char line[search_win_len + 10];
long pos = 0;
size_t len;
char *startpattern = NULL;
fseek(f, 0L, SEEK_END);
pos = ftell(f);
while(pos > search_win_len) {
int offset = 0;
pos = pos - (search_win_len/2);
fseek(f, pos, SEEK_SET);
len = fread(&line, 1, search_win_len, f);
if(len <= 0) {
/* pattern not found / reached end */
return 1;
}
line[len] = '\0';
if((startpattern = strstr(line, pattern)) != NULL) {
int containsend = 0;
char *s = startpattern;
char *end;
offset = startpattern - line;
/* search the beginning of the line */
while((s > line) && (s[0] != CR) && (s[0] != LF)) {
s--;
}
if((s[0] != CR) && (s[0] != LF)) {
// beginning of the line not in the buffer
qs_readline(pos, f);
}
s++;
end = startpattern;
/* search the end of the line */
while((offset < search_win_len) && end[0] && end[0] != CR && end[0] != LF) {
end++;
offset++;
}
/* print the line containing the pattern */
if((end[0] == CR) || (end[0] == LF)) {
end[0] = '\0';
printf("%s\n", s);
containsend = 1;
} else {
printf("%s", s);
}
fseek(f, pos + offset, SEEK_SET);
if(containsend) {
// skip the line at the current position
cont = fgets(line, sizeof(line), f);
} else {
cont = line;
}
if(cont) {
while(fgets(line, sizeof(line), f) != NULL) {
printf("%s", line);
}
}
return 0;
}
}
return 1;
}
int main(int argc, const char * const argv[]) {
FILE *f;
const char *filename = NULL;
const char *pattern = NULL;
char *cmd = strrchr(argv[0], '/');
int status = 0;
if(cmd == NULL) {
cmd = (char *)argv[0];
} else {
cmd++;
}
argc--;
argv++;
while(argc >= 1) {
if(strcmp(*argv,"-i") == 0) {
if (--argc >= 1) {
filename = *(++argv);
}
} else if(strcmp(*argv,"-p") == 0) {
if (--argc >= 1) {
pattern = *(++argv);
}
} else if(strcmp(*argv,"-?") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"-help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--help") == 0) {
usage(cmd, 0);
} else if(strcmp(*argv,"--man") == 0) {
usage(cmd, 1);
}
argc--;
argv++;
}
if(filename == NULL || pattern == NULL) {
usage(cmd, 0);
}
if((f = fopen(filename, "r")) == NULL) {
fprintf(stderr, "[%s]: ERROR, could not open file '%s'\n", cmd, filename);
exit(1);
}
status = qs_tail(cmd, f, pattern);
fclose(f);
return status;
}