Merging upstream version 11.76.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
b1c252527e
commit
bca5300afb
65 changed files with 413 additions and 7681 deletions
|
@ -1,46 +1,13 @@
|
|||
# $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
|
||||
bin_PROGRAMS=qslog qsgeo qsrespeed qsre
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Makefile.in generated by automake 1.15 from Makefile.am.
|
||||
# Makefile.in generated by automake 1.16.3 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994-2014 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1994-2020 Free Software Foundation, Inc.
|
||||
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
|
@ -88,11 +88,8 @@ 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)
|
||||
bin_PROGRAMS = qslog$(EXEEXT) qsgeo$(EXEEXT) qsrespeed$(EXEEXT) \
|
||||
qsre$(EXEEXT)
|
||||
subdir = src
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
||||
|
@ -105,52 +102,18 @@ 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
|
||||
|
@ -165,7 +128,10 @@ 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__maybe_remake_depfiles = depfiles
|
||||
am__depfiles_remade = ./$(DEPDIR)/qs_util.Po ./$(DEPDIR)/qsgeo.Po \
|
||||
./$(DEPDIR)/qslog.Po ./$(DEPDIR)/qsre.Po \
|
||||
./$(DEPDIR)/qsrespeed.Po
|
||||
am__mv = mv -f
|
||||
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
|
@ -179,16 +145,10 @@ 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)
|
||||
SOURCES = $(qsgeo_SOURCES) $(qslog_SOURCES) $(qsre_SOURCES) \
|
||||
$(qsrespeed_SOURCES)
|
||||
DIST_SOURCES = $(qsgeo_SOURCES) $(qslog_SOURCES) $(qsre_SOURCES) \
|
||||
$(qsrespeed_SOURCES)
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
|
@ -303,45 +263,12 @@ 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
|
||||
|
||||
|
@ -369,8 +296,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
|||
*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);; \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
|
@ -424,46 +351,14 @@ uninstall-binPROGRAMS:
|
|||
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)
|
||||
|
@ -472,41 +367,23 @@ qsrespeed$(EXEEXT): $(qsrespeed_OBJECTS) $(qsrespeed_DEPENDENCIES) $(EXTRA_qsres
|
|||
@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@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qs_util.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsgeo.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qslog.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsre.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qsrespeed.Po@am__quote@ # am--include-marker
|
||||
|
||||
$(am__depfiles_remade):
|
||||
@$(MKDIR_P) $(@D)
|
||||
@echo '# dummy' >$@-t && $(am__mv) $@-t $@
|
||||
|
||||
am--depfiles: $(am__depfiles_remade)
|
||||
|
||||
.c.o:
|
||||
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||
|
@ -574,7 +451,10 @@ cscopelist-am: $(am__tagged_files)
|
|||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
distdir: $(BUILT_SOURCES)
|
||||
$(MAKE) $(AM_MAKEFLAGS) distdir-am
|
||||
|
||||
distdir-am: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
|
@ -646,7 +526,11 @@ clean: clean-am
|
|||
clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f ./$(DEPDIR)/qs_util.Po
|
||||
-rm -f ./$(DEPDIR)/qsgeo.Po
|
||||
-rm -f ./$(DEPDIR)/qslog.Po
|
||||
-rm -f ./$(DEPDIR)/qsre.Po
|
||||
-rm -f ./$(DEPDIR)/qsrespeed.Po
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-tags
|
||||
|
@ -692,7 +576,11 @@ install-ps-am:
|
|||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f ./$(DEPDIR)/qs_util.Po
|
||||
-rm -f ./$(DEPDIR)/qsgeo.Po
|
||||
-rm -f ./$(DEPDIR)/qslog.Po
|
||||
-rm -f ./$(DEPDIR)/qsre.Po
|
||||
-rm -f ./$(DEPDIR)/qsrespeed.Po
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
|
@ -712,7 +600,7 @@ uninstall-am: uninstall-binPROGRAMS
|
|||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
|
||||
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles 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 \
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* See http://mod-qos.sourceforge.net/ for further
|
||||
* details.
|
||||
*
|
||||
* Copyright (C) 2023 Pascal Buchbinder
|
||||
* Copyright (C) 2025 Pascal Buchbinder
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* 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
|
|
@ -4,7 +4,7 @@
|
|||
* See http://mod-qos.sourceforge.net/ for further
|
||||
* details.
|
||||
*
|
||||
* Copyright (C) 2023 Pascal Buchbinder
|
||||
* Copyright (C) 2025 Pascal Buchbinder
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* See http://mod-qos.sourceforge.net/ for further
|
||||
* details.
|
||||
*
|
||||
* Copyright (C) 2023 Pascal Buchbinder
|
||||
* Copyright (C) 2025 Pascal Buchbinder
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -29,8 +29,8 @@
|
|||
/* ----------------------------------
|
||||
* version info
|
||||
* ---------------------------------- */
|
||||
static const char man_version[] = "11.74";
|
||||
static const char man_date[] = "May 2023";
|
||||
static const char man_version[] = "11.76";
|
||||
static const char man_date[] = "January 2025";
|
||||
|
||||
/* ----------------------------------
|
||||
* definitions
|
||||
|
|
|
@ -1,413 +0,0 @@
|
|||
/**
|
||||
* 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
336
tools/src/qsdt.c
|
@ -1,336 +0,0 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
|
@ -1,376 +0,0 @@
|
|||
/* -*-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;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@
|
|||
* See http://mod-qos.sourceforge.net/ for further
|
||||
* details.
|
||||
*
|
||||
* Copyright (C) 2023 Pascal Buchbinder
|
||||
* Copyright (C) 2025 Pascal Buchbinder
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -239,7 +239,7 @@ static void usage(const char *cmd, int man) {
|
|||
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("qslog(1), qsre(1), qsrespeed(1)\n");
|
||||
printf(".SH AUTHOR\n");
|
||||
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
|
||||
} else {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* See http://mod-qos.sourceforge.net/ for further details.
|
||||
*
|
||||
* Copyright (C) 2023 Pascal Buchbinder
|
||||
* Copyright (C) 2025 Pascal Buchbinder
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
/* -*-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;
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
* See http://mod-qos.sourceforge.net/ for further
|
||||
* details.
|
||||
*
|
||||
* Copyright (C) 2023 Pascal Buchbinder
|
||||
* Copyright (C) 2025 Pascal Buchbinder
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -2247,7 +2247,7 @@ static void usage(const char *cmd, int man) {
|
|||
printf("\n");
|
||||
if(man) {
|
||||
printf(".SH SEE ALSO\n");
|
||||
printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qssign(1), qstail(1)\n");
|
||||
printf("qsgeo(1), qsre(1), qsrespeed(1)\n");
|
||||
printf(".SH AUTHOR\n");
|
||||
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
|
||||
} else {
|
||||
|
|
|
@ -1,423 +0,0 @@
|
|||
/* -*-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;
|
||||
}
|
|
@ -1,784 +0,0 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* See http://mod-qos.sourceforge.net/ for further
|
||||
* details.
|
||||
*
|
||||
* Copyright (C) 2023 Pascal Buchbinder
|
||||
* Copyright (C) 2025 Pascal Buchbinder
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -101,7 +101,7 @@ static void usage(const char *cmd, int man) {
|
|||
|
||||
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("qsgeo(1), qslog(1), qsrespeed(1)\n");
|
||||
printf(".SH AUTHOR\n");
|
||||
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
|
||||
} else {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* See http://mod-qos.sourceforge.net/ for further
|
||||
* details.
|
||||
*
|
||||
* Copyright (C) 2023 Pascal Buchbinder
|
||||
* Copyright (C) 2025 Pascal Buchbinder
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -102,7 +102,7 @@ static void usage(const char *cmd, int man) {
|
|||
|
||||
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("qsgeo(1), qslog(1), qsre(1)\n");
|
||||
printf(".SH AUTHOR\n");
|
||||
printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
|
||||
} else {
|
||||
|
|
|
@ -1,522 +0,0 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
|
@ -1,799 +0,0 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
/* -*-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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue