1
0
Fork 0

Merging upstream version 11.76.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-03-24 14:50:13 +01:00
parent b1c252527e
commit bca5300afb
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
65 changed files with 413 additions and 7681 deletions

View file

@ -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

View file

@ -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 \

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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;
}

View file

@ -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 {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}