pax_global_header00006660000000000000000000000064141062236700014513gustar00rootroot0000000000000052 comment=fdd3002816aa5d44e7639a80600817e8a81080c3 proftpd-mod_prometheus-0.2/000077500000000000000000000000001410622367000160625ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/.gitattributes000066400000000000000000000000621410622367000207530ustar00rootroot00000000000000*.pl linguist-language=C *.pm linguist-language=C proftpd-mod_prometheus-0.2/.github/000077500000000000000000000000001410622367000174225ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/.github/workflows/000077500000000000000000000000001410622367000214575ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/.github/workflows/ci.yml000066400000000000000000000134541410622367000226040ustar00rootroot00000000000000name: CI on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ubuntu-latest strategy: matrix: compiler: - clang - gcc container: - alpine:3.14 - centos:8 - ubuntu:18.04 container: ${{ matrix.container }} steps: - name: Checkout ProFTPD uses: actions/checkout@v2 with: repository: proftpd/proftpd path: proftpd - name: Checkout module source code uses: actions/checkout@v2 with: path: proftpd/contrib/mod_prometheus - name: Whitespace check if: ${{ matrix.container == 'ubuntu:18.04' }} run: | apt-get update -qq apt-get install -y git cd proftpd/contrib/mod_prometheus if [[ -n $(git diff --check HEAD^) ]]; then echo "You must remove whitespace before submitting a pull request" echo "" git diff --check HEAD^ exit 1 fi - name: Install Alpine packages if: ${{ matrix.container == 'alpine:3.14' }} run: | apk update # for builds apk add bash build-base clang compiler-rt-static gcc make zlib-dev # for unit tests apk add check check-dev subunit subunit-dev # for libmicrohttpd support apk add libmicrohttpd-dev # for SQLite support apk add sqlite sqlite-dev # for OpenSSL support apk add openssl openssl-dev # for zlib support apk add zlib-dev # for debugging clang --version gcc --version openssl version -a - name: Install Centos packages if: ${{ matrix.container == 'centos:8' }} run: | # Need to add other repos for e.g. libsodium yum install -y dnf-plugins-core epel-release clang gcc make zlib-devel yum config-manager --set-enabled powertools # for unit tests yum install -y check-devel https://cbs.centos.org/kojifiles/packages/subunit/1.4.0/1.el8/x86_64/subunit-1.4.0-1.el8.x86_64.rpm https://cbs.centos.org/kojifiles/packages/subunit/1.4.0/1.el8/x86_64/subunit-devel-1.4.0-1.el8.x86_64.rpm # for libmicrohttpd support yum install -y libmicrohttpd-devel # for SQLite support yum install -y sqlite-devel # for OpenSSL support yum install -y openssl-devel # for zlib support yum install -y zlib-devel # for debugging clang --version gcc --version openssl version -a - name: Install Ubuntu packages if: ${{ matrix.container == 'ubuntu:18.04' }} run: | apt-get update -qq # for builds apt-get install -y clang gcc make # for unit tests apt-get install -y check libsubunit-dev # for libmicrohttpd support apt-get install -y libmicrohttpd-dev # for SQLite support apt-get install -y libsqlite3-dev sqlite3 # for OpenSSL support apt-get install -y libssl-dev # for zlib support apt-get install -y zlib1g-dev # for integration/regression test apt-get install -y \ libcompress-raw-zlib-perl \ libdata-dumper-simple-perl \ libdatetime-perl \ libfile-copy-recursive-perl \ libfile-path-tiny-perl \ libfile-spec-native-perl \ libnet-ssh2-perl \ libnet-ssleay-perl \ libnet-telnet-perl \ libposix-2008-perl \ libtest-unit-perl \ libtime-hr-perl \ libwww-perl PERL_MM_USE_DEFAULT=1 perl -MCPAN -e 'install Net::FTPSSL' # for test code coverage apt-get install -y lcov ruby gem install coveralls-lcov # for HTML validation apt-get install -y tidy # for debugging clang --version gcc --version openssl version -a - name: Prepare code coverage if: ${{ matrix.container == 'ubuntu:18.04' }} run: | lcov --directory proftpd --zerocounters - name: Build as static module env: CC: ${{ matrix.compiler }} run: | cd proftpd ./configure LIBS="-lm -lsubunit -lrt -pthread" --enable-devel=coverage --enable-tests --with-modules=mod_prometheus:mod_sftp:mod_tls make - name: Run unit tests env: CC: ${{ matrix.compiler }} # Note: Skip the unit tests on Alpine if: ${{ matrix.container != 'alpine:3.14' }} run: | cd proftpd/contrib/mod_prometheus make TEST_VERBOSE=1 check - name: Install as static module run: | cd proftpd make install - name: Run integration tests if: ${{ matrix.compiler == 'gcc' && matrix.container == 'ubuntu:18.04' }} env: PROFTPD_TEST_BIN: /usr/local/sbin/proftpd PROFTPD_TEST_DIR: ${{ github.workspace }}/proftpd run: | cd proftpd/contrib/mod_prometheus perl tests.pl - name: Build as shared module env: CC: ${{ matrix.compiler }} run: | cd proftpd make clean ./configure LIBS="-lm -lsubunit -lrt -pthread" --enable-devel --enable-dso --with-shared=mod_prometheus make - name: Install as shared module run: | cd proftpd make install - name: Check HTML docs if: ${{ matrix.container == 'ubuntu:18.04' }} run: | cd proftpd/contrib/mod_prometheus for f in $(/bin/ls *.html); do echo "Processing $f"; tidy -errors -omit -q $f; done || exit 0 proftpd-mod_prometheus-0.2/.gitignore000066400000000000000000000002371410622367000200540ustar00rootroot00000000000000configure Makefile config.log config.status autom4te.cache mod_prometheus.h tests.log t/api-tests t/api-tests.log .libs *.a *.sw? *.la *.lo *Tests*.log *.o *~ proftpd-mod_prometheus-0.2/Makefile.in000066400000000000000000000040261410622367000201310ustar00rootroot00000000000000top_builddir=../.. top_srcdir=../.. srcdir=@srcdir@ include $(top_srcdir)/Make.rules .SUFFIXES: .la .lo SHARED_CFLAGS=-DPR_SHARED_MODULE SHARED_LDFLAGS=-avoid-version -export-dynamic -module VPATH=@srcdir@ MODULE_LIBS=@MODULE_LIBS@ MODULE_NAME=mod_prometheus MODULE_OBJS=mod_prometheus.o \ lib/prometheus/db.o \ lib/prometheus/http.o \ lib/prometheus/metric.o \ lib/prometheus/metric/db.o \ lib/prometheus/registry.o \ lib/prometheus/text.o SHARED_MODULE_OBJS=mod_prometheus.lo \ lib/prometheus/db.lo \ lib/prometheus/http.lo \ lib/prometheus/metric.lo \ lib/prometheus/metric/db.lo \ lib/prometheus/registry.lo \ lib/prometheus/text.lo # Necessary redefinitions INCLUDES=-I. -I./include -I../.. -I../../include @INCLUDES@ CPPFLAGS= $(ADDL_CPPFLAGS) -DHAVE_CONFIG_H $(DEFAULT_PATHS) $(PLATFORM) $(INCLUDES) LDFLAGS=-L../../lib @LIBDIRS@ .c.o: $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ .c.lo: $(LIBTOOL) --mode=compile --tag=CC $(CC) $(CPPFLAGS) $(CFLAGS) $(SHARED_CFLAGS) -c $< -o $@ shared: $(SHARED_MODULE_OBJS) $(LIBTOOL) --mode=link --tag=CC $(CC) -o $(MODULE_NAME).la $(SHARED_MODULE_OBJS) -rpath $(LIBEXECDIR) $(LDFLAGS) $(SHARED_LDFLAGS) $(SHARED_MODULE_LIBS) `cat $(MODULE_NAME).c | grep '$$Libraries:' | sed -e 's/^.*\$$Libraries: \(.*\)\\$$/\1/'` static: $(MODULE_OBJS) test -z "$(MODULE_LIBS)" || echo "$(MODULE_LIBS)" >> $(MODULE_LIBS_FILE) $(AR) rc $(MODULE_NAME).a $(MODULE_OBJS) $(RANLIB) $(MODULE_NAME).a install: install-misc if [ -f $(MODULE_NAME).la ] ; then \ $(LIBTOOL) --mode=install --tag=CC $(INSTALL_BIN) $(MODULE_NAME).la $(DESTDIR)$(LIBEXECDIR) ; \ fi install-misc: clean: $(LIBTOOL) --mode=clean $(RM) $(MODULE_NAME).a $(MODULE_NAME).la *.o *.lo .libs/*.o lib/prometheus/*.o lib/prometheus/metric/*.o lib/prometheus/*.lo lib/prometheus/metric/*.lo cd t/ && $(MAKE) clean # Run the API tests check: test -z "$(ENABLE_TESTS)" || (cd t/ && $(MAKE) api-tests) distclean: clean $(RM) Makefile $(MODULE_NAME).h config.status config.cache config.log *.gcda *.gcno -$(RM) -r .libs/ .git/ CVS/ RCS/ proftpd-mod_prometheus-0.2/README.md000066400000000000000000000012411410622367000173370ustar00rootroot00000000000000proftpd-mod_prometheus ====================== Status ------ [![GitHub Actions CI Status](https://github.com/Castaglia/proftpd-mod_prometheus/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Castaglia/proftpd-mod_prometheus/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/license-GPL-brightgreen.svg)](https://img.shields.io/badge/license-GPL-brightgreen.svg) Synopsis -------- The `mod_prometheus` module for ProFTPD provides metrics via Prometheus. See the [mod_prometheus.html](https://htmlpreview.github.io/?https://github.com/Castaglia/proftpd-mod_prometheus/blob/master/mod_prometheus.html) documentation for more details. proftpd-mod_prometheus-0.2/config.guess000077500000000000000000001364711410622367000204160ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2020 Free Software Foundation, Inc. timestamp='2020-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2020 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c89 c99 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$driver" break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" # If ldd exists, use it to detect musl libc. if command -v ldd >/dev/null && \ ldd --version 2>&1 | grep -q ^musl then LIBC=musl fi ;; esac # Note: order is significant - the case branches are not exclusive. case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)` case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "$machine-${os}${release}${abi-}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; *:OS108:*:*) echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; *:Twizzler:*:*) echo "$UNAME_MACHINE"-unknown-twizzler exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ [ "$TARGET_BINARY_INTERFACE"x = x ] then echo m88k-dg-dgux"$UNAME_RELEASE" else echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` case "$UNAME_MACHINE" in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "$sc_kernel_bits" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if [ "$HP_ARCH" = "" ]; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ "$HP_ARCH" = hppa2.0w ] then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi else echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf fi exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) case "$UNAME_MACHINE" in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-pc-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; *:Minix:*:*) echo "$UNAME_MACHINE"-unknown-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-"$LIBC" exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk"$UNAME_RELEASE" exit ;; NSV-*:NONSTOP_KERNEL:*:*) echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. # shellcheck disable=SC2154 if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) echo "$UNAME_MACHINE"-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; *:Unleashed:*:*) echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" exit ;; esac # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: proftpd-mod_prometheus-0.2/config.sub000077500000000000000000000755711410622367000200640ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2020 Free Software Foundation, Inc. timestamp='2020-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2020 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Split fields of configuration type # shellcheck disable=SC2162 IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \ | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 os=$maybe_os ;; android-linux) basic_machine=$field1-unknown os=linux-android ;; *) basic_machine=$field1-$field2 os=$field3 ;; esac ;; *-*) # A lone config we happen to match not fitting any pattern case $field1-$field2 in decstation-3100) basic_machine=mips-dec os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ | unicom* | ibm* | next | hp | isi* | apollo | altos* \ | convergent* | ncr* | news | 32* | 3600* | 3100* \ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ | ultra | tti* | harris | dolphin | highlevel | gould \ | cbm | ns | masscomp | apple | axis | knuth | cray \ | microblaze* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 os= ;; *) basic_machine=$field1 os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc os=bsd ;; a29khif) basic_machine=a29k-amd os=udi ;; adobe68k) basic_machine=m68010-adobe os=scout ;; alliant) basic_machine=fx80-alliant os= ;; altos | altos3068) basic_machine=m68k-altos os= ;; am29k) basic_machine=a29k-none os=bsd ;; amdahl) basic_machine=580-amdahl os=sysv ;; amiga) basic_machine=m68k-unknown os= ;; amigaos | amigados) basic_machine=m68k-unknown os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=sysv4 ;; apollo68) basic_machine=m68k-apollo os=sysv ;; apollo68bsd) basic_machine=m68k-apollo os=bsd ;; aros) basic_machine=i386-pc os=aros ;; aux) basic_machine=m68k-apple os=aux ;; balance) basic_machine=ns32k-sequent os=dynix ;; blackfin) basic_machine=bfin-unknown os=linux ;; cegcc) basic_machine=arm-unknown os=cegcc ;; convex-c1) basic_machine=c1-convex os=bsd ;; convex-c2) basic_machine=c2-convex os=bsd ;; convex-c32) basic_machine=c32-convex os=bsd ;; convex-c34) basic_machine=c34-convex os=bsd ;; convex-c38) basic_machine=c38-convex os=bsd ;; cray) basic_machine=j90-cray os=unicos ;; crds | unos) basic_machine=m68k-crds os= ;; da30) basic_machine=m68k-da30 os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec os= ;; delta88) basic_machine=m88k-motorola os=sysv3 ;; dicos) basic_machine=i686-pc os=dicos ;; djgpp) basic_machine=i586-pc os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=ose ;; gmicro) basic_machine=tron-gmicro os=sysv ;; go32) basic_machine=i386-pc os=go32 ;; h8300hms) basic_machine=h8300-hitachi os=hms ;; h8300xray) basic_machine=h8300-hitachi os=xray ;; h8500hms) basic_machine=h8500-hitachi os=hms ;; harris) basic_machine=m88k-harris os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp os=hpux ;; hp300bsd) basic_machine=m68k-hp os=bsd ;; hppaosf) basic_machine=hppa1.1-hp os=osf ;; hppro) basic_machine=hppa1.1-hp os=proelf ;; i386mach) basic_machine=i386-mach os=mach ;; isi68 | isi) basic_machine=m68k-isi os=sysv ;; m68knommu) basic_machine=m68k-unknown os=linux ;; magnum | m3230) basic_machine=mips-mips os=sysv ;; merlin) basic_machine=ns32k-utek os=sysv ;; mingw64) basic_machine=x86_64-pc os=mingw64 ;; mingw32) basic_machine=i686-pc os=mingw32 ;; mingw32ce) basic_machine=arm-unknown os=mingw32ce ;; monitor) basic_machine=m68k-rom68k os=coff ;; morphos) basic_machine=powerpc-unknown os=morphos ;; moxiebox) basic_machine=moxie-unknown os=moxiebox ;; msdos) basic_machine=i386-pc os=msdos ;; msys) basic_machine=i686-pc os=msys ;; mvs) basic_machine=i370-ibm os=mvs ;; nacl) basic_machine=le32-unknown os=nacl ;; ncr3000) basic_machine=i486-ncr os=sysv4 ;; netbsd386) basic_machine=i386-pc os=netbsd ;; netwinder) basic_machine=armv4l-rebel os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=newsos ;; news1000) basic_machine=m68030-sony os=newsos ;; necv70) basic_machine=v70-nec os=sysv ;; nh3000) basic_machine=m68k-harris os=cxux ;; nh[45]000) basic_machine=m88k-harris os=cxux ;; nindy960) basic_machine=i960-intel os=nindy ;; mon960) basic_machine=i960-intel os=mon960 ;; nonstopux) basic_machine=mips-compaq os=nonstopux ;; os400) basic_machine=powerpc-ibm os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=ose ;; os68k) basic_machine=m68k-none os=os68k ;; paragon) basic_machine=i860-intel os=osf ;; parisc) basic_machine=hppa-unknown os=linux ;; pw32) basic_machine=i586-unknown os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=rdos ;; rdos32) basic_machine=i386-pc os=rdos ;; rom68k) basic_machine=m68k-rom68k os=coff ;; sa29200) basic_machine=a29k-amd os=udi ;; sei) basic_machine=mips-sei os=seiux ;; sequent) basic_machine=i386-sequent os= ;; sps7) basic_machine=m68k-bull os=sysv2 ;; st2000) basic_machine=m68k-tandem os= ;; stratus) basic_machine=i860-stratus os=sysv4 ;; sun2) basic_machine=m68000-sun os= ;; sun2os3) basic_machine=m68000-sun os=sunos3 ;; sun2os4) basic_machine=m68000-sun os=sunos4 ;; sun3) basic_machine=m68k-sun os= ;; sun3os3) basic_machine=m68k-sun os=sunos3 ;; sun3os4) basic_machine=m68k-sun os=sunos4 ;; sun4) basic_machine=sparc-sun os= ;; sun4os3) basic_machine=sparc-sun os=sunos3 ;; sun4os4) basic_machine=sparc-sun os=sunos4 ;; sun4sol2) basic_machine=sparc-sun os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun os= ;; sv1) basic_machine=sv1-cray os=unicos ;; symmetry) basic_machine=i386-sequent os=dynix ;; t3e) basic_machine=alphaev5-cray os=unicos ;; t90) basic_machine=t90-cray os=unicos ;; toad1) basic_machine=pdp10-xkl os=tops20 ;; tpf) basic_machine=s390x-ibm os=tpf ;; udi29k) basic_machine=a29k-amd os=udi ;; ultra3) basic_machine=a29k-nyu os=sym1 ;; v810 | necv810) basic_machine=v810-nec os=none ;; vaxv) basic_machine=vax-dec os=sysv ;; vms) basic_machine=vax-dec os=vms ;; vsta) basic_machine=i386-pc os=vsta ;; vxworks960) basic_machine=i960-wrs os=vxworks ;; vxworks68) basic_machine=m68k-wrs os=vxworks ;; vxworks29k) basic_machine=a29k-wrs os=vxworks ;; xbox) basic_machine=i686-pc os=mingw32 ;; ymp) basic_machine=ymp-cray os=unicos ;; *) basic_machine=$1 os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) cpu=m68k vendor=motorola ;; dpx2*) cpu=m68k vendor=bull os=sysv3 ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi os=${os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray os=${os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $os in irix*) ;; *) os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony os=newsos ;; next | m*-next) cpu=m68k vendor=next case $os in openstep*) ;; nextstep*) ;; ns2*) os=nextstep2 ;; *) os=nextstep3 ;; esac ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde os=${os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) # shellcheck disable=SC2162 IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x$os != x ] then case $os in # First match some system type aliases that might get confused # with valid system types. # solaris* is a basic system type, with this one exception. auroraux) os=auroraux ;; bluegene*) os=cnk ;; solaris1 | solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; solaris) os=solaris2 ;; unixware*) os=sysv4.2uw ;; gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # es1800 is here to avoid being matched by es* (a different OS) es1800*) os=ose ;; # Some version numbers need modification chorusos*) os=chorusos ;; isc) os=isc2.2 ;; sco6) os=sco5v6 ;; sco5) os=sco3.2v5 ;; sco4) os=sco3.2v4 ;; sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` ;; sco3.2v[4-9]* | sco5v6*) # Don't forget version if it is 3.2v4 or newer. ;; scout) # Don't match below ;; sco*) os=sco3.2v2 ;; psos*) os=psos ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. # sysv* is not here because it comes later, after sysvr4. gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | kopensolaris* | plan9* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ | knetbsd* | mirbsd* | netbsd* \ | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \ | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \ | chorusrdb* | cegcc* | glidix* \ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \ | linux-newlib* | linux-musl* | linux-uclibc* \ | uxpv* | beos* | mpeix* | udk* | moxiebox* \ | interix* | uwin* | mks* | rhapsody* | darwin* \ | openstep* | oskit* | conix* | pw32* | nonstopux* \ | storm-chaos* | tops10* | tenex* | tops20* | its* \ | os2* | vos* | palmos* | uclinux* | nucleus* \ | morphos* | superux* | rtmk* | windiss* \ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ | skyos* | haiku* | rdos* | toppers* | drops* | es* \ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ | nsk* | powerunix) # Remember, each alternative MUST END IN *, to match a version number. ;; qnx*) case $cpu in x86 | i*86) ;; *) os=nto-$os ;; esac ;; hiux*) os=hiuxwe2 ;; nto-qnx*) ;; nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; sim | xray | os68k* | v88r* \ | windows* | osx | abug | netware* | os9* \ | macos* | mpw* | magic* | mmixware* | mon960* | lnews*) ;; linux-dietlibc) os=linux-dietlibc ;; linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; lynx*178) os=lynxos178 ;; lynx*5) os=lynxos5 ;; lynx*) os=lynxos ;; mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; opened*) os=openedition ;; os400*) os=os400 ;; sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; wince*) os=wince ;; utek*) os=bsd ;; dynix*) os=bsd ;; acis*) os=aos ;; atheos*) os=atheos ;; syllable*) os=syllable ;; 386bsd) os=bsd ;; ctix* | uts*) os=sysv ;; nova*) os=rtmk-nova ;; ns2) os=nextstep2 ;; # Preserve the version number of sinix5. sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; sinix*) os=sysv4 ;; tpf*) os=tpf ;; triton*) os=sysv3 ;; oss*) os=sysv3 ;; svr4*) os=sysv4 ;; svr3) os=sysv3 ;; sysvr4) os=sysv4 ;; # This must come after sysvr4. sysv*) ;; ose*) os=ose ;; *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) os=mint ;; zvmoe) os=zvmoe ;; dicos*) os=dicos ;; pikeos*) # Until real need of OS specific support for # particular features comes up, bare metal # configurations are quite functional. case $cpu in arm*) os=eabi ;; *) os=elf ;; esac ;; nacl*) ;; ios) ;; none) ;; *-eabi) ;; *) echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $cpu-$vendor in score-*) os=elf ;; spu-*) os=elf ;; *-acorn) os=riscix1.2 ;; arm*-rebel) os=linux ;; arm*-semi) os=aout ;; c4x-* | tic4x-*) os=coff ;; c8051-*) os=elf ;; clipper-intergraph) os=clix ;; hexagon-*) os=elf ;; tic54x-*) os=coff ;; tic55x-*) os=coff ;; tic6x-*) os=coff ;; # This must come before the *-dec entry. pdp10-*) os=tops20 ;; pdp11-*) os=none ;; *-dec | vax-*) os=ultrix4.2 ;; m68*-apollo) os=domain ;; i386-sun) os=sunos4.0.2 ;; m68000-sun) os=sunos3 ;; m68*-cisco) os=aout ;; mep-*) os=elf ;; mips*-cisco) os=elf ;; mips*-*) os=elf ;; or32-*) os=coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=sysv3 ;; sparc-* | *-sun) os=sunos4.1.1 ;; pru-*) os=elf ;; *-be) os=beos ;; *-ibm) os=aix ;; *-knuth) os=mmixware ;; *-wec) os=proelf ;; *-winbond) os=proelf ;; *-oki) os=proelf ;; *-hp) os=hpux ;; *-hitachi) os=hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=sysv ;; *-cbm) os=amigaos ;; *-dg) os=dgux ;; *-dolphin) os=sysv3 ;; m68k-ccur) os=rtu ;; m88k-omron*) os=luna ;; *-next) os=nextstep ;; *-sequent) os=ptx ;; *-crds) os=unos ;; *-ns) os=genix ;; i370-*) os=mvs ;; *-gould) os=sysv ;; *-highlevel) os=bsd ;; *-encore) os=bsd ;; *-sgi) os=irix ;; *-siemens) os=sysv4 ;; *-masscomp) os=rtu ;; f30[01]-fujitsu | f700-fujitsu) os=uxpv ;; *-rom68k) os=coff ;; *-*bug) os=coff ;; *-apple) os=macos ;; *-atari*) os=mint ;; *-wrs) os=vxworks ;; *) os=none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $os in riscix*) vendor=acorn ;; sunos*) vendor=sun ;; cnk*|-aix*) vendor=ibm ;; beos*) vendor=be ;; hpux*) vendor=hp ;; mpeix*) vendor=hp ;; hiux*) vendor=hitachi ;; unos*) vendor=crds ;; dgux*) vendor=dg ;; luna*) vendor=omron ;; genix*) vendor=ns ;; clix*) vendor=intergraph ;; mvs* | opened*) vendor=ibm ;; os400*) vendor=ibm ;; ptx*) vendor=sequent ;; tpf*) vendor=ibm ;; vxsim* | vxworks* | windiss*) vendor=wrs ;; aux*) vendor=apple ;; hms*) vendor=hitachi ;; mpw* | macos*) vendor=apple ;; *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) vendor=atari ;; vos*) vendor=stratus ;; esac ;; esac echo "$cpu-$vendor-$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: proftpd-mod_prometheus-0.2/configure000077500000000000000000004447631410622367000200130ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= PACKAGE_URL= ac_unique_file="./mod_prometheus.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS MODULE_LIBS LIBDIRS INCLUDES SET_MAKE EGREP GREP CPP OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_os target_vendor target_cpu target host_os host_vendor host_cpu host build_os build_vendor build_cpu build target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_includes with_libraries ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-includes=LIST add additional include paths to proftpd. LIST is a colon-separated list of include paths to add e.g. --with-includes=/some/mysql/include:/my/include --with-libraries=LIST add additional library paths to proftpd. LIST is a colon-separated list of include paths to add e.g. --with-libraries=/some/mysql/libdir:/my/libs Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 $as_echo_n "checking target system type... " >&6; } if ${ac_cv_target+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$target_alias" = x; then ac_cv_target=$ac_cv_host else ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 $as_echo "$ac_cv_target" >&6; } case $ac_cv_target in *-*-*) ;; *) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; esac target=$ac_cv_target ac_save_IFS=$IFS; IFS='-' set x $ac_cv_target shift target_cpu=$1 target_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: target_os=$* IFS=$ac_save_IFS case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- ostype=`echo $build_os | sed 's/\..*$//g' | sed 's/-.*//g' | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ` ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" if test "x$ac_cv_header_minix_config_h" = xyes; then : MINIX=yes else MINIX= fi if test "$MINIX" = yes; then $as_echo "#define _POSIX_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h $as_echo "#define _MINIX 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 $as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } if ${ac_cv_safe_to_define___extensions__+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define __EXTENSIONS__ 1 $ac_includes_default int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_define___extensions__=yes else ac_cv_safe_to_define___extensions__=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 $as_echo "$ac_cv_safe_to_define___extensions__" >&6; } test $ac_cv_safe_to_define___extensions__ = yes && $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h $as_echo "#define _ALL_SOURCE 1" >>confdefs.h $as_echo "#define _GNU_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing strerror" >&5 $as_echo_n "checking for library containing strerror... " >&6; } if ${ac_cv_search_strerror+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char strerror (); int main () { return strerror (); ; return 0; } _ACEOF for ac_lib in '' cposix; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_strerror=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_strerror+:} false; then : break fi done if ${ac_cv_search_strerror+:} false; then : else ac_cv_search_strerror=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_strerror" >&5 $as_echo "$ac_cv_search_strerror" >&6; } ac_res=$ac_cv_search_strerror if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi # Check whether --with-includes was given. if test "${with_includes+set}" = set; then : withval=$with_includes; ac_addl_includes=`echo "$withval" | sed -e 's/:/ /g'` ; for ainclude in $ac_addl_includes; do if test x"$ac_build_addl_includes" = x ; then ac_build_addl_includes="-I$ainclude" else ac_build_addl_includes="-I$ainclude $ac_build_addl_includes" fi done CPPFLAGS="$CPPFLAGS $ac_build_addl_includes" fi # Check whether --with-libraries was given. if test "${with_libraries+set}" = set; then : withval=$with_libraries; ac_addl_libdirs=`echo "$withval" | sed -e 's/:/ /g'` ; for alibdir in $ac_addl_libdirs; do if test x"$ac_build_addl_libdirs" = x ; then ac_build_addl_libdirs="-L$alibdir" else ac_build_addl_libdirs="-L$alibdir $ac_build_addl_libdirs" fi done LDFLAGS="$LDFLAGS $ac_build_addl_libdirs" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi for ac_header in microhttpd.h sqlite3.h stdlib.h unistd.h limits.h fcntl.h sys/sysctl.h sys/sysinfo.h zlib.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in sysctl sysinfo do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done # Check for SQLite-isms { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_stmt_readonly" >&5 $as_echo_n "checking for sqlite3_stmt_readonly... " >&6; } saved_libs="$LIBS" LIBS="-lsqlite3" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_SQLITE3_H # include #endif int main () { (void) sqlite3_stmt_readonly(NULL); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define HAVE_SQLITE3_STMT_READONLY 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$saved_libs" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_trace" >&5 $as_echo_n "checking for sqlite3_trace... " >&6; } saved_libs="$LIBS" LIBS="-lsqlite3" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_SQLITE3_H # include #endif int main () { (void) sqlite3_trace(NULL, NULL, NULL); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define HAVE_SQLITE3_TRACE 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$saved_libs" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_trace_v2" >&5 $as_echo_n "checking for sqlite3_trace_v2... " >&6; } saved_libs="$LIBS" LIBS="-lsqlite3" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_SQLITE3_H # include #endif int main () { (void) sqlite3_trace_v2(NULL, 0, NULL, NULL); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define HAVE_SQLITE3_TRACE_V2 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$saved_libs" ac_compression_libs='' saved_libs="$LIBS" LIBS="$LIBS -lz" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gzip in libz" >&5 $as_echo_n "checking for gzip in libz... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_ZLIB_H # include #endif int main () { int res; res = deflateSetHeader(NULL, NULL); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ac_compression_libs="-lz" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$saved_libs" INCLUDES="$ac_build_addl_includes" LIBDIRS="$ac_build_addl_libdirs" MODULE_LIBS="$ac_compression_libs" ac_config_headers="$ac_config_headers mod_prometheus.h" ac_config_files="$ac_config_files t/Makefile Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by $as_me, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "mod_prometheus.h") CONFIG_HEADERS="$CONFIG_HEADERS mod_prometheus.h" ;; "t/Makefile") CONFIG_FILES="$CONFIG_FILES t/Makefile" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi proftpd-mod_prometheus-0.2/configure.in000066400000000000000000000106131410622367000203740ustar00rootroot00000000000000dnl ProFTPD - mod_prometheus dnl Copyright (c) 2021 TJ Saunders dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. dnl dnl Process this file with autoconf to produce a configure script. AC_INIT(./mod_prometheus.c) AC_CANONICAL_SYSTEM ostype=`echo $build_os | sed 's/\..*$//g' | sed 's/-.*//g' | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ` AC_PROG_CC AC_PROG_CPP AC_AIX AC_ISC_POSIX AC_MINIX AC_PROG_MAKE_SET dnl Need to support/handle the --with-includes and --with-libraries options AC_ARG_WITH(includes, [AC_HELP_STRING( [--with-includes=LIST], [add additional include paths to proftpd. LIST is a colon-separated list of include paths to add e.g. --with-includes=/some/mysql/include:/my/include]) ], [ ac_addl_includes=`echo "$withval" | sed -e 's/:/ /g'` ; for ainclude in $ac_addl_includes; do if test x"$ac_build_addl_includes" = x ; then ac_build_addl_includes="-I$ainclude" else ac_build_addl_includes="-I$ainclude $ac_build_addl_includes" fi done CPPFLAGS="$CPPFLAGS $ac_build_addl_includes" ]) AC_ARG_WITH(libraries, [AC_HELP_STRING( [--with-libraries=LIST], [add additional library paths to proftpd. LIST is a colon-separated list of include paths to add e.g. --with-libraries=/some/mysql/libdir:/my/libs]) ], [ ac_addl_libdirs=`echo "$withval" | sed -e 's/:/ /g'` ; for alibdir in $ac_addl_libdirs; do if test x"$ac_build_addl_libdirs" = x ; then ac_build_addl_libdirs="-L$alibdir" else ac_build_addl_libdirs="-L$alibdir $ac_build_addl_libdirs" fi done LDFLAGS="$LDFLAGS $ac_build_addl_libdirs" ]) AC_HEADER_STDC AC_CHECK_HEADERS(microhttpd.h sqlite3.h stdlib.h unistd.h limits.h fcntl.h sys/sysctl.h sys/sysinfo.h zlib.h) AC_CHECK_FUNCS(sysctl sysinfo) # Check for SQLite-isms AC_MSG_CHECKING([for sqlite3_stmt_readonly]) saved_libs="$LIBS" LIBS="-lsqlite3" AC_TRY_LINK([ #include #include #ifdef HAVE_SQLITE3_H # include #endif ], [ (void) sqlite3_stmt_readonly(NULL); ], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_SQLITE3_STMT_READONLY, 1, [Define if you have the sqlite3_stmt_readonly function]) ], [ AC_MSG_RESULT(no) ] ) LIBS="$saved_libs" AC_MSG_CHECKING([for sqlite3_trace]) saved_libs="$LIBS" LIBS="-lsqlite3" AC_TRY_LINK([ #include #include #ifdef HAVE_SQLITE3_H # include #endif ], [ (void) sqlite3_trace(NULL, NULL, NULL); ], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_SQLITE3_TRACE, 1, [Define if you have the sqlite3_trace function]) ], [ AC_MSG_RESULT(no) ] ) LIBS="$saved_libs" AC_MSG_CHECKING([for sqlite3_trace_v2]) saved_libs="$LIBS" LIBS="-lsqlite3" AC_TRY_LINK([ #include #include #ifdef HAVE_SQLITE3_H # include #endif ], [ (void) sqlite3_trace_v2(NULL, 0, NULL, NULL); ], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_SQLITE3_TRACE_V2, 1, [Define if you have the sqlite3_trace_v2 function]) ], [ AC_MSG_RESULT(no) ] ) LIBS="$saved_libs" ac_compression_libs='' saved_libs="$LIBS" LIBS="$LIBS -lz" AC_MSG_CHECKING([for gzip in libz]) AC_TRY_LINK( [ #include #include #ifdef HAVE_ZLIB_H # include #endif ], [ int res; res = deflateSetHeader(NULL, NULL); ], [ AC_MSG_RESULT(yes) ac_compression_libs="-lz" ], [ AC_MSG_RESULT(no) ] ) LIBS="$saved_libs" INCLUDES="$ac_build_addl_includes" LIBDIRS="$ac_build_addl_libdirs" MODULE_LIBS="$ac_compression_libs" AC_SUBST(INCLUDES) AC_SUBST(LDFLAGS) AC_SUBST(LIBDIRS) AC_SUBST(MODULE_LIBS) AC_CONFIG_HEADER(mod_prometheus.h) AC_OUTPUT( t/Makefile Makefile ) proftpd-mod_prometheus-0.2/include/000077500000000000000000000000001410622367000175055ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/include/prometheus/000077500000000000000000000000001410622367000217005ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/include/prometheus/db.h000066400000000000000000000074121410622367000224420ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus database API * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #ifndef MOD_PROMETHEUS_DB_H #define MOD_PROMETHEUS_DB_H #include "mod_prometheus.h" struct prom_dbh; int prom_db_init(pool *p); int prom_db_free(void); /* Create/prepare the database (with the given schema name) at the given path */ struct prom_dbh *prom_db_open(pool *p, const char *table_path, const char *schema_name); /* Open the existing database (with the given schema name) at the given path. */ struct prom_dbh *prom_db_open_readonly(pool *p, const char *table_path, const char *schema_name); /* Create/prepare the database (with the given schema name) at the given path. * If the database/schema already exists, check that its schema version is * greater than or equal to the given minimum version. If not, delete that * database and create a new one. */ struct prom_dbh *prom_db_open_with_version(pool *p, const char *table_path, const char *schema_name, unsigned int schema_version, int flags); #define PROM_DB_OPEN_FL_SCHEMA_VERSION_CHECK 0x001 #define PROM_DB_OPEN_FL_ERROR_ON_SCHEMA_VERSION_SKEW 0x002 #define PROM_DB_OPEN_FL_INTEGRITY_CHECK 0x004 #define PROM_DB_OPEN_FL_VACUUM 0x008 #define PROM_DB_OPEN_FL_SKIP_VACUUM 0x010 #define PROM_DB_OPEN_FL_SKIP_TABLE_INIT 0x020 /* Open the existing database (with the given schema name) at the given path. * If the database/schema already exists, check that its schema version is * greater than or equal to the given minimum version. */ struct prom_dbh *prom_db_open_readonly_with_version(pool *p, const char *table_path, const char *schema_name, unsigned int schema_version, int flags); /* Close the database. */ int prom_db_close(pool *p, struct prom_dbh *dbh); int prom_db_prepare_stmt(pool *p, struct prom_dbh *dbh, const char *stmt); int prom_db_finish_stmt(pool *p, struct prom_dbh *dbh, const char *stmt); int prom_db_bind_stmt(pool *p, struct prom_dbh *dbh, const char *stmt, int idx, int type, void *data); #define PROM_DB_BIND_TYPE_INT 1 #define PROM_DB_BIND_TYPE_LONG 2 #define PROM_DB_BIND_TYPE_DOUBLE 3 #define PROM_DB_BIND_TYPE_TEXT 4 #define PROM_DB_BIND_TYPE_NULL 5 /* Executes the given statement. Assumes that the caller is not using a SELECT, * and/or is uninterested in the statement results. */ int prom_db_exec_stmt(pool *p, struct prom_dbh *dbh, const char *stmt, const char **errstr); /* Executes the given statement as a previously prepared statement. */ array_header *prom_db_exec_prepared_stmt(pool *p, struct prom_dbh *dbh, const char *stmt, const char **errstr); /* Rebuild the named index. */ int prom_db_reindex(pool *p, struct prom_dbh *dbh, const char *index_name, const char **errstr); /* Obtain the ROWID for the last inserted row. */ int prom_db_last_row_id(pool *p, struct prom_dbh *dbh, int64_t *row_id); #endif /* MOD_PROMETHEUS_DB_H */ proftpd-mod_prometheus-0.2/include/prometheus/http.h000066400000000000000000000031061410622367000230300ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus http API * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #ifndef MOD_PROMETHEUS_HTTP_H #define MOD_PROMETHEUS_HTTP_H #include "mod_prometheus.h" #include "prometheus/registry.h" struct prom_http; struct prom_http *prom_http_start(pool *p, const pr_netaddr_t *addr, struct prom_registry *registry, const char *username, const char *password); /* This function will exit once the exporter finishes. */ int prom_http_run_loop(pool *p, struct prom_http *http); int prom_http_stop(pool *p, struct prom_http *http); int prom_http_init(pool *p); int prom_http_free(void); #endif /* MOD_PROMETHEUS_HTTP_H */ proftpd-mod_prometheus-0.2/include/prometheus/metric.h000066400000000000000000000072241410622367000233410ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus metric API * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #ifndef MOD_PROMETHEUS_METRIC_H #define MOD_PROMETHEUS_METRIC_H #include "mod_prometheus.h" #include "prometheus/db.h" struct prom_metric; struct prom_metric *prom_metric_create(pool *p, const char *name, struct prom_dbh *dbh); int prom_metric_destroy(pool *p, struct prom_metric *metric); int prom_metric_add_counter(struct prom_metric *metric, const char *suffix, const char *help_text); int prom_metric_add_gauge(struct prom_metric *metric, const char *suffix, const char *help_text); int prom_metric_add_histogram(struct prom_metric *metric, const char *suffix, const char *help_text, unsigned int bucket_count, ...); int prom_metric_set_dbh(struct prom_metric *metric, struct prom_dbh *dbh); /* Returns the metric name. */ const char *prom_metric_get_name(struct prom_metric *metric); /* Decrement the specified metric by the given `decr`; applies to any * gauge records associated with this metric. */ int prom_metric_decr(pool *p, const struct prom_metric *metric, uint32_t decr, pr_table_t *labels); /* Increment the specified metric by the given `incr`; applies to any * counter/gauge records associated with this metric. */ int prom_metric_incr(pool *p, const struct prom_metric *metric, uint32_t incr, pr_table_t *labels); /* Increment the specified metric type by the given `incr`. */ int prom_metric_incr_type(pool *p, const struct prom_metric *metric, uint32_t incr, pr_table_t *labels, int metric_type); /* Observe the specified metric by the given `val`; apply to any * histogram records associated with this metric. */ int prom_metric_observe(pool *p, const struct prom_metric *metric, double val, pr_table_t *labels); /* Setl the specified metric by the given `val`; applies to any * gauge records associated with this metric. */ int prom_metric_set(pool *p, const struct prom_metric *metric, uint32_t val, pr_table_t *labels); /* Returns the collected samples for this metric and type. The `counts` * and `sums` arrays are used for histograms, to differentiate those samples * from the histogram bucket samples. */ const array_header *prom_metric_get(pool *p, struct prom_metric *metric, int metric_type, const array_header **counts, const array_header **sums); #define PROM_METRIC_TYPE_COUNTER 1 #define PROM_METRIC_TYPE_GAUGE 2 #define PROM_METRIC_TYPE_HISTOGRAM 3 /* Get the Prometheus exposition formatted text for the metric. */ const char *prom_metric_get_text(pool *p, struct prom_metric *metric, const char *registry_name, size_t *textlen); struct prom_dbh *prom_metric_init(pool *p, const char *tables_path); int prom_metric_free(pool *p, struct prom_dbh *dbh); #endif /* MOD_PROMETHEUS_METRIC_H */ proftpd-mod_prometheus-0.2/include/prometheus/metric/000077500000000000000000000000001410622367000231635ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/include/prometheus/metric/db.h000066400000000000000000000043231410622367000237230ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus metrics datastore API * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #ifndef MOD_PROMETHEUS_METRIC_DB_H #define MOD_PROMETHEUS_METRIC_DB_H #include "mod_prometheus.h" #include "prometheus/db.h" int prom_metric_db_close(pool *p, struct prom_dbh *dbh); struct prom_dbh *prom_metric_db_open(pool *p, const char *tables_path); struct prom_dbh *prom_metric_db_init(pool *p, const char *tables_path, int flags); int prom_metric_db_create(pool *p, struct prom_dbh *dbh, const char *metric_name, int metric_type, int64_t *metric_id); int prom_metric_db_exists(pool *p, struct prom_dbh *dbh, const char *metric_name); int prom_metric_db_sample_exists(pool *p, struct prom_dbh *dbh, int64_t metric_id, const char *sample_labels); int prom_metric_db_sample_decr(pool *p, struct prom_dbh *dbh, int64_t metric_id, double sample_val, const char *sample_labels); int prom_metric_db_sample_incr(pool *p, struct prom_dbh *dbh, int64_t metric_id, double sample_val, const char *sample_labels); int prom_metric_db_sample_set(pool *p, struct prom_dbh *dbh, int64_t metric_id, double sample_val, const char *sample_labels); const array_header *prom_metric_db_sample_get(pool *p, struct prom_dbh *dbh, int64_t metric_id); #endif /* MOD_PROMETHEUS_METRIC_DB_H */ proftpd-mod_prometheus-0.2/include/prometheus/registry.h000066400000000000000000000044411410622367000237240ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus registry API * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #ifndef MOD_PROMETHEUS_REGISTRY_H #define MOD_PROMETHEUS_REGISTRY_H #include "mod_prometheus.h" #include "prometheus/db.h" #include "prometheus/metric.h" struct prom_registry; /* Returns the name/prefix prepended to all metrics in this registry. */ const char *prom_registry_get_name(struct prom_registry *registry); /* Returns the text for all collector's metrics in the registry. */ const char *prom_registry_get_text(pool *p, struct prom_registry *registry); int prom_registry_add_metric(struct prom_registry *registry, struct prom_metric *metric); int prom_registry_remove_metric(struct prom_registry *registry, struct prom_metric *metric); /* Returns the metric object for the given metric name. */ const struct prom_metric *prom_registry_get_metric( struct prom_registry *registry, const char *metric_name); /* Sets the given database handle on all registered metrics. */ int prom_registry_set_dbh(struct prom_registry *registry, struct prom_dbh *dbh); /* Caches a sorted list of metric names, for use in generating the text. */ int prom_registry_sort_metrics(struct prom_registry *registry); struct prom_registry *prom_registry_init(pool *p, const char *name); int prom_registry_free(struct prom_registry *registry); #endif /* MOD_PROMETHEUS_REGISTRY_H */ proftpd-mod_prometheus-0.2/include/prometheus/text.h000066400000000000000000000033061410622367000230370ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus text API * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #ifndef MOD_PROMETHEUS_TEXT_H #define MOD_PROMETHEUS_TEXT_H #include "mod_prometheus.h" #include "prometheus/metric.h" struct prom_text; struct prom_text *prom_text_create(pool *p); int prom_text_destroy(struct prom_text *text); int prom_text_add_byte(struct prom_text *text, char ch); int prom_text_add_str(struct prom_text *text, const char *str, size_t sz); /* Obtain a copy of the accumulated text, duplicated from the given pool. */ char *prom_text_get_str(pool *p, struct prom_text *text, size_t *sz); /* Convert the given labels to text. */ const char *prom_text_from_labels(pool *p, struct prom_text *text, pr_table_t *labels); #endif /* MOD_PROMETHEUS_TEXT_H */ proftpd-mod_prometheus-0.2/install-sh000077500000000000000000000324641410622367000200770ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2006-12-25.00 # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: proftpd-mod_prometheus-0.2/lib/000077500000000000000000000000001410622367000166305ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/lib/prometheus/000077500000000000000000000000001410622367000210235ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/lib/prometheus/db.c000066400000000000000000000724111410622367000215610ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus database implementation * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #include "mod_prometheus.h" #include "prometheus/db.h" #include struct prom_dbh { pool *pool; sqlite3 *db; const char *schema; pr_table_t *prepared_stmts; }; static const char *current_schema = NULL; static const char *trace_channel = "prometheus.db"; #define PROM_DB_SQLITE_MAX_RETRY_COUNT 8 #define PROM_DB_SQLITE_MAX_RETRY_DELAY_MS 250 #define PROM_DB_SQLITE_TRACE_LEVEL 17 static int db_busy(void *user_data, int busy_count) { int retry = FALSE; /* How many retries do we want to allow? */ if (busy_count <= PROM_DB_SQLITE_MAX_RETRY_COUNT) { retry = TRUE; } if (current_schema != NULL) { pr_trace_msg(trace_channel, 1, "(sqlite3): schema '%s': busy count = %d, retry = %s", current_schema, busy_count, retry ? "true" : "false"); } else { pr_trace_msg(trace_channel, 1, "(sqlite3): busy count = %d, retry = %s", busy_count, retry ? "true" : "false"); } /* If we're busy, then sleep for a short while, on the assumption that the * other process will finish its business with our tables. */ (void) pr_timer_usleep(PROM_DB_SQLITE_MAX_RETRY_DELAY_MS * 1000); return retry; } #if defined(SQLITE_CONFIG_LOG) static void db_err(void *user_data, int err_code, const char *err_msg) { if (current_schema != NULL) { pr_trace_msg(trace_channel, 1, "(sqlite3): schema '%s': [error %d] %s", current_schema, err_code, err_msg); } else { pr_trace_msg(trace_channel, 1, "(sqlite3): [error %d] %s", err_code, err_msg); } } #endif /* SQLITE_CONFIG_LOG */ #if defined(SQLITE_CONFIG_SQLLOG) static void db_sql(void *user_data, sqlite3 *db, const char *info, int event_type) { switch (event_type) { case 0: /* Opening database. */ pr_trace_msg(trace_channel, 1, "(sqlite3): opened database: %s", info); break; case 1: if (current_schema != NULL) { pr_trace_msg(trace_channel, 1, "(sqlite3): schema '%s': executed statement: %s", current_schema, info); } else { pr_trace_msg(trace_channel, 1, "(sqlite3): executed statement: %s", info); } break; case 2: /* Closing database. */ pr_trace_msg(trace_channel, 1, "(sqlite3): closed database: %s", sqlite3_db_filename(db, "main")); break; } } #endif /* SQLITE_CONFIG_SQLLOG */ #if defined(HAVE_SQLITE3_TRACE_V2) static int db_trace2(unsigned int trace_type, void *user_data, void *ptr, void *ptr_data) { const char *schema_name; schema_name = user_data; switch (trace_type) { case SQLITE_TRACE_STMT: { const char *stmt; stmt = ptr_data; if (schema_name == NULL) { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): executing stmt '%s'", stmt); } else { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): schema '%s': executing stmt '%s'", schema_name, stmt); } break; } case SQLITE_TRACE_PROFILE: { sqlite3_stmt *pstmt; int64_t ns = 0; const char *expanded_sql = NULL; pstmt = ptr; ns = *((int64_t *) ptr_data); expanded_sql = sqlite3_expanded_sql(pstmt); if (schema_name == NULL) { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): stmt '%s' ran for %lu nanosecs", expanded_sql, (unsigned long) ns); } else { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): schema '%s': stmt '%s' ran for %lu nanosecs", schema_name, expanded_sql, (unsigned long) ns); } break; } case SQLITE_TRACE_ROW: { sqlite3_stmt *pstmt; const char *expanded_sql = NULL; pstmt = ptr; expanded_sql = sqlite3_expanded_sql(pstmt); if (schema_name == NULL) { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): returning result row for stmt '%s'", expanded_sql); } else { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): schema '%s': returning result row for stmt '%s'", schema_name, expanded_sql); } break; } case SQLITE_TRACE_CLOSE: { sqlite3 *db; db = ptr; if (schema_name == NULL) { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): closing database connection to %s", sqlite3_db_filename(db, "main")); } else { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): schema '%s': closing database connection to %s", schema_name, sqlite3_db_filename(db, "main")); } break; } default: break; } return 0; } #elif defined(HAVE_SQLITE3_TRACE) static void db_trace(void *user_data, const char *trace_msg) { if (user_data != NULL) { const char *schema_name; schema_name = user_data; pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): schema '%s': %s", schema_name, trace_msg); } else { pr_trace_msg(trace_channel, PROM_DB_SQLITE_TRACE_LEVEL, "(sqlite3): %s", trace_msg); } } #endif /* HAVE_SQLITE3_TRACE */ static int stmt_cb(void *v, int ncols, char **cols, char **col_names) { register int i; const char *stmt; stmt = v; pr_trace_msg(trace_channel, 9, "results for '%s':", stmt); for (i = 0; i < ncols; i++) { pr_trace_msg(trace_channel, 9, "col #%d [%s]: %s", i+1, col_names[i], cols[i]); } return 0; } int prom_db_exec_stmt(pool *p, struct prom_dbh *dbh, const char *stmt, const char **errstr) { int res; char *ptr = NULL; unsigned int nretries = 0; if (dbh == NULL || stmt == NULL) { errno = EINVAL; return -1; } pr_trace_msg(trace_channel, 10, "schema '%s': executing statement '%s'", dbh->schema, stmt); current_schema = dbh->schema; res = sqlite3_exec(dbh->db, stmt, stmt_cb, (void *) stmt, &ptr); while (res != SQLITE_OK) { if (res == SQLITE_BUSY) { struct timeval tv; sqlite3_free(ptr); nretries++; pr_trace_msg(trace_channel, 3, "attempt #%u, database busy, trying '%s' again", nretries, stmt); /* Sleep for short bit, then try again. */ tv.tv_sec = 0; tv.tv_usec = 500000L; if (select(0, NULL, NULL, NULL, &tv) < 0) { if (errno == EINTR) { pr_signals_handle(); } } res = sqlite3_exec(dbh->db, stmt, NULL, NULL, &ptr); continue; } pr_trace_msg(trace_channel, 1, "error executing '%s': (%d) %s", stmt, res, ptr); if (errstr != NULL) { *errstr = pstrdup(p, ptr); } current_schema = NULL; sqlite3_free(ptr); errno = EINVAL; return -1; } if (ptr != NULL) { sqlite3_free(ptr); } current_schema = NULL; pr_trace_msg(trace_channel, 13, "successfully executed '%s'", stmt); return 0; } /* Prepared statements */ int prom_db_prepare_stmt(pool *p, struct prom_dbh *dbh, const char *stmt) { sqlite3_stmt *pstmt = NULL; int res; if (p == NULL || dbh == NULL || stmt == NULL) { errno = EINVAL; return -1; } pstmt = (sqlite3_stmt *) pr_table_get(dbh->prepared_stmts, stmt, NULL); if (pstmt != NULL) { res = sqlite3_reset(pstmt); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 3, "error resetting prepared statement '%s': %s", stmt, sqlite3_errmsg(dbh->db)); errno = EPERM; return -1; } return 0; } res = sqlite3_prepare_v2(dbh->db, stmt, -1, &pstmt, NULL); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "schema '%s': error preparing statement '%s': %s", dbh->schema, stmt, sqlite3_errmsg(dbh->db)); errno = EINVAL; return -1; } /* The prepared statement handling here relies on this cache, thus if we fail * to stash the prepared statement here, it will cause problems later. */ res = pr_table_add(dbh->prepared_stmts, pstrdup(dbh->pool, stmt), pstmt, sizeof(sqlite3_stmt *)); if (res < 0) { int xerrno = errno; pr_trace_msg(trace_channel, 4, "error stashing prepared statement '%s': %s", stmt, strerror(xerrno)); errno = xerrno; return -1; } return 0; } int prom_db_bind_stmt(pool *p, struct prom_dbh *dbh, const char *stmt, int idx, int type, void *data) { sqlite3_stmt *pstmt; int res; if (p == NULL || dbh == NULL || stmt == NULL) { errno = EINVAL; return -1; } /* SQLite3 bind parameters start at index 1. */ if (idx < 1) { errno = EINVAL; return -1; } if (dbh->prepared_stmts == NULL) { errno = ENOENT; return -1; } pstmt = (sqlite3_stmt *) pr_table_get(dbh->prepared_stmts, stmt, NULL); if (pstmt == NULL) { pr_trace_msg(trace_channel, 19, "unable to find prepared statement for '%s'", stmt); errno = ENOENT; return -1; } switch (type) { case PROM_DB_BIND_TYPE_INT: { int i; if (data == NULL) { errno = EINVAL; return -1; } i = *((int *) data); res = sqlite3_bind_int(pstmt, idx, i); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to INT %d: %s", idx, stmt, i, sqlite3_errmsg(dbh->db)); errno = EPERM; return -1; } break; } case PROM_DB_BIND_TYPE_LONG: { long l; if (data == NULL) { errno = EINVAL; return -1; } l = *((long *) data); res = sqlite3_bind_int(pstmt, idx, l); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to LONG %ld: %s", idx, stmt, l, sqlite3_errmsg(dbh->db)); errno = EPERM; return -1; } break; } case PROM_DB_BIND_TYPE_DOUBLE: { double d; if (data == NULL) { errno = EINVAL; return -1; } d = *((double *) data); res = sqlite3_bind_double(pstmt, idx, d); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to DOUBLE %0.3f: %s", idx, stmt, d, sqlite3_errmsg(dbh->db)); errno = EPERM; return -1; } break; } case PROM_DB_BIND_TYPE_TEXT: { const char *text; if (data == NULL) { errno = EINVAL; return -1; } text = (const char *) data; res = sqlite3_bind_text(pstmt, idx, text, -1, NULL); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to TEXT '%s': %s", idx, stmt, text, sqlite3_errmsg(dbh->db)); errno = EPERM; return -1; } break; } case PROM_DB_BIND_TYPE_NULL: res = sqlite3_bind_null(pstmt, idx); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to NULL: %s", idx, stmt, sqlite3_errmsg(dbh->db)); errno = EPERM; return -1; } break; default: pr_trace_msg(trace_channel, 2, "unknown/unsupported bind data type %d", type); errno = EINVAL; return -1; } return 0; } int prom_db_finish_stmt(pool *p, struct prom_dbh *dbh, const char *stmt) { sqlite3_stmt *pstmt; int res; if (p == NULL || dbh == NULL || stmt == NULL) { errno = EINVAL; return -1; } if (dbh->prepared_stmts == NULL) { errno = ENOENT; return -1; } pstmt = (sqlite3_stmt *) pr_table_get(dbh->prepared_stmts, stmt, NULL); if (pstmt == NULL) { pr_trace_msg(trace_channel, 19, "unable to find prepared statement for '%s'", stmt); errno = ENOENT; return -1; } res = sqlite3_finalize(pstmt); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 3, "schema '%s': error finishing prepared statement '%s': %s", dbh->schema, stmt, sqlite3_errmsg(dbh->db)); errno = EPERM; return -1; } (void) pr_table_remove(dbh->prepared_stmts, stmt, NULL); return 0; } array_header *prom_db_exec_prepared_stmt(pool *p, struct prom_dbh *dbh, const char *stmt, const char **errstr) { sqlite3_stmt *pstmt; int readonly = FALSE, res; array_header *results = NULL; if (p == NULL || dbh == NULL || stmt == NULL) { errno = EINVAL; return NULL; } if (dbh->prepared_stmts == NULL) { errno = ENOENT; return NULL; } pstmt = (sqlite3_stmt *) pr_table_get(dbh->prepared_stmts, stmt, NULL); if (pstmt == NULL) { pr_trace_msg(trace_channel, 19, "unable to find prepared statement for '%s'", stmt); errno = ENOENT; return NULL; } current_schema = dbh->schema; /* The sqlit3_stmt_readonly() function first appeared in SQLite 3.7.x. */ #if defined(HAVE_SQLITE3_STMT_READONLY) readonly = sqlite3_stmt_readonly(pstmt); #else readonly = FALSE; #endif /* SQLite 3.7.x or earlier */ if (readonly == FALSE) { /* Assume this is an INSERT/UPDATE/DELETE. */ res = sqlite3_step(pstmt); if (res != SQLITE_DONE) { const char *errmsg; errmsg = sqlite3_errmsg(dbh->db); if (errstr) { *errstr = pstrdup(p, errmsg); } pr_trace_msg(trace_channel, 2, "error executing '%s': %s", stmt, errmsg); current_schema = NULL; errno = EPERM; return NULL; } current_schema = NULL; /* Indicate success for non-readonly statements by returning an empty * result set. */ pr_trace_msg(trace_channel, 13, "successfully executed '%s'", stmt); results = make_array(p, 0, sizeof(char *)); return results; } results = make_array(p, 0, sizeof(char *)); res = sqlite3_step(pstmt); while (res == SQLITE_ROW) { register int i; int ncols; ncols = sqlite3_column_count(pstmt); pr_trace_msg(trace_channel, 12, "schema '%s': executing prepared statement '%s' returned row " "(columns: %d)", dbh->schema, stmt, ncols); for (i = 0; i < ncols; i++) { char *val = NULL; pr_signals_handle(); /* By using sqlite3_column_text, SQLite will coerce the column value * into a string. */ val = pstrdup(p, (const char *) sqlite3_column_text(pstmt, i)); pr_trace_msg(trace_channel, 17, "column %s [%u]: '%s'", sqlite3_column_name(pstmt, i), i, val != NULL ? val : "NULL"); *((char **) push_array(results)) = val; } res = sqlite3_step(pstmt); } if (res != SQLITE_DONE) { const char *errmsg; errmsg = sqlite3_errmsg(dbh->db); if (errstr != NULL) { *errstr = pstrdup(p, errmsg); } current_schema = NULL; (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "schema '%s': executing prepared statement '%s' did not complete " "successfully: %s", dbh->schema, stmt, errmsg); errno = EPERM; return NULL; } current_schema = NULL; pr_trace_msg(trace_channel, 13, "successfully executed '%s'", stmt); return results; } /* Database opening/closing. */ static struct prom_dbh *db_open(pool *p, const char *table_path, const char *schema_name, int flags) { int res; pool *sub_pool; const char *stmt; sqlite3 *db = NULL; struct prom_dbh *dbh; if (p == NULL || table_path == NULL || schema_name == NULL) { errno = EINVAL; return NULL; } pr_trace_msg(trace_channel, 19, "attempting to open %s tables at path '%s'", schema_name, table_path); #if defined(SQLITE_OPEN_PRIVATECACHE) /* By default, disable the shared cache mode. */ flags |= SQLITE_OPEN_PRIVATECACHE; #endif res = sqlite3_open_v2(table_path, &db, flags, NULL); if (res != SQLITE_OK) { pr_log_debug(DEBUG0, MOD_PROMETHEUS_VERSION ": error opening SQLite database '%s': %s", table_path, sqlite3_errmsg(db)); if (db != NULL) { sqlite3_close(db); } errno = EPERM; return NULL; } /* Make sure we set our busy handler. */ sqlite3_busy_handler(db, db_busy, (void *) schema_name); if (pr_trace_get_level(trace_channel) >= PROM_DB_SQLITE_TRACE_LEVEL) { #if defined(HAVE_SQLITE3_TRACE_V2) sqlite3_trace_v2(db, SQLITE_TRACE_STMT|SQLITE_TRACE_PROFILE|SQLITE_TRACE_ROW|SQLITE_TRACE_CLOSE, db_trace2, (void *) schema_name); #elif defined(HAVE_SQLITE3_TRACE) sqlite3_trace(db, db_trace, (void *) schema_name); #endif /* HAVE_SQLITE3_TRACE or HAVE_SQLITE3_TRACE_V2 */ } sub_pool = make_sub_pool(p); pr_pool_tag(sub_pool, "Proxy Database Pool"); dbh = pcalloc(sub_pool, sizeof(struct prom_dbh)); dbh->pool = sub_pool; dbh->db = db; dbh->schema = pstrdup(dbh->pool, schema_name); stmt = "PRAGMA temp_store = MEMORY;"; res = prom_db_exec_stmt(p, dbh, stmt, NULL); if (res < 0) { pr_trace_msg(trace_channel, 2, "error setting MEMORY temp store on SQLite database '%s': %s", table_path, sqlite3_errmsg(dbh->db)); } /* Tell SQLite to only use in-memory journals. This is necessary for * working properly when a chroot is used. Note that the MEMORY journal mode * of SQLite is supported only for SQLite-3.6.5 and later. */ stmt = "PRAGMA journal_mode = MEMORY;"; res = prom_db_exec_stmt(p, dbh, stmt, NULL); if (res < 0) { pr_trace_msg(trace_channel, 2, "error setting MEMORY journal mode on SQLite database '%s': %s", table_path, sqlite3_errmsg(dbh->db)); } dbh->prepared_stmts = pr_table_nalloc(dbh->pool, 0, 4); pr_trace_msg(trace_channel, 9, "opened SQLite table '%s'", table_path); return dbh; } struct prom_dbh *prom_db_open(pool *p, const char *table_path, const char *schema_name) { int flags; flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; return db_open(p, table_path, schema_name, flags); } struct prom_dbh *prom_db_open_readonly(pool *p, const char *table_path, const char *schema_name) { int flags; flags = SQLITE_OPEN_READONLY; return db_open(p, table_path, schema_name, flags); } static int get_schema_version(pool *p, struct prom_dbh *dbh, const char *schema_name, unsigned int *schema_version) { int res, version; const char *stmt, *errstr = NULL; array_header *results; stmt = "SELECT version FROM schema_version WHERE schema = ?;"; res = prom_db_prepare_stmt(p, dbh, stmt); if (res < 0) { /* This can happen when the schema_version table does not exist; treat * as "missing". */ pr_trace_msg(trace_channel, 5, "error preparing statement '%s', treating as missing schema version", stmt); *schema_version = 0; return 0; } res = prom_db_bind_stmt(p, dbh, stmt, 1, PROM_DB_BIND_TYPE_TEXT, (void *) schema_name); if (res < 0) { return -1; } results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); (void) prom_db_finish_stmt(p, dbh, stmt); if (results == NULL) { *schema_version = 0; return 0; } if (results->nelts != 1) { pr_log_debug(DEBUG3, MOD_PROMETHEUS_VERSION ": expected 1 result from statement '%s', got %d", stmt, results->nelts); errno = EINVAL; return -1; } version = atoi(((char **) results->elts)[0]); if (version < 0) { /* Invalid schema version; treat as "missing". */ pr_trace_msg(trace_channel, 5, "statement '%s' yielded invalid schema version %d, treating as missing", stmt, version); *schema_version = 0; return 0; } *schema_version = version; return 0; } static int set_schema_version(pool *p, struct prom_dbh *dbh, const char *schema_name, unsigned int schema_version) { int res, xerrno = 0; const char *stmt, *errstr = NULL; array_header *results; /* CREATE TABLE schema_version ( * schema TEXT NOT NULL PRIMARY KEY, * version INTEGER NOT NULL * ); */ stmt = "CREATE TABLE IF NOT EXISTS schema_version (schema TEXT NOT NULL PRIMARY KEY, version INTEGER NOT NULL);"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_debug(DEBUG3, MOD_PROMETHEUS_VERSION ": error executing statement '%s': %s", stmt, errstr); errno = EPERM; return -1; } stmt = "INSERT INTO schema_version (schema, version) VALUES (?, ?);"; res = prom_db_prepare_stmt(p, dbh, stmt); if (res < 0) { xerrno = errno; (void) pr_log_debug(DEBUG3, MOD_PROMETHEUS_VERSION ": schema '%s': error preparing statement '%s': %s", dbh->schema, stmt, strerror(xerrno)); errno = xerrno; return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 1, PROM_DB_BIND_TYPE_TEXT, (void *) schema_name); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 2, PROM_DB_BIND_TYPE_INT, (void *) &schema_version); if (res < 0) { return -1; } results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); xerrno = errno; (void) prom_db_finish_stmt(p, dbh, stmt); if (results == NULL) { (void) pr_log_debug(DEBUG3, MOD_PROMETHEUS_VERSION ": error executing statement '%s': %s", stmt, errstr ? errstr : strerror(xerrno)); errno = EPERM; return -1; } return 0; } static void check_db_integrity(pool *p, struct prom_dbh *dbh, int flags) { int res; const char *stmt, *errstr = NULL; if (flags & PROM_DB_OPEN_FL_INTEGRITY_CHECK) { stmt = "PRAGMA integrity_check;"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_debug(DEBUG3, MOD_PROMETHEUS_VERSION ": error executing statement '%s': %s", stmt, errstr); } } if (flags & PROM_DB_OPEN_FL_VACUUM) { stmt = "VACUUM;"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_debug(DEBUG3, MOD_PROMETHEUS_VERSION ": error executing statement '%s': %s", stmt, errstr); } } } struct prom_dbh *prom_db_open_with_version(pool *p, const char *table_path, const char *schema_name, unsigned int schema_version, int flags) { pool *tmp_pool = NULL; struct prom_dbh *dbh = NULL; int res = 0, xerrno = 0; unsigned int current_version = 0; dbh = prom_db_open(p, table_path, schema_name); if (dbh == NULL) { return NULL; } if (flags & PROM_DB_OPEN_FL_SCHEMA_VERSION_CHECK) { pr_trace_msg(trace_channel, 19, "ensuring that schema at path '%s' has at least schema version %u", table_path, schema_version); tmp_pool = make_sub_pool(p); res = get_schema_version(tmp_pool, dbh, schema_name, ¤t_version); if (res < 0) { xerrno = errno; prom_db_close(p, dbh); destroy_pool(tmp_pool); errno = xerrno; return NULL; } if (current_version >= schema_version) { pr_trace_msg(trace_channel, 11, "schema version %u >= desired version %u for path '%s'", current_version, schema_version, table_path); check_db_integrity(tmp_pool, dbh, flags); destroy_pool(tmp_pool); return dbh; } if (flags & PROM_DB_OPEN_FL_ERROR_ON_SCHEMA_VERSION_SKEW) { pr_trace_msg(trace_channel, 5, "schema version %u < desired version %u for path '%s', failing", current_version, schema_version, table_path); prom_db_close(p, dbh); destroy_pool(tmp_pool); errno = EPERM; return NULL; } /* The schema version is skewed; delete the old table, create a new one. */ pr_trace_msg(trace_channel, 4, "schema version %u < desired version %u for path '%s', deleting file", current_version, schema_version, table_path); if (prom_db_close(p, dbh) < 0) { pr_log_debug(DEBUG0, MOD_PROMETHEUS_VERSION ": error closing '%s' database: %s", table_path, strerror(errno)); } if (unlink(table_path) < 0) { pr_log_pri(PR_LOG_NOTICE, MOD_PROMETHEUS_VERSION ": error deleting '%s': %s", table_path, strerror(errno)); } dbh = prom_db_open(p, table_path, schema_name); if (dbh == NULL) { xerrno = errno; destroy_pool(tmp_pool); errno = xerrno; return NULL; } res = set_schema_version(tmp_pool, dbh, schema_name, schema_version); xerrno = errno; } else { check_db_integrity(tmp_pool, dbh, flags); } destroy_pool(tmp_pool); if (res < 0) { errno = xerrno; return NULL; } return dbh; } struct prom_dbh *prom_db_open_readonly_with_version(pool *p, const char *table_path, const char *schema_name, unsigned int schema_version, int flags) { pool *tmp_pool = NULL; struct prom_dbh *dbh = NULL; int res = 0, xerrno = 0; unsigned int current_version = 0; dbh = prom_db_open_readonly(p, table_path, schema_name); if (dbh == NULL) { return NULL; } if (flags & PROM_DB_OPEN_FL_SCHEMA_VERSION_CHECK) { pr_trace_msg(trace_channel, 19, "ensuring that schema at path '%s' has at least schema version %u", table_path, schema_version); tmp_pool = make_sub_pool(p); res = get_schema_version(tmp_pool, dbh, schema_name, ¤t_version); if (res < 0) { xerrno = errno; prom_db_close(p, dbh); destroy_pool(tmp_pool); errno = xerrno; return NULL; } if (current_version >= schema_version) { pr_trace_msg(trace_channel, 11, "schema version %u >= desired version %u for path '%s'", current_version, schema_version, table_path); check_db_integrity(tmp_pool, dbh, flags); destroy_pool(tmp_pool); return dbh; } if (flags & PROM_DB_OPEN_FL_ERROR_ON_SCHEMA_VERSION_SKEW) { pr_trace_msg(trace_channel, 5, "schema version %u < desired version %u for path '%s', failing", current_version, schema_version, table_path); prom_db_close(p, dbh); destroy_pool(tmp_pool); errno = EPERM; return NULL; } } else { check_db_integrity(tmp_pool, dbh, flags); } destroy_pool(tmp_pool); if (res < 0) { errno = xerrno; return NULL; } return dbh; } int prom_db_close(pool *p, struct prom_dbh *dbh) { pool *tmp_pool; sqlite3_stmt *pstmt; int res; if (p == NULL || dbh == NULL) { errno = EINVAL; return -1; } pr_trace_msg(trace_channel, 19, "closing '%s' database handle", dbh->schema); tmp_pool = make_sub_pool(p); /* Make sure to close/finish any prepared statements associated with * the database. */ pstmt = sqlite3_next_stmt(dbh->db, NULL); while (pstmt != NULL) { sqlite3_stmt *next; const char *sql; pr_signals_handle(); next = sqlite3_next_stmt(dbh->db, pstmt); sql = pstrdup(tmp_pool, sqlite3_sql(pstmt)); res = sqlite3_finalize(pstmt); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 2, "schema '%s': error finishing prepared statement '%s': %s", dbh->schema, sql, sqlite3_errmsg(dbh->db)); } else { pr_trace_msg(trace_channel, 18, "finished prepared statement '%s'", sql); } pstmt = next; } destroy_pool(tmp_pool); res = sqlite3_close(dbh->db); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 2, "error closing SQLite database: %s", sqlite3_errmsg(dbh->db)); errno = EPERM; return -1; } pr_table_empty(dbh->prepared_stmts); pr_table_free(dbh->prepared_stmts); destroy_pool(dbh->pool); pr_trace_msg(trace_channel, 18, "%s", "closed SQLite database"); return 0; } int prom_db_reindex(pool *p, struct prom_dbh *dbh, const char *index_name, const char **errstr) { int res; const char *stmt; if (p == NULL || dbh == NULL || index_name == NULL) { errno = EINVAL; return -1; } stmt = pstrcat(p, "REINDEX ", index_name, ";", NULL); res = prom_db_exec_stmt(p, dbh, stmt, errstr); return res; } int prom_db_last_row_id(pool *p, struct prom_dbh *dbh, int64_t *id) { if (p == NULL || dbh == NULL || id == NULL) { errno = EINVAL; return -1; } (void) p; *id = sqlite3_last_insert_rowid(dbh->db); return 0; } int prom_db_init(pool *p) { const char *version; if (p == NULL) { errno = EINVAL; return -1; } #if defined(SQLITE_CONFIG_LOG) /* Register an error logging callback with SQLite3. */ sqlite3_config(SQLITE_CONFIG_LOG, db_err, NULL); #endif /* SQLITE_CONFIG_LOG */ #if defined(SQLITE_CONFIG_SQLLOG) sqlite3_config(SQLITE_CONFIG_SQLLOG, db_sql, NULL); #endif /* SQLITE_CONFIG_SQLLOG */ /* Check that the SQLite headers used match the version of the SQLite * library used. * * For now, we only log if there is a difference. */ version = sqlite3_libversion(); if (strcmp(version, SQLITE_VERSION) != 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "compiled using SQLite version '%s' headers, but linked to " "SQLite version '%s' library", SQLITE_VERSION, version); } pr_trace_msg(trace_channel, 9, "using SQLite %s", version); return 0; } int prom_db_free(void) { return 0; } proftpd-mod_prometheus-0.2/lib/prometheus/http.c000066400000000000000000000447501410622367000221600ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus http implementation * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #include "mod_prometheus.h" #include "prometheus/http.h" #if defined(HAVE_ZLIB_H) # include /* RFC 1952 Section 2.3 defines the gzip header: * * +---+---+---+---+---+---+---+---+---+---+ * |ID1|ID2|CM |FLG| MTIME |XFL|OS | * +---+---+---+---+---+---+---+---+---+---+ */ static gz_header gzip_header = { TRUE, /* is text? */ 0, /* modification time */ 0, /* flags */ 0, /* os */ NULL, /* extra */ 0, 0, NULL, /* name */ 0, (unsigned char *) MOD_PROMETHEUS_VERSION, /* comment */ 0, TRUE, /* header CRC */ 0 }; #endif /* HAVE_ZLIB_H */ /* Per libmicrohttpd docs, we should define this after we have our system * headers, but before including `microhttpd.h`. */ #define MHD_PLATFORM_H 1 #include #if MHD_VERSION < 0x00097002 /* Prior to this change, the library used only `int`, not `enum MHD_Result`. * So to avoid compiler warnings for older library versions (such as those * provided by Centos), we need to jump through preprocessor hoops. */ # undef MHD_YES # undef MHD_NO enum MHD_Result { /* MHD result code for "NO". */ MHD_NO = 0, /* MHD result code for "YES". */ MHD_YES = 1 }; #endif struct prom_http { pool *pool; struct prom_registry *registry; struct MHD_Daemon *mhd; }; /* HTTP Basic Auth settings. */ static const char *http_realm = "proftpd"; static const char *http_username = NULL; static const char *http_password = NULL; static const char *trace_channel = "prometheus.http"; static const char *clf_channel = "prometheus.http.clf"; static void log_cb(void *user_data, const char *fmt, va_list msg) { pr_trace_vmsg(trace_channel, 7, fmt, msg); } static void panic_cb(void *user_data, const char *file, unsigned int lineno, const char *reason) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "microhttpd panic: [%s:%u] %s", file, lineno, reason); } static int can_gzip(struct MHD_Connection *conn) { #if defined(HAVE_ZLIB_H) const char *accept_encoding, *gzip_encoding = NULL; accept_encoding = MHD_lookup_connection_value(conn, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT_ENCODING); if (accept_encoding == NULL) { return FALSE; } pr_trace_msg(trace_channel, 19, "found Accept-Encoding request header: '%s'", accept_encoding); if (strcmp(accept_encoding, "*") == 0) { return TRUE; } gzip_encoding = strstr(accept_encoding, "gzip"); if (gzip_encoding == NULL) { return FALSE; } if ((gzip_encoding == accept_encoding || gzip_encoding[-1] == ',' || gzip_encoding[-1] == ' ') && (gzip_encoding[4] == '\0' || gzip_encoding[4] == ',' || gzip_encoding[4] == ';')) { return TRUE; } #endif /* HAVE_ZLIB_H */ return FALSE; } #if defined(HAVE_ZLIB_H) static const char *zlib_strerror(int zerrno) { const char *zstr = "unknown"; switch (zerrno) { case Z_OK: zstr = "OK"; break; case Z_STREAM_END: return "End of stream"; break; case Z_STREAM_ERROR: return "Stream error"; break; case Z_NEED_DICT: return "Need dictionary"; break; case Z_ERRNO: zstr = strerror(errno); break; case Z_DATA_ERROR: zstr = "Data error"; break; case Z_MEM_ERROR: zstr = "Memory error"; break; case Z_BUF_ERROR: zstr = "Buffer error"; break; case Z_VERSION_ERROR: zstr = "Version error"; break; } return zstr; } #endif /* HAVE_ZLIB_H */ static const char *gzip_text(pool *p, const char *text, size_t text_len, size_t *gzipped_textlen) { #if defined(HAVE_ZLIB_H) int res; z_stream *zstrm; unsigned char *output_buf = NULL; const char *gzipped_text = NULL; zstrm = pcalloc(p, sizeof(z_stream)); zstrm->zalloc = Z_NULL; zstrm->zfree = Z_NULL; zstrm->opaque = Z_NULL; zstrm->next_in = (Bytef *) text; zstrm->avail_in = zstrm->total_in = text_len; /* It's possible that it may require more room to compress the given * text, especially if it's small. Be prepared. */ zstrm->avail_out = zstrm->total_out = (text_len * 3); zstrm->next_out = output_buf = pcalloc(p, zstrm->avail_out); /* Note that it is IMPORTANT that the `windowBits` value be 31 or more here, * to indicate to zlib that it should add a gzip header. Subtle magic. */ res = deflateInit2(zstrm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); if (res != Z_OK) { deflateEnd(zstrm); pr_trace_msg(trace_channel, 1, "error initializing zlib for deflation: %s (%d)", zstrm->msg ? zstrm->msg : zlib_strerror(res), res); return NULL; } res = deflateSetHeader(zstrm, &gzip_header); if (res != Z_OK) { deflateEnd(zstrm); pr_trace_msg(trace_channel, 1, "error setting gzip header: %s (%d)", zstrm->msg ? zstrm->msg : zlib_strerror(res), res); return NULL; } res = deflate(zstrm, Z_FINISH); if (res != Z_STREAM_END) { deflateEnd(zstrm); pr_trace_msg(trace_channel, 1, "error compressing data: %s", zstrm->msg ? zstrm->msg : zlib_strerror(res)); return NULL; } pr_trace_msg(trace_channel, 19, "available compressed text: %lu bytes", (unsigned long) zstrm->total_out); *gzipped_textlen = zstrm->total_out; gzipped_text = pcalloc(p, *gzipped_textlen); memcpy((char *) gzipped_text, output_buf, *gzipped_textlen); deflateEnd(zstrm); return gzipped_text; #endif /* HAVE_ZLIB_H */ errno = ENOSYS; return NULL; } static const char *get_ip_text(pool *p, const struct sockaddr *sa) { char *remote_ip; #if defined(PR_USE_IPV6) size_t remote_iplen = INET6_ADDRSTRLEN; #else size_t remote_iplen = INET_ADDRSTRLEN; #endif /* PR_USE_IPV6 */ remote_ip = pcalloc(p, remote_iplen); switch (sa->sa_family) { case AF_INET: { struct sockaddr_in *sin; sin = (struct sockaddr_in *) sa; pr_inet_ntop(AF_INET, &(sin->sin_addr), remote_ip, remote_iplen - 1); break; } #if defined(PR_USE_IPV6) case AF_INET6: { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *) sa; pr_inet_ntop(AF_INET6, &(sin6->sin6_addr), remote_ip, remote_iplen - 1); break; } #endif /* PR_USE_IPV6 */ default: snprintf(remote_ip, remote_iplen-1, "%s", "unknown"); break; } return remote_ip; } static void log_clf(pool *p, struct MHD_Connection *conn, const char *username, const char *http_method, const char *http_uri, const char *http_version, unsigned int status_code, size_t resplen) { int clf_level = 1, res; const union MHD_ConnectionInfo *conn_info = NULL; const char *remote_ip = NULL; char timestamp[128]; struct tm *tm; time_t now; res = pr_trace_get_level(clf_channel); if (res < clf_level) { return; } now = time(NULL); tm = pr_gmtime(p, &now); if (tm == NULL) { return; } conn_info = MHD_get_connection_info(conn, MHD_CONNECTION_INFO_CLIENT_ADDRESS, NULL); if (username == NULL) { username = "-"; } remote_ip = get_ip_text(p, conn_info->client_addr); memset(timestamp, '\0', sizeof(timestamp)); strftime(timestamp, sizeof(timestamp)-1, "%d/%b/%Y:%H:%M:%S %z", tm); pr_trace_msg(clf_channel, clf_level, "%s - %s [%s] \"%s %s %s\" %u %lu", remote_ip, username, timestamp, http_method, http_uri, http_version, status_code, (unsigned long) resplen); } static enum MHD_Result handle_request_cb(void *user_data, struct MHD_Connection *conn, const char *http_uri, const char *http_method, const char *http_version, const char *request_body, size_t *request_bodysz, void **conn_user_data) { struct prom_http *http; pool *resp_pool; unsigned int status_code; const char *text; size_t textlen; struct MHD_Response *resp = NULL; int res; http = user_data; resp_pool = make_sub_pool(http->pool); pr_pool_tag(resp_pool, "Prometheus response pool"); if (strcmp(http_method, "GET") != 0) { status_code = MHD_HTTP_METHOD_NOT_ALLOWED; text = "Method Not Allowed\n"; textlen = strlen(text); resp = MHD_create_response_from_buffer(textlen, (void *) text, MHD_RESPMEM_PERSISTENT); (void) MHD_add_response_header(resp, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain"); res = MHD_queue_response(conn, status_code, resp); MHD_destroy_response(resp); log_clf(resp_pool, conn, NULL, http_method, http_uri, http_version, status_code, textlen); destroy_pool(resp_pool); return res; } if (strcmp(http_uri, "/") == 0) { status_code = MHD_HTTP_OK; text = "OK\n"; textlen = strlen(text); resp = MHD_create_response_from_buffer(textlen, (void *) text, MHD_RESPMEM_PERSISTENT); (void) MHD_add_response_header(resp, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain"); res = MHD_queue_response(conn, status_code, resp); MHD_destroy_response(resp); log_clf(resp_pool, conn, NULL, http_method, http_uri, http_version, status_code, textlen); destroy_pool(resp_pool); return res; } if (strcmp(http_uri, "/metrics") == 0) { int xerrno, use_gzip = FALSE; char *request_username = NULL; if (http_username != NULL) { char *request_password = NULL; int auth_failed = TRUE; pr_trace_msg(trace_channel, 19, "exporter received /metrics request, validating basic auth"); request_username = MHD_basic_auth_get_username_password(conn, &request_password); if (request_username == NULL) { pr_trace_msg(trace_channel, 19, "/metrics request lacks required credentials, rejecting"); auth_failed = TRUE; } else { if (strcmp(request_username, http_username) == 0) { if (strcmp(request_password, http_password) == 0) { char *ptr; /* Authenticated. */ auth_failed = FALSE; pr_trace_msg(trace_channel, 19, "/metrics request from '%s' validated", request_username); /* Free the username memory from libmicrohttpd, but keep a * copy for ourselves, for CLF logging, first. */ ptr = request_username; request_username = pstrdup(resp_pool, ptr); free(ptr); } else { /* Wrong password. */ pr_trace_msg(trace_channel, 19, "/metrics request from '%s' used wrong password, rejecting", request_username); auth_failed = TRUE; } } else { /* Wrong username. */ pr_trace_msg(trace_channel, 19, "/metrics request used wrong username '%s', rejecting", request_username); auth_failed = TRUE; } } if (request_password != NULL) { free(request_password); request_password = NULL; } if (auth_failed == TRUE) { text = "Authentication required\n"; textlen = strlen(text); status_code = MHD_HTTP_UNAUTHORIZED; resp = MHD_create_response_from_buffer(textlen, (void *) text, MHD_RESPMEM_PERSISTENT); (void) MHD_add_response_header(resp, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain"); res = MHD_queue_basic_auth_fail_response(conn, http_realm, resp); MHD_destroy_response(resp); log_clf(resp_pool, conn, request_username, http_method, http_uri, http_version, status_code, textlen); destroy_pool(resp_pool); return res; } } else { pr_trace_msg(trace_channel, 19, "exporter received /metrics request"); } text = prom_registry_get_text(resp_pool, http->registry); xerrno = errno; if (text == NULL) { pr_trace_msg(trace_channel, 3, "error getting registry text: %s", strerror(xerrno)); switch (xerrno) { case ENOENT: status_code = MHD_HTTP_NOT_FOUND; text = "Not Found\n"; break; case EINVAL: status_code = MHD_HTTP_BAD_REQUEST; text = "Bad Request\n"; break; default: status_code = MHD_HTTP_INTERNAL_SERVER_ERROR; text = "Internal Server Error\n"; break; } textlen = strlen(text); resp = MHD_create_response_from_buffer(textlen, (void *) text, MHD_RESPMEM_PERSISTENT); (void) MHD_add_response_header(resp, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain"); res = MHD_queue_response(conn, status_code, resp); MHD_destroy_response(resp); log_clf(resp_pool, conn, request_username, http_method, http_uri, http_version, status_code, textlen); destroy_pool(resp_pool); return res; } textlen = strlen(text); status_code = MHD_HTTP_OK; use_gzip = can_gzip(conn); if (use_gzip == TRUE) { const char *gzipped_text = NULL; size_t gzipped_textlen = 0; pr_trace_msg(trace_channel, 12, "client indicates support for gzip-compressed content, " "attempting to compress text (%lu bytes):\n%.*s", (unsigned long) textlen, (int) textlen, text); gzipped_text = gzip_text(resp_pool, text, textlen, &gzipped_textlen); if (gzipped_text != NULL) { text = gzipped_text; textlen = gzipped_textlen; pr_trace_msg(trace_channel, 19, "registry text:\n(gzip compressed, %lu bytes)", (size_t) textlen); } else { use_gzip = FALSE; } } else { pr_trace_msg(trace_channel, 19, "registry text:\n%.*s", (int) textlen, text); } resp = MHD_create_response_from_buffer(textlen, (void *) text, MHD_RESPMEM_MUST_COPY); (void) MHD_add_response_header(resp, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain"); if (use_gzip == TRUE) { (void) MHD_add_response_header(resp, MHD_HTTP_HEADER_CONTENT_ENCODING, "gzip"); } res = MHD_queue_response(conn, status_code, resp); MHD_destroy_response(resp); log_clf(resp_pool, conn, request_username, http_method, http_uri, http_version, status_code, textlen); destroy_pool(resp_pool); return res; } /* Note that we could use 404 Not Found here, but using 400 Bad Request * leaks less information. */ status_code = MHD_HTTP_BAD_REQUEST; text = "Bad Request\n"; textlen = strlen(text); resp = MHD_create_response_from_buffer(textlen, (void *) text, MHD_RESPMEM_PERSISTENT); (void) MHD_add_response_header(resp, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain"); res = MHD_queue_response(conn, status_code, resp); MHD_destroy_response(resp); log_clf(resp_pool, conn, NULL, http_method, http_uri, http_version, status_code, textlen); destroy_pool(resp_pool); return res; } struct prom_http *prom_http_start(pool *p, const pr_netaddr_t *addr, struct prom_registry *registry, const char *username, const char *password) { struct prom_http *http; pool *http_pool; struct MHD_Daemon *mhd; unsigned int http_port; int flags; if (p == NULL || addr == NULL || registry == NULL) { errno = EINVAL; return NULL; } http_pool = make_sub_pool(p); pr_pool_tag(http_pool, "Prometheus exporter pool"); http = pcalloc(http_pool, sizeof(struct prom_http)); http->pool = http_pool; http->registry = registry; http_port = ntohs(pr_netaddr_get_port(addr)); pr_trace_msg(trace_channel, 9, "starting exporter %son %s:%u", username != NULL ? "requiring basic auth " : "", pr_netaddr_get_ipstr(addr), http_port); flags = MHD_USE_INTERNAL_POLLING_THREAD|MHD_USE_ERROR_LOG|MHD_USE_DEBUG; mhd = MHD_start_daemon(flags, http_port, NULL, NULL, handle_request_cb, http, MHD_OPTION_EXTERNAL_LOGGER, log_cb, NULL, MHD_OPTION_CONNECTION_LIMIT, 1, MHD_OPTION_CONNECTION_TIMEOUT, 10, MHD_OPTION_SOCK_ADDR, pr_netaddr_get_sockaddr(addr), MHD_OPTION_END); if (mhd == NULL) { int xerrno = errno; /* Usually this happens because of an option specification issue. */ (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error starting exporter: %s", strerror(xerrno)); errno = xerrno; return NULL; } http->mhd = mhd; http_username = username; http_password = password; return http; } int prom_http_run_loop(pool *p, struct prom_http *http) { unsigned long sleep_ms = 500; if (p == NULL || http == NULL) { errno = EINVAL; return -1; } (void) p; (void) http; /* Just run in a loop, handling signals. */ while (TRUE) { pr_timer_usleep(sleep_ms * 1000); pr_signals_handle(); } return 0; } int prom_http_stop(pool *p, struct prom_http *http) { if (p == NULL || http == NULL) { errno = EINVAL; return -1; } (void) p; MHD_stop_daemon(http->mhd); return 0; } int prom_http_init(pool *p) { enum MHD_Result result; if (p == NULL) { errno = EINVAL; return -1; } MHD_set_panic_func(panic_cb, NULL); pr_trace_msg(trace_channel, 7, "libmicrohttpd version: %s", MHD_get_version()); /* List of libmicrohttpd features in which we are interested. */ result = MHD_is_feature_supported(MHD_FEATURE_MESSAGES); pr_trace_msg(trace_channel, 7, " debug messages: %s", result == MHD_YES ? "true" : "false"); result = MHD_is_feature_supported(MHD_FEATURE_TLS); pr_trace_msg(trace_channel, 7, " TLS support: %s", result == MHD_YES ? "true" : "false"); result = MHD_is_feature_supported(MHD_FEATURE_IPv6); pr_trace_msg(trace_channel, 7, " IPv6 support: %s", result == MHD_YES ? "true" : "false"); result = MHD_is_feature_supported(MHD_FEATURE_BASIC_AUTH); pr_trace_msg(trace_channel, 7, " Basic Auth support: %s", result == MHD_YES ? "true" : "false"); return 0; } int prom_http_free(void) { MHD_set_panic_func(NULL, NULL); return 0; } proftpd-mod_prometheus-0.2/lib/prometheus/metric.c000066400000000000000000000645711410622367000224670ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus metric implementation * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #include "mod_prometheus.h" #include "prometheus/metric.h" #include "prometheus/metric/db.h" #include "prometheus/text.h" struct prom_histogram_bucket { int64_t bucket_id; int is_inf_bucket; double upper_bound; const char *upper_bound_text; }; struct prom_metric { pool *pool; struct prom_dbh *dbh; const char *name; /* Counter */ int64_t counter_id; const char *counter_name; size_t counter_namelen; const char *counter_help; size_t counter_helplen; /* Gauge */ int64_t gauge_id; const char *gauge_name; size_t gauge_namelen; const char *gauge_help; size_t gauge_helplen; /* Histogram */ const char *histogram_name; size_t histogram_namelen; const char *histogram_help; size_t histogram_helplen; unsigned int histogram_bucket_count; struct prom_histogram_bucket **histogram_buckets; const char *histogram_count_name; int64_t histogram_count_id; const char *histogram_sum_name; int64_t histogram_sum_id; }; static const char *trace_channel = "prometheus.metric"; /* Returns the name of the given metric. */ const char *prom_metric_get_name(struct prom_metric *metric) { if (metric == NULL) { errno = EINVAL; return NULL; } return metric->name; } static struct prom_text *add_help_text(struct prom_text *text, const char *registry_name, size_t registry_namelen, const char *name, size_t namelen, const char *help, size_t helplen) { prom_text_add_str(text, "# HELP ", 7); prom_text_add_str(text, registry_name, registry_namelen); prom_text_add_byte(text, '_'); prom_text_add_str(text, name, namelen); prom_text_add_byte(text, ' '); prom_text_add_str(text, help, helplen); prom_text_add_str(text, ".\n", 2); return text; } static struct prom_text *add_type_text(struct prom_text *text, const char *registry_name, size_t registry_namelen, const char *name, size_t namelen, int metric_type) { prom_text_add_str(text, "# TYPE ", 7); prom_text_add_str(text, registry_name, registry_namelen); prom_text_add_byte(text, '_'); prom_text_add_str(text, name, namelen); switch (metric_type) { case PROM_METRIC_TYPE_COUNTER: prom_text_add_str(text, " counter\n", 9); break; case PROM_METRIC_TYPE_GAUGE: prom_text_add_str(text, " gauge\n", 7); break; case PROM_METRIC_TYPE_HISTOGRAM: prom_text_add_str(text, " histogram\n", 11); break; default: break; } return text; } static struct prom_text *add_histogram_text(struct prom_text *text, const char *registry_name, size_t registry_namelen, const char *name, size_t namelen, const char *suffix, size_t suffixlen, const array_header *results) { register unsigned int i; char **elts; if (results->nelts == 0) { return text; } elts = results->elts; for (i = 0; i < results->nelts; i += 2) { double sample_val; char sample_text[50], *sample_labels, *ptr = NULL; size_t sample_labelslen; int sample_textlen; sample_val = strtod(elts[i], &ptr); memset(sample_text, '\0', sizeof(sample_text)); sample_textlen = snprintf(sample_text, sizeof(sample_text)-1, "%0.17g", sample_val); sample_labels = elts[i+1]; sample_labelslen = strlen(sample_labels); prom_text_add_str(text, registry_name, registry_namelen); prom_text_add_byte(text, '_'); prom_text_add_str(text, name, namelen); prom_text_add_str(text, suffix, suffixlen); if (sample_labelslen > 0) { prom_text_add_str(text, sample_labels, sample_labelslen); } prom_text_add_byte(text, ' '); prom_text_add_str(text, sample_text, sample_textlen); prom_text_add_byte(text, '\n'); } return text; } static struct prom_text *add_metric_type_text(pool *p, struct prom_metric *metric, struct prom_text *text, const char *registry_name, size_t registry_namelen, int metric_type) { register unsigned int i; const array_header *results, *histogram_counts = NULL, *histogram_sums = NULL; const char *type_name, *type_help; size_t type_namelen, type_helplen; char **elts; results = prom_metric_get(p, metric, metric_type, &histogram_counts, &histogram_sums); if (results == NULL) { return NULL; } switch (metric_type) { case PROM_METRIC_TYPE_COUNTER: type_name = metric->counter_name; type_namelen = metric->counter_namelen; type_help = metric->counter_help; type_helplen = metric->counter_helplen; break; case PROM_METRIC_TYPE_GAUGE: type_name = metric->gauge_name; type_namelen = metric->gauge_namelen; type_help = metric->gauge_help; type_helplen = metric->gauge_helplen; break; case PROM_METRIC_TYPE_HISTOGRAM: type_name = metric->histogram_name; type_namelen = metric->histogram_namelen; type_help = metric->histogram_help; type_helplen = metric->histogram_helplen; break; default: errno = EINVAL; return NULL; } add_help_text(text, registry_name, registry_namelen, type_name, type_namelen, type_help, type_helplen); add_type_text(text, registry_name, registry_namelen, type_name, type_namelen, metric_type); if (results->nelts == 0) { /* Provide the default value of 0. */ prom_text_add_str(text, registry_name, registry_namelen); prom_text_add_byte(text, '_'); prom_text_add_str(text, type_name, type_namelen); prom_text_add_str(text, " 0\n", 3); return text; } elts = results->elts; for (i = 0; i < results->nelts; i += 2) { double sample_val; char sample_text[50], *sample_labels, *ptr = NULL; size_t sample_labelslen; int sample_textlen; sample_val = strtod(elts[i], &ptr); memset(sample_text, '\0', sizeof(sample_text)); sample_textlen = snprintf(sample_text, sizeof(sample_text)-1, "%0.17g", sample_val); sample_labels = elts[i+1]; sample_labelslen = strlen(sample_labels); prom_text_add_str(text, registry_name, registry_namelen); prom_text_add_byte(text, '_'); prom_text_add_str(text, type_name, type_namelen); /* XXX For histogram buckets, ensure "+Inf" bucket is last. */ if (metric_type == PROM_METRIC_TYPE_HISTOGRAM) { /* For histograms, `results` contains the bucket samples; name them * accordingly. */ prom_text_add_str(text, "_bucket", 7); } if (sample_labelslen > 0) { prom_text_add_str(text, sample_labels, sample_labelslen); } prom_text_add_byte(text, ' '); prom_text_add_str(text, sample_text, sample_textlen); prom_text_add_byte(text, '\n'); } if (metric_type == PROM_METRIC_TYPE_HISTOGRAM) { add_histogram_text(text, registry_name, registry_namelen, type_name, type_namelen, "_count", 6, histogram_counts); add_histogram_text(text, registry_name, registry_namelen, type_name, type_namelen, "_sum", 4, histogram_sums); } return text; } /* Get the Prometheus text for the given metric: for each metric * type, add: * * "# HELP name ...\n" * "# TYPE name ...\n" * "name sample_val\n" */ const char *prom_metric_get_text(pool *p, struct prom_metric *metric, const char *registry_name, size_t *len) { int xerrno; pool *tmp_pool; size_t registry_namelen; struct prom_text *text; char *res; if (p == NULL || metric == NULL || registry_name == NULL || len == NULL) { errno = EINVAL; return NULL; } registry_namelen = strlen(registry_name); tmp_pool = make_sub_pool(p); text = prom_text_create(tmp_pool); add_metric_type_text(tmp_pool, metric, text, registry_name, registry_namelen, PROM_METRIC_TYPE_COUNTER); add_metric_type_text(tmp_pool, metric, text, registry_name, registry_namelen, PROM_METRIC_TYPE_GAUGE); add_metric_type_text(tmp_pool, metric, text, registry_name, registry_namelen, PROM_METRIC_TYPE_HISTOGRAM); res = prom_text_get_str(p, text, len); xerrno = errno; if (res != NULL) { pr_trace_msg(trace_channel, 19, "converted '%s' metric to text:\n%.*s", metric->name, (int) *len, res); } prom_text_destroy(text); destroy_pool(tmp_pool); errno = xerrno; return res; } /* Returns the samples collected for this metric and type. */ const array_header *prom_metric_get(pool *p, struct prom_metric *metric, int metric_type, const array_header **histogram_counts, const array_header **histogram_sums) { const array_header *results = NULL; if (p == NULL || metric == NULL) { errno = EINVAL; return NULL; } switch (metric_type) { case PROM_METRIC_TYPE_COUNTER: if (metric->counter_name == NULL) { /* No counter associated with this metric. */ errno = EPERM; return NULL; } results = prom_metric_db_sample_get(p, metric->dbh, metric->counter_id); if (results != NULL) { pr_trace_msg(trace_channel, 17, "found samples (%d) for counter metric '%s'", results->nelts/2, metric->counter_name); } break; case PROM_METRIC_TYPE_GAUGE: if (metric->gauge_name == NULL) { /* No gauge associated with this metric. */ errno = EPERM; return NULL; } results = prom_metric_db_sample_get(p, metric->dbh, metric->gauge_id); if (results != NULL) { pr_trace_msg(trace_channel, 17, "found samples (%d) for gauge metric '%s'", results->nelts/2, metric->gauge_name); } break; case PROM_METRIC_TYPE_HISTOGRAM: { register unsigned int i; const array_header *sample_results; if (metric->histogram_name == NULL) { /* No histogram associated with this metric. */ errno = EPERM; return NULL; } /* For histograms, the caller needs to provide ways to return the * count/sum as well. */ if (histogram_counts == NULL || histogram_sums == NULL) { errno = EINVAL; return NULL; } for (i = 0; i < metric->histogram_bucket_count; i++) { struct prom_histogram_bucket *bucket; const array_header *bucket_results; bucket = ((struct prom_histogram_bucket **) metric->histogram_buckets)[i]; bucket_results = prom_metric_db_sample_get(p, metric->dbh, bucket->bucket_id); if (bucket_results != NULL) { pr_trace_msg(trace_channel, 17, "found samples (%d) for histogram bucket '%s' metric '%s'", bucket_results->nelts/2, bucket->upper_bound_text, metric->histogram_name); } if (results != NULL) { array_cat((array_header *) results, bucket_results); } else { results = bucket_results; } } sample_results = prom_metric_db_sample_get(p, metric->dbh, metric->histogram_count_id); if (sample_results != NULL) { pr_trace_msg(trace_channel, 17, "found samples (%d) for histogram bucket 'count' metric '%s'", sample_results->nelts/2, metric->histogram_name); } *histogram_counts = sample_results; sample_results = prom_metric_db_sample_get(p, metric->dbh, metric->histogram_sum_id); if (sample_results != NULL) { pr_trace_msg(trace_channel, 17, "found samples (%d) for histogram bucket 'sum' metric '%s'", sample_results->nelts/2, metric->histogram_name); } *histogram_sums = sample_results; return results; } default: pr_trace_msg(trace_channel, 9, "unknown metric type %d requested for '%s'", metric_type, metric->name); errno = EINVAL; return NULL; } return results; } int prom_metric_decr(pool *p, const struct prom_metric *metric, uint32_t val, pr_table_t *labels) { int res, xerrno; pool *tmp_pool; struct prom_text *text; const char *label_str; if (p == NULL || metric == NULL) { errno = EINVAL; return -1; } /* Decrement operation only supported for gauges. */ if (metric->gauge_name == NULL) { errno = EPERM; return -1; } tmp_pool = make_sub_pool(p); text = prom_text_create(tmp_pool); label_str = prom_text_from_labels(tmp_pool, text, labels); res = prom_metric_db_sample_decr(p, metric->dbh, metric->gauge_id, (double) val, label_str); xerrno = errno; prom_text_destroy(text); destroy_pool(tmp_pool); errno = xerrno; return res; } int prom_metric_incr_type(pool *p, const struct prom_metric *metric, uint32_t val, pr_table_t *labels, int metric_type) { int res = 0, xerrno; pool *tmp_pool; struct prom_text *text; const char *metric_name, *label_str; int64_t metric_id; if (p == NULL || metric == NULL) { errno = EINVAL; return -1; } /* Increment operation only supported for counters/gauges. */ switch (metric_type) { case PROM_METRIC_TYPE_COUNTER: if (metric->counter_name == NULL) { errno = EPERM; return -1; } metric_name = metric->counter_name; metric_id = metric->counter_id; break; case PROM_METRIC_TYPE_GAUGE: if (metric->gauge_name == NULL) { errno = EPERM; return -1; } metric_name = metric->gauge_name; metric_id = metric->gauge_id; break; case PROM_METRIC_TYPE_HISTOGRAM: errno = EPERM; return -1; default: errno = EINVAL; return -1; } tmp_pool = make_sub_pool(p); text = prom_text_create(tmp_pool); label_str = prom_text_from_labels(tmp_pool, text, labels); res = prom_metric_db_sample_incr(p, metric->dbh, metric_id, (double) val, label_str); xerrno = errno; if (res < 0) { pr_trace_msg(trace_channel, 12, "error incrementing '%s' by %lu: %s", metric_name, (unsigned long) val, strerror(xerrno)); } prom_text_destroy(text); destroy_pool(tmp_pool); errno = xerrno; return res; } int prom_metric_incr(pool *p, const struct prom_metric *metric, uint32_t val, pr_table_t *labels) { if (metric == NULL) { errno = EINVAL; return -1; } /* Increment operation only supported for counters/gauges. */ if (metric->counter_name == NULL && metric->gauge_name == NULL) { errno = EPERM; return -1; } if (metric->counter_name != NULL) { int res; res = prom_metric_incr_type(p, metric, val, labels, PROM_METRIC_TYPE_COUNTER); if (res < 0) { return -1; } } if (metric->gauge_name != NULL) { int res; res = prom_metric_incr_type(p, metric, val, labels, PROM_METRIC_TYPE_GAUGE); if (res < 0) { return -1; } } return 0; } int prom_metric_observe(pool *p, const struct prom_metric *metric, double val, pr_table_t *labels) { register int i; int res; pool *tmp_pool; struct prom_text *text; const char *label_str; if (p == NULL || metric == NULL) { errno = EINVAL; return -1; } /* Observe operation only supported for histograms. */ if (metric->histogram_name == NULL) { errno = EPERM; return -1; } tmp_pool = make_sub_pool(p); /* We start with the largest bucket, and work our way down. */ for (i = metric->histogram_bucket_count-1; i >= 0; i--) { struct prom_histogram_bucket *bucket; bucket = ((struct prom_histogram_bucket **) metric->histogram_buckets)[i]; if (val > bucket->upper_bound && bucket->is_inf_bucket == FALSE) { /* Value is too large for this bucket. */ break; } (void) pr_table_add(labels, "le", bucket->upper_bound_text, 0); text = prom_text_create(tmp_pool); label_str = prom_text_from_labels(tmp_pool, text, labels); res = prom_metric_db_sample_incr(p, metric->dbh, bucket->bucket_id, (double) 1.0, label_str); if (res < 0) { pr_trace_msg(trace_channel, 12, "error observing '%s' with %g: %s", metric->histogram_name, val, strerror(errno)); } prom_text_destroy(text); (void) pr_table_remove(labels, "le", NULL); } text = prom_text_create(tmp_pool); label_str = prom_text_from_labels(tmp_pool, text, labels); res = prom_metric_db_sample_incr(p, metric->dbh, metric->histogram_count_id, (double) 1.0, label_str); if (res < 0) { pr_trace_msg(trace_channel, 12, "error incrementing '%s' by %lu: %s", metric->histogram_count_name, (unsigned long) val, strerror(errno)); } res = prom_metric_db_sample_incr(p, metric->dbh, metric->histogram_sum_id, val, label_str); if (res < 0) { pr_trace_msg(trace_channel, 12, "error incrementing '%s' by %lu: %s", metric->histogram_sum_name, (unsigned long) val, strerror(errno)); } prom_text_destroy(text); destroy_pool(tmp_pool); return 0; } int prom_metric_set(pool *p, const struct prom_metric *metric, uint32_t val, pr_table_t *labels) { int res, xerrno; pool *tmp_pool; struct prom_text *text; const char *label_str; if (p == NULL || metric == NULL) { errno = EINVAL; return -1; } /* Set operation only supported for gauges. */ if (metric->gauge_name == NULL) { errno = EPERM; return -1; } tmp_pool = make_sub_pool(p); text = prom_text_create(tmp_pool); label_str = prom_text_from_labels(tmp_pool, text, labels); res = prom_metric_db_sample_set(p, metric->dbh, metric->gauge_id, (double) val, label_str); xerrno = errno; prom_text_destroy(text); destroy_pool(tmp_pool); errno = xerrno; return res; } int prom_metric_add_counter(struct prom_metric *metric, const char *suffix, const char *help_text) { int res; int64_t counter_id; if (metric == NULL || help_text == NULL) { errno = EINVAL; return -1; } if (suffix != NULL) { metric->counter_name = pstrcat(metric->pool, metric->name, "_", suffix, NULL); } else { metric->counter_name = metric->name; } metric->counter_namelen = strlen(metric->counter_name); metric->counter_help = pstrdup(metric->pool, help_text); metric->counter_helplen = strlen(metric->counter_help); res = prom_metric_db_exists(metric->pool, metric->dbh, metric->counter_name); if (res == 0) { pr_trace_msg(trace_channel, 3, "'%s' metric already exists in database", metric->counter_name); errno = EEXIST; return -1; } res = prom_metric_db_create(metric->pool, metric->dbh, metric->counter_name, PROM_METRIC_TYPE_COUNTER, &counter_id); if (res < 0) { pr_trace_msg(trace_channel, 3, "error adding '%s' metric to database: %s", metric->counter_name, strerror(errno)); errno = EEXIST; return -1; } metric->counter_id = counter_id; pr_trace_msg(trace_channel, 27, "added '%s' counter metric (ID %lld) to database", metric->counter_name, (long long int) metric->counter_id); return 0; } int prom_metric_add_gauge(struct prom_metric *metric, const char *suffix, const char *help_text) { int res; int64_t gauge_id; if (metric == NULL || help_text == NULL) { errno = EINVAL; return -1; } if (suffix != NULL) { metric->gauge_name = pstrcat(metric->pool, metric->name, "_", suffix, NULL); } else { metric->gauge_name = metric->name; } metric->gauge_namelen = strlen(metric->gauge_name); metric->gauge_help = pstrdup(metric->pool, help_text); metric->gauge_helplen = strlen(metric->gauge_help); res = prom_metric_db_exists(metric->pool, metric->dbh, metric->gauge_name); if (res == 0) { pr_trace_msg(trace_channel, 3, "'%s' metric already exists in database", metric->gauge_name); errno = EEXIST; return -1; } res = prom_metric_db_create(metric->pool, metric->dbh, metric->gauge_name, PROM_METRIC_TYPE_GAUGE, &gauge_id); if (res < 0) { pr_trace_msg(trace_channel, 3, "error adding '%s' metric to database: %s", metric->gauge_name, strerror(errno)); errno = EEXIST; return -1; } metric->gauge_id = gauge_id; pr_trace_msg(trace_channel, 27, "added '%s' gauge metric (ID %lld) to database", metric->gauge_name, (long long int) metric->gauge_id); return 0; } static const char *get_double_text(pool *p, double val) { char *text; size_t text_len; text_len = 50; text = pcalloc(p, text_len); snprintf(text, text_len-1, "%f", val); if (strstr(text, ".") == NULL) { strcat(text, ".0"); } return text; } int prom_metric_add_histogram(struct prom_metric *metric, const char *suffix, const char *help_text, unsigned int bucket_count, ...) { register unsigned int i; int res, xerrno, have_error = FALSE; va_list ap; if (metric == NULL || help_text == NULL) { errno = EINVAL; return -1; } if (suffix != NULL) { metric->histogram_name = pstrcat(metric->pool, metric->name, "_", suffix, NULL); } else { metric->histogram_name = metric->name; } metric->histogram_namelen = strlen(metric->histogram_name); metric->histogram_help = pstrdup(metric->pool, help_text); metric->histogram_helplen = strlen(metric->histogram_help); /* Add one more for the "+Inf" bucket. */ metric->histogram_bucket_count = bucket_count + 1; metric->histogram_buckets = pcalloc(metric->pool, sizeof(struct prom_histogram_bucket *) * metric->histogram_bucket_count); for (i = 0; i < metric->histogram_bucket_count; i++) { metric->histogram_buckets[i] = pcalloc(metric->pool, sizeof(struct prom_histogram_bucket)); } va_start(ap, bucket_count); for (i = 0; i < metric->histogram_bucket_count; i++) { struct prom_histogram_bucket *bucket; const char *sample_name; bucket = ((struct prom_histogram_bucket **) metric->histogram_buckets)[i]; if (i != metric->histogram_bucket_count-1) { bucket->upper_bound = va_arg(ap, double); bucket->upper_bound_text = get_double_text(metric->pool, bucket->upper_bound); sample_name = pstrcat(metric->pool, metric->histogram_name, "_", bucket->upper_bound_text, NULL); } else { /* The "+Inf" bucket. */ bucket->is_inf_bucket = TRUE; bucket->upper_bound_text = pstrdup(metric->pool, "+Inf"); sample_name = pstrcat(metric->pool, metric->histogram_name, "_inf", NULL); } res = prom_metric_db_exists(metric->pool, metric->dbh, sample_name); if (res == 0) { pr_trace_msg(trace_channel, 3, "'%s' metric already exists in database", sample_name); xerrno = EEXIST; have_error = TRUE; break; } res = prom_metric_db_create(metric->pool, metric->dbh, sample_name, PROM_METRIC_TYPE_HISTOGRAM, &(bucket->bucket_id)); if (res < 0) { pr_trace_msg(trace_channel, 3, "error adding '%s' metric to database: %s", sample_name, strerror(errno)); xerrno = EEXIST; have_error = TRUE; break; } } va_end(ap); if (have_error == TRUE) { errno = xerrno; return -1; } /* The histogram "count" sample. */ metric->histogram_count_name = pstrcat(metric->pool, metric->histogram_name, "_count", NULL); res = prom_metric_db_exists(metric->pool, metric->dbh, metric->histogram_count_name); if (res == 0) { pr_trace_msg(trace_channel, 3, "'%s' metric already exists in database", metric->histogram_count_name); errno = EEXIST; return -1; } res = prom_metric_db_create(metric->pool, metric->dbh, metric->histogram_count_name, PROM_METRIC_TYPE_HISTOGRAM, &(metric->histogram_count_id)); if (res < 0) { pr_trace_msg(trace_channel, 3, "error adding '%s' metric to database: %s", metric->histogram_count_name, strerror(errno)); errno = EEXIST; return -1; } /* The histogram "sum" sample. */ metric->histogram_sum_name = pstrcat(metric->pool, metric->histogram_name, "_sum", NULL); res = prom_metric_db_exists(metric->pool, metric->dbh, metric->histogram_sum_name); if (res == 0) { pr_trace_msg(trace_channel, 3, "'%s' metric already exists in database", metric->histogram_sum_name); errno = EEXIST; return -1; } res = prom_metric_db_create(metric->pool, metric->dbh, metric->histogram_sum_name, PROM_METRIC_TYPE_HISTOGRAM, &(metric->histogram_sum_id)); if (res < 0) { pr_trace_msg(trace_channel, 3, "error adding '%s' metric to database: %s", metric->histogram_sum_name, strerror(errno)); errno = EEXIST; return -1; } pr_trace_msg(trace_channel, 27, "added '%s' histogram metric (count ID %lld, sum ID %lld) to database", metric->histogram_name, (long long) metric->histogram_count_id, (long long) metric->histogram_sum_id); return 0; } int prom_metric_set_dbh(struct prom_metric *metric, struct prom_dbh *dbh) { if (metric == NULL || dbh == NULL) { errno = EINVAL; return -1; } metric->dbh = dbh; return 0; } struct prom_metric *prom_metric_create(pool *p, const char *name, struct prom_dbh *dbh) { pool *metric_pool; struct prom_metric *metric; if (p == NULL || name == NULL || dbh == NULL) { errno = EINVAL; return NULL; } metric_pool = make_sub_pool(p); pr_pool_tag(metric_pool, "Prometheus metric pool"); metric = pcalloc(metric_pool, sizeof(struct prom_metric)); metric->pool = metric_pool; metric->name = pstrdup(metric->pool, name); metric->dbh = dbh; return metric; } int prom_metric_destroy(pool *p, struct prom_metric *metric) { if (metric == NULL) { errno = EINVAL; return -1; } destroy_pool(metric->pool); return 0; } struct prom_dbh *prom_metric_init(pool *p, const char *tables_path) { struct prom_dbh *dbh; dbh = prom_metric_db_init(p, tables_path, 0); if (dbh == NULL) { int xerrno = errno; (void) pr_log_pri(PR_LOG_NOTICE, MOD_PROMETHEUS_VERSION ": failed to initialize metrics datastore: %s", strerror(xerrno)); errno = xerrno; return NULL; } return dbh; } int prom_metric_free(pool *p, struct prom_dbh *dbh) { int res; res = prom_metric_db_close(p, dbh); return res; } proftpd-mod_prometheus-0.2/lib/prometheus/metric/000077500000000000000000000000001410622367000223065ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/lib/prometheus/metric/db.c000066400000000000000000000410631410622367000230430ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus metrics datastore implementation * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #include "mod_prometheus.h" #include "prometheus/db.h" #include "prometheus/metric/db.h" #define PROM_METRICS_DB_SCHEMA_NAME "prom_metrics" #define PROM_METRICS_DB_SCHEMA_VERSION 1 static const char *trace_channel = "prometheus.metric.db"; static int metrics_db_add_schema(pool *p, struct prom_dbh *dbh, const char *db_path) { int res; const char *stmt, *errstr = NULL; /* CREATE TABLE metrics ( * metric_id INTEGER NOT NULL PRIMARY KEY, * metric_name TEXT NOT NULL, * metric_type INTEGER NOT NULL * ); */ stmt = "CREATE TABLE IF NOT EXISTS metrics (metric_id INTEGER NOT NULL PRIMARY KEY, metric_name TEXT NOT NULL, metric_type INTEGER NOT NULL);"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error executing '%s': %s", stmt, errstr); errno = EPERM; return -1; } /* CREATE INDEX metric_id_idx */ stmt = "CREATE INDEX IF NOT EXISTS metric_id_idx ON metrics (metric_id);"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error executing '%s': %s", stmt, errstr); errno = EPERM; return -1; } /* CREATE TABLE metric_samples ( * sample_id INTEGER NOT NULL PRIMARY KEY, * metric_id INTEGER NOT NULL, * sample_value DOUBLE NOT NULL, * sample_labels TEXT NOT NULL, * FOREIGN KEY (metric_id) REFERENCES metrics (metric_id) * ); */ stmt = "CREATE TABLE IF NOT EXISTS metric_samples (sample_id INTEGER NOT NULL PRIMARY KEY, metric_id INTEGER NOT NULL, sample_value DOUBLE NOT NULL, sample_labels TEXT NOT NULL, FOREIGN KEY (metric_id) REFERENCES metrics (metric_id));"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error executing '%s': %s", stmt, errstr); errno = EPERM; return -1; } /* CREATE INDEX sample_id_idx */ stmt = "CREATE INDEX IF NOT EXISTS sample_id_idx ON metric_samples (sample_id);"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error executing '%s': %s", stmt, errstr); errno = EPERM; return -1; } /* CREATE INDEX metric_id_sample_labels_idx */ stmt = "CREATE INDEX IF NOT EXISTS metric_id_sample_labels_idx ON metric_samples (metric_id, sample_labels);"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error executing '%s': %s", stmt, errstr); errno = EPERM; return -1; } return 0; } static int metrics_db_truncate_tables(pool *p, struct prom_dbh *dbh) { int res; const char *index_name, *stmt, *errstr = NULL; stmt = "DELETE FROM metric_samples;"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error executing '%s': %s", stmt, errstr); errno = EPERM; return -1; } stmt = "DELETE FROM metrics;"; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error executing '%s': %s", stmt, errstr); errno = EPERM; return -1; } /* Note: don't forget to rebuild the indices, too! */ index_name = "sample_id_idx"; res = prom_db_reindex(p, dbh, index_name, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error reindexing '%s': %s", index_name, errstr); errno = EPERM; return -1; } index_name = "metric_id_idx"; res = prom_db_reindex(p, dbh, index_name, &errstr); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error reindexing '%s': %s", index_name, errstr); errno = EPERM; return -1; } return 0; } int prom_metric_db_create(pool *p, struct prom_dbh *dbh, const char *metric_name, int metric_type, int64_t *row_id) { int res, xerrno; const char *stmt, *errstr = NULL; array_header *results; if (p == NULL || dbh == NULL || metric_name == NULL) { errno = EINVAL; return -1; } stmt = "INSERT INTO metrics (metric_name, metric_type) VALUES (?, ?);"; res = prom_db_prepare_stmt(p, dbh, stmt); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 1, PROM_DB_BIND_TYPE_TEXT, (void *) metric_name); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 2, PROM_DB_BIND_TYPE_INT, (void *) &metric_type); if (res < 0) { return -1; } results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); xerrno = errno; (void) prom_db_finish_stmt(p, dbh, stmt); if (results == NULL) { pr_trace_msg(trace_channel, 7, "error executing '%s': %s", stmt, errstr ? errstr : strerror(xerrno)); errno = EPERM; return -1; } if (row_id != NULL) { res = prom_db_last_row_id(p, dbh, row_id); if (res < 0) { pr_trace_msg(trace_channel, 7, "error obtaining last row ID for metric '%s', type %d: %s", metric_name, metric_type, strerror(errno)); } } return 0; } int prom_metric_db_exists(pool *p, struct prom_dbh *dbh, const char *metric_name) { int res, xerrno; const char *stmt, *errstr = NULL; array_header *results; if (p == NULL || dbh == NULL || metric_name == NULL) { errno = EINVAL; return -1; } stmt = "SELECT metric_id FROM metrics WHERE metric_name = ?;"; res = prom_db_prepare_stmt(p, dbh, stmt); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 1, PROM_DB_BIND_TYPE_TEXT, (void *) metric_name); if (res < 0) { return -1; } results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); xerrno = errno; (void) prom_db_finish_stmt(p, dbh, stmt); if (results == NULL) { pr_trace_msg(trace_channel, 7, "error executing '%s': %s", stmt, errstr ? errstr : strerror(xerrno)); errno = EPERM; return -1; } if (results->nelts == 0) { errno = ENOENT; return -1; } return 0; } int prom_metric_db_sample_exists(pool *p, struct prom_dbh *dbh, int64_t metric_id, const char *sample_labels) { int res, xerrno; const char *stmt, *errstr = NULL; array_header *results; if (sample_labels == NULL) { errno = EINVAL; return -1; } stmt = "SELECT sample_value FROM metric_samples WHERE metric_id = ? AND sample_labels = ?;"; res = prom_db_prepare_stmt(p, dbh, stmt); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 1, PROM_DB_BIND_TYPE_INT, (void *) &metric_id); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 2, PROM_DB_BIND_TYPE_TEXT, (void *) sample_labels); if (res < 0) { return -1; } results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); xerrno = errno; (void) prom_db_finish_stmt(p, dbh, stmt); if (results == NULL) { pr_trace_msg(trace_channel, 7, "error executing '%s': %s", stmt, errstr ? errstr : strerror(xerrno)); errno = EPERM; return -1; } if (results->nelts == 0) { errno = ENOENT; return -1; } return 0; } static int db_sample_create(pool *p, struct prom_dbh *dbh, int64_t metric_id, double sample_val, const char *sample_labels) { int res, xerrno; const char *stmt, *errstr = NULL; array_header *results; stmt = "INSERT INTO metric_samples (metric_id, sample_value, sample_labels) VALUES (?, ?, ?);"; res = prom_db_prepare_stmt(p, dbh, stmt); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 1, PROM_DB_BIND_TYPE_INT, (void *) &metric_id); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 2, PROM_DB_BIND_TYPE_DOUBLE, (void *) &sample_val); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 3, PROM_DB_BIND_TYPE_TEXT, (void *) sample_labels); if (res < 0) { return -1; } results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); xerrno = errno; (void) prom_db_finish_stmt(p, dbh, stmt); if (results == NULL) { pr_trace_msg(trace_channel, 7, "error executing '%s': %s", stmt, errstr ? errstr : strerror(xerrno)); errno = EPERM; return -1; } return 0; } static int db_sample_adj(pool *p, struct prom_dbh *dbh, const char *stmt, int64_t metric_id, double sample_val, const char *sample_labels) { int res, xerrno; const char *errstr = NULL; array_header *results; res = prom_db_prepare_stmt(p, dbh, stmt); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 1, PROM_DB_BIND_TYPE_DOUBLE, (void *) &sample_val); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 2, PROM_DB_BIND_TYPE_INT, (void *) &metric_id); if (res < 0) { return -1; } res = prom_db_bind_stmt(p, dbh, stmt, 3, PROM_DB_BIND_TYPE_TEXT, (void *) sample_labels); if (res < 0) { return -1; } results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); xerrno = errno; (void) prom_db_finish_stmt(p, dbh, stmt); if (results == NULL) { pr_trace_msg(trace_channel, 7, "error executing '%s': %s", stmt, errstr ? errstr : strerror(xerrno)); errno = EPERM; return -1; } return 0; } int prom_metric_db_sample_decr(pool *p, struct prom_dbh *dbh, int64_t metric_id, double sample_val, const char *sample_labels) { int res; const char *stmt; /* NOTE: Beware of race conditions with other processes, due to this * check-and-set sequence, for this sample. * * Consider using wrapping this in a BEGIN/COMMIT block; use EXCLUSIVE? */ res = prom_metric_db_sample_exists(p, dbh, metric_id, sample_labels); if (res < 0) { double init_val = 0.0; if (errno != ENOENT) { return -1; } res = db_sample_create(p, dbh, metric_id, init_val, sample_labels); if (res < 0) { return -1; } } stmt = "UPDATE metric_samples SET sample_value = sample_value - ? WHERE metric_id = ? AND sample_labels = ?;"; return db_sample_adj(p, dbh, stmt, metric_id, sample_val, sample_labels); } int prom_metric_db_sample_incr(pool *p, struct prom_dbh *dbh, int64_t metric_id, double sample_val, const char *sample_labels) { int res; const char *stmt; /* NOTE: Beware of race conditions with other processes, due to this * check-and-set sequence, for this sample. * * Consider using wrapping this in a BEGIN/COMMIT block; use EXCLUSIVE? */ res = prom_metric_db_sample_exists(p, dbh, metric_id, sample_labels); if (res < 0) { double init_val = 0.0; if (errno != ENOENT) { return -1; } res = db_sample_create(p, dbh, metric_id, init_val, sample_labels); if (res < 0) { return -1; } } stmt = "UPDATE metric_samples SET sample_value = sample_value + ? WHERE metric_id = ? AND sample_labels = ?;"; return db_sample_adj(p, dbh, stmt, metric_id, sample_val, sample_labels); } int prom_metric_db_sample_set(pool *p, struct prom_dbh *dbh, int64_t metric_id, double sample_val, const char *sample_labels) { int res; const char *stmt; /* NOTE: Beware of race conditions with other processes, due to this * check-and-set sequence, for this sample. * * Consider using wrapping this in a BEGIN/COMMIT block; use EXCLUSIVE? */ res = prom_metric_db_sample_exists(p, dbh, metric_id, sample_labels); if (res < 0) { double init_val = 0.0; if (errno != ENOENT) { return -1; } res = db_sample_create(p, dbh, metric_id, init_val, sample_labels); if (res < 0) { return -1; } } stmt = "UPDATE metric_samples SET sample_value = ? WHERE metric_id = ? AND sample_labels = ?;"; return db_sample_adj(p, dbh, stmt, metric_id, sample_val, sample_labels); } const array_header *prom_metric_db_sample_get(pool *p, struct prom_dbh *dbh, int64_t metric_id) { int res, xerrno; const char *stmt, *errstr = NULL; array_header *results; if (p == NULL || dbh == NULL) { errno = EINVAL; return NULL; } stmt = "SELECT sample_value, sample_labels FROM metric_samples WHERE metric_id = ? ORDER BY sample_labels ASC;"; res = prom_db_prepare_stmt(p, dbh, stmt); if (res < 0) { return NULL; } res = prom_db_bind_stmt(p, dbh, stmt, 1, PROM_DB_BIND_TYPE_INT, (void *) &metric_id); if (res < 0) { return NULL; } results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); xerrno = errno; (void) prom_db_finish_stmt(p, dbh, stmt); if (results == NULL) { pr_trace_msg(trace_channel, 7, "error executing '%s': %s", stmt, errstr ? errstr : strerror(xerrno)); errno = EPERM; return NULL; } return results; } int prom_metric_db_close(pool *p, struct prom_dbh *dbh) { if (p == NULL) { errno = EINVAL; return -1; } /* TODO: Implement any necessary cleanup */ if (dbh != NULL) { if (prom_db_close(p, dbh) < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error detaching database with schema '%s': %s", PROM_METRICS_DB_SCHEMA_NAME, strerror(errno)); } } return 0; } struct prom_dbh *prom_metric_db_open(pool *p, const char *tables_path) { int xerrno; struct prom_dbh *dbh; const char *db_path; if (p == NULL || tables_path == NULL) { errno = EINVAL; return NULL; } db_path = pdircat(p, tables_path, "metrics.db", NULL); /* Make sure we have our own per-session database handle, per SQLite3 * recommendation. */ PRIVS_ROOT dbh = prom_db_open_readonly_with_version(p, db_path, PROM_METRICS_DB_SCHEMA_NAME, PROM_METRICS_DB_SCHEMA_VERSION, 0); xerrno = errno; PRIVS_RELINQUISH if (dbh == NULL) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error opening database '%s' for schema '%s', version %u: %s", db_path, PROM_METRICS_DB_SCHEMA_NAME, PROM_METRICS_DB_SCHEMA_VERSION, strerror(xerrno)); errno = xerrno; return NULL; } return dbh; } struct prom_dbh *prom_metric_db_init(pool *p, const char *tables_path, int flags) { int db_flags, res, xerrno = 0; const char *db_path = NULL; struct prom_dbh *dbh; if (p == NULL || tables_path == NULL) { errno = EINVAL; return NULL; } db_path = pdircat(p, tables_path, "metrics.db", NULL); db_flags = PROM_DB_OPEN_FL_SCHEMA_VERSION_CHECK|PROM_DB_OPEN_FL_INTEGRITY_CHECK|PROM_DB_OPEN_FL_VACUUM; if (flags & PROM_DB_OPEN_FL_SKIP_VACUUM) { /* If the caller needs us to skip the vacuum, we will. */ db_flags &= ~PROM_DB_OPEN_FL_VACUUM; } PRIVS_ROOT dbh = prom_db_open_with_version(p, db_path, PROM_METRICS_DB_SCHEMA_NAME, PROM_METRICS_DB_SCHEMA_VERSION, db_flags); xerrno = errno; PRIVS_RELINQUISH if (dbh == NULL) { (void) pr_log_pri(PR_LOG_NOTICE, MOD_PROMETHEUS_VERSION ": error opening database '%s' for schema '%s', version %u: %s", db_path, PROM_METRICS_DB_SCHEMA_NAME, PROM_METRICS_DB_SCHEMA_VERSION, strerror(xerrno)); errno = xerrno; return NULL; } if (flags & PROM_DB_OPEN_FL_SKIP_TABLE_INIT) { /* Skip adding/initializing tables. */ return dbh; } res = metrics_db_add_schema(p, dbh, db_path); if (res < 0) { xerrno = errno; (void) pr_log_debug(DEBUG0, MOD_PROMETHEUS_VERSION ": error creating schema in database '%s' for '%s': %s", db_path, PROM_METRICS_DB_SCHEMA_NAME, strerror(xerrno)); (void) prom_db_close(p, dbh); errno = xerrno; return NULL; } res = metrics_db_truncate_tables(p, dbh); if (res < 0) { xerrno = errno; (void) prom_db_close(p, dbh); errno = xerrno; return NULL; } return dbh; } proftpd-mod_prometheus-0.2/lib/prometheus/registry.c000066400000000000000000000203471410622367000230450ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus registry implementation * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #include "mod_prometheus.h" #include "prometheus/registry.h" #include "prometheus/metric.h" #include "prometheus/text.h" struct prom_registry { pool *pool; const char *name; pr_table_t *metrics; /* Pool/list of sorted metric names, for scraping. */ pool *sorted_pool; array_header *sorted_keys; }; static const char *trace_channel = "prometheus.registry"; int prom_registry_add_metric(struct prom_registry *registry, struct prom_metric *metric) { int res; if (registry == NULL || metric == NULL) { errno = EINVAL; return -1; } res = pr_table_add(registry->metrics, prom_metric_get_name(metric), metric, sizeof(void *)); return res; } const struct prom_metric *prom_registry_get_metric( struct prom_registry *registry, const char *metric_name) { if (registry == NULL || metric_name == NULL) { errno = EINVAL; return NULL; } return pr_table_get(registry->metrics, metric_name, NULL); } const char *prom_registry_get_name(struct prom_registry *registry) { if (registry == NULL) { errno = EINVAL; return NULL; } return registry->name; } /* Returns the text for all metrics in the registry. */ const char *prom_registry_get_text(pool *p, struct prom_registry *registry) { pool *tmp_pool; register unsigned int i; struct prom_text *text; int key_count; array_header *keys; char **elts, *str; if (p == NULL || registry == NULL) { errno = EINVAL; return NULL; } /* Sanity check. */ key_count = pr_table_count(registry->metrics); if (key_count == 0) { pr_trace_msg(trace_channel, 17, "'%s' registry has no metrics, returning no text", registry->name); errno = ENOENT; return NULL; } tmp_pool = make_sub_pool(p); text = prom_text_create(tmp_pool); if (registry->sorted_keys != NULL) { keys = registry->sorted_keys; } else { const void *key; keys = make_array(tmp_pool, key_count, sizeof(char *)); pr_table_rewind(registry->metrics); key = pr_table_next(registry->metrics); while (key != NULL) { pr_signals_handle(); /* No need to duplicate this text; it's a pointer to an object we * already have in memory. */ *((char **) push_array(keys)) = (char *) key; key = pr_table_next(registry->metrics); } } elts = keys->elts; for (i = 0; i < keys->nelts; i++) { pool *iter_pool; struct prom_metric *metric; const char *metric_text; size_t metric_textlen; pr_trace_msg(trace_channel, 19, "getting text for '%s' metric", elts[i]); metric = (struct prom_metric *) pr_table_get(registry->metrics, elts[i], NULL); iter_pool = make_sub_pool(tmp_pool); metric_text = prom_metric_get_text(iter_pool, metric, registry->name, &metric_textlen); if (metric_text != NULL) { prom_text_add_str(text, pstrdup(tmp_pool, metric_text), metric_textlen); } else { pr_trace_msg(trace_channel, 7, "error getting '%s' metric text: %s", elts[i], strerror(errno)); } destroy_pool(iter_pool); } prom_text_add_byte(text, '\n'); str = prom_text_get_str(p, text, NULL); prom_text_destroy(text); destroy_pool(tmp_pool); return str; } static int metric_set_dbh_cb(const void *key_data, size_t key_datasz, const void *value_data, size_t value_datasz, void *user_data) { int res; struct prom_metric *metric; struct prom_dbh *dbh; metric = (struct prom_metric *) value_data; dbh = user_data; res = prom_metric_set_dbh(metric, dbh); if (res < 0) { pr_trace_msg(trace_channel, 7, "error setting metric dbh: %s", strerror(errno)); } return 0; } int prom_registry_set_dbh(struct prom_registry *registry, struct prom_dbh *dbh) { int res, xerrno; if (registry == NULL || dbh == NULL) { errno = EINVAL; return -1; } res = pr_table_do(registry->metrics, metric_set_dbh_cb, dbh, PR_TABLE_DO_FL_ALL); xerrno = errno; if (res < 0) { pr_trace_msg(trace_channel, 3, "error doing registry metrics table: %s", strerror(xerrno)); } errno = xerrno; return res; } static int metric_keycmp(const void *a, const void *b) { return strcmp(*((char **) a), *((char **) b)); } int prom_registry_sort_metrics(struct prom_registry *registry) { int key_count; pool *sorted_pool; array_header *sorted_keys; const void *key; if (registry == NULL) { errno = EINVAL; return -1; } /* Discard any existing sorted list. */ if (registry->sorted_pool != NULL) { destroy_pool(registry->sorted_pool); registry->sorted_pool = NULL; registry->sorted_keys = NULL; } /* If there are no metrics, there's nothing to do. */ key_count = pr_table_count(registry->metrics); if (key_count == 0) { return 0; } sorted_pool = make_sub_pool(registry->pool); pr_pool_tag(sorted_pool, "Prometheus Registry sorted metric names"); registry->sorted_pool = sorted_pool; sorted_keys = make_array(sorted_pool, key_count, sizeof(char *)); pr_table_rewind(registry->metrics); key = pr_table_next(registry->metrics); while (key != NULL) { pr_signals_handle(); /* No need to duplicate this text; it's a pointer to an object we * already have in memory. */ *((char **) push_array(sorted_keys)) = (char *) key; key = pr_table_next(registry->metrics); } qsort((void *) sorted_keys->elts, sorted_keys->nelts, sizeof(char *), metric_keycmp); registry->sorted_keys = sorted_keys; if (pr_trace_get_level(trace_channel) > 17) { register unsigned int i; char **keys; pr_trace_msg(trace_channel, 17, "registry '%s' sorted metrics (%d):", registry->name, sorted_keys->nelts); keys = sorted_keys->elts; for (i = 0; i < sorted_keys->nelts; i++) { pr_trace_msg(trace_channel, 17, " %s (%u)", keys[i], i+1); } } return 0; } struct prom_registry *prom_registry_init(pool *p, const char *name) { struct prom_registry *registry; pool *registry_pool; if (p == NULL || name == NULL) { errno = EINVAL; return NULL; } registry_pool = make_sub_pool(p); pr_pool_tag(registry_pool, "Prometheus Registry pool"); registry = pcalloc(registry_pool, sizeof(struct prom_registry)); registry->pool = registry_pool; registry->name = pstrdup(registry->pool, name); registry->metrics = pr_table_nalloc(registry->pool, 0, 8); return registry; } static int metric_free_cb(const void *key_data, size_t key_datasz, const void *value_data, size_t value_datasz, void *user_data) { int res; struct prom_metric *metric; pool *registry_pool; metric = (struct prom_metric *) value_data; registry_pool = user_data; res = prom_metric_destroy(registry_pool, metric); if (res < 0) { pr_trace_msg(trace_channel, 7, "error destroy metric: %s", strerror(errno)); } return 0; } int prom_registry_free(struct prom_registry *registry) { int res; if (registry == NULL) { errno = EINVAL; return -1; } res = pr_table_do(registry->metrics, metric_free_cb, registry->pool, PR_TABLE_DO_FL_ALL); if (res < 0) { pr_trace_msg(trace_channel, 3, "error doing registry metrics table: %s", strerror(errno)); } (void) pr_table_empty(registry->metrics); (void) pr_table_free(registry->metrics); destroy_pool(registry->pool); return 0; } proftpd-mod_prometheus-0.2/lib/prometheus/text.c000066400000000000000000000136361410622367000221640ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus text implementation * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #include "mod_prometheus.h" #include "prometheus/text.h" struct prom_text { pool *pool; char *buf, *ptr; size_t bufsz, buflen; }; #define PROM_TEXT_DEFAULT_BUFFER_SIZE 1024 static const char *trace_channel = "prometheus.text"; static void ensure_text_size(struct prom_text *text, size_t new_textsz) { char *buf, *ptr; size_t buflen; if (new_textsz <= text->bufsz) { /* Nothing to do. */ return; } ptr = pcalloc(text->pool, new_textsz); memcpy(ptr, text->ptr, text->bufsz - text->buflen); buf = ptr + (text->buf - text->ptr); /* buflen is the remaining length of buffer. So we add the increased buffer * size to the existing buflen to obtain the new remaining length. */ buflen = text->buflen + (new_textsz - text->bufsz); text->bufsz = new_textsz; text->buf = buf; text->buflen = buflen; text->ptr = ptr; } int prom_text_add_byte(struct prom_text *text, char ch) { if (text == NULL) { errno = EINVAL; return -1; } if (text->buflen == 0) { ensure_text_size(text, text->bufsz * 2); } pr_trace_msg(trace_channel, 19, "appending character (%c)", ch); *(text->buf++) = ch; text->buflen -= 1; return 0; } int prom_text_add_str(struct prom_text *text, const char *str, size_t sz) { register unsigned int i; if (text == NULL || str == NULL) { errno = EINVAL; return -1; } if (sz == 0) { return 0; } if (text->buflen < sz) { ensure_text_size(text, text->bufsz * 2); } pr_trace_msg(trace_channel, 19, "appending text '%.*s' (%lu)", (int) sz, str, (unsigned long) sz); for (i = 0; i < sz; i++) { *(text->buf++) = str[i]; } text->buflen -= sz; return 0; } char *prom_text_get_str(pool *p, struct prom_text *text, size_t *sz) { char *str; if (p == NULL || text == NULL) { errno = EINVAL; return NULL; } if (text->buflen == text->bufsz) { /* No textual data accumulated yet. */ errno = ENOENT; return NULL; } str = pstrdup(p, text->ptr); if (sz != NULL) { *sz = text->buf - text->ptr; } return str; } static int label_keycmp(const void *a, const void *b) { return strcmp(*((char **) a), *((char **) b)); } /* Note that format for the returned text is very specific. And, for * consistency, we should always sort by key names. Label tables ALWAYS * have strings for keys and values. (If they don't, it's a code bug.) */ const char *prom_text_from_labels(pool *p, struct prom_text *text, pr_table_t *labels) { register unsigned int i; pool *tmp_pool; int key_count = 0; array_header *keys; const void *key; char **key_names, *label_text = NULL; if (p == NULL || text == NULL) { errno = EINVAL; return NULL; } if (labels != NULL) { key_count = pr_table_count(labels); } if (key_count == 0) { return pstrdup(p, ""); } tmp_pool = make_sub_pool(text->pool); /* First, get a list of keys, in sorted order. */ keys = make_array(p, key_count, sizeof(char *)); pr_table_rewind(labels); key = pr_table_next(labels); while (key != NULL) { pr_signals_handle(); *((char **) push_array(keys)) = pstrdup(p, key); key = pr_table_next(labels); } qsort((void *) keys->elts, keys->nelts, sizeof(char *), label_keycmp); /* Now, start building up the textual data. */ key_names = keys->elts; prom_text_add_byte(text, '{'); for (i = 0; i < keys->nelts; i++) { size_t key_namesz, valsz; char *key_name; const void *val; if (i != 0) { prom_text_add_byte(text, ','); } key_name = key_names[i]; key_namesz = strlen(key_name); prom_text_add_str(text, key_name, key_namesz); prom_text_add_byte(text, '='); prom_text_add_byte(text, '"'); val = pr_table_get(labels, key_name, &valsz); /* Note that we _subtract_ one here, because tables store values as * opaque objects, and thus include the terminating NUL for text. But * we do not want to include that NUL in our length calculations. */ prom_text_add_str(text, val, valsz-1); prom_text_add_byte(text, '"'); } prom_text_add_byte(text, '}'); destroy_pool(tmp_pool); /* Finally, get our accumulated textual data. */ label_text = prom_text_get_str(p, text, NULL); pr_trace_msg(trace_channel, 9, "converted labels to text '%s'", label_text); return label_text; } struct prom_text *prom_text_create(pool *p) { pool *text_pool; struct prom_text *text; if (p == NULL) { errno = EINVAL; return NULL; } text_pool = make_sub_pool(p); pr_pool_tag(text_pool, "Prometheus text pool"); text = pcalloc(text_pool, sizeof(struct prom_text)); text->pool = text_pool; text->bufsz = text->buflen = PROM_TEXT_DEFAULT_BUFFER_SIZE; text->ptr = text->buf = pcalloc(text->pool, text->bufsz); return text; } int prom_text_destroy(struct prom_text *text) { if (text == NULL) { errno = EINVAL; return -1; } destroy_pool(text->pool); return 0; } proftpd-mod_prometheus-0.2/mod_prometheus.c000066400000000000000000001734621410622367000212750ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. * * -----DO NOT EDIT BELOW THIS LINE----- * $Archive: mod_prometheus.a $ * $Libraries: -lmicrohttpd -lsqlite3$ */ #include "mod_prometheus.h" #include "prometheus/db.h" #include "prometheus/registry.h" #include "prometheus/metric.h" #include "prometheus/metric/db.h" #include "prometheus/http.h" /* Defaults */ #define PROMETHEUS_DEFAULT_EXPORTER_PORT 9273 extern xaset_t *server_list; int prometheus_logfd = -1; module prometheus_module; pool *prometheus_pool = NULL; static int prometheus_engine = FALSE; static unsigned long prometheus_opts = 0UL; static const char *prometheus_tables_dir = NULL; static uint64_t prometheus_connected_ms = 0; static struct prom_dbh *prometheus_dbh = NULL; static struct prom_registry *prometheus_registry = NULL; static struct prom_http *prometheus_exporter_http = NULL; static pid_t prometheus_exporter_pid = 0; static int prometheus_saw_user_cmd = FALSE; static int prometheus_saw_pass_cmd = FALSE; /* Number of seconds to wait for the exporter process to stop before * we terminate it with extreme prejudice. * * Currently this has a granularity of seconds; needs to be in millsecs * (e.g. for 500 ms timeout). */ static time_t prometheus_exporter_timeout = 1; static void prom_event_decr(const char *metric_name, uint32_t decr, ...) #if defined(__GNUC__) __attribute__ ((sentinel)); #else ; #endif /* GNUC */ static void prom_event_incr(const char *metric_name, uint32_t incr, ...) #if defined(__GNUC__) __attribute__ ((sentinel)); #else ; #endif /* GNUC */ static void prom_event_observe(const char *metric_name, double observed, ...) #if defined(__GNUC__) __attribute__ ((sentinel)); #else ; #endif /* GNUC */ static const char *trace_channel = "prometheus"; static int prom_mkdir(const char *dir, uid_t uid, gid_t gid, mode_t mode) { mode_t prev_mask; struct stat st; int res = -1; pr_fs_clear_cache2(dir); res = pr_fsio_stat(dir, &st); if (res == -1 && errno != ENOENT) { return -1; } /* The directory already exists. */ if (res == 0) { return 0; } /* The given mode is absolute, not subject to any Umask setting. */ prev_mask = umask(0); if (pr_fsio_mkdir(dir, mode) < 0) { int xerrno = errno; (void) umask(prev_mask); errno = xerrno; return -1; } umask(prev_mask); if (pr_fsio_chown(dir, uid, gid) < 0) { return -1; } return 0; } static int prom_mkpath(pool *p, const char *path, uid_t uid, gid_t gid, mode_t mode) { char *currpath = NULL, *tmppath = NULL; struct stat st; pr_fs_clear_cache2(path); if (pr_fsio_stat(path, &st) == 0) { /* Path already exists, nothing to be done. */ errno = EEXIST; return -1; } tmppath = pstrdup(p, path); currpath = "/"; while (tmppath && *tmppath) { char *currdir = strsep(&tmppath, "/"); currpath = pdircat(p, currpath, currdir, NULL); if (prom_mkdir(currpath, uid, gid, mode) < 0) { return -1; } pr_signals_handle(); } return 0; } static int prom_openlog(void) { int res = 0; config_rec *c; c = find_config(main_server->conf, CONF_PARAM, "PrometheusLog", FALSE); if (c != NULL) { const char *path; path = c->argv[0]; if (strncasecmp(path, "none", 5) != 0) { int xerrno; pr_signals_block(); PRIVS_ROOT res = pr_log_openfile(path, &prometheus_logfd, 0600); xerrno = errno; PRIVS_RELINQUISH pr_signals_unblock(); if (res < 0) { if (res == -1) { pr_log_pri(PR_LOG_NOTICE, MOD_PROMETHEUS_VERSION ": notice: unable to open PrometheusLog '%s': %s", path, strerror(xerrno)); } else if (res == PR_LOG_WRITABLE_DIR) { pr_log_pri(PR_LOG_WARNING, MOD_PROMETHEUS_VERSION ": notice: unable to open PrometheusLog '%s': parent directory is " "world-writable", path); } else if (res == PR_LOG_SYMLINK) { pr_log_pri(PR_LOG_WARNING, MOD_PROMETHEUS_VERSION ": notice: unable to open PrometheusLog '%s': cannot log to " "a symlink", path); } } } } return res; } /* We don't want to do the full daemonize() as provided in main.c; we * already forked. */ static void prom_daemonize(const char *daemon_dir) { #ifndef HAVE_SETSID int tty_fd; #endif #ifdef HAVE_SETSID /* setsid() is the preferred way to disassociate from the * controlling terminal */ setsid(); #else /* Open /dev/tty to access our controlling tty (if any) */ tty_fd = open("/dev/tty", O_RDWR); if (tty_fd != -1) { if (ioctl(tty_fd, TIOCNOTTY, NULL) == -1) { perror("ioctl"); exit(1); } close(tty_fd); } #endif /* HAVE_SETSID */ /* Close the three big boys. */ close(fileno(stdin)); close(fileno(stdout)); close(fileno(stderr)); /* Portable way to prevent re-acquiring a tty in the future */ #if defined(HAVE_SETPGID) setpgid(0, getpid()); #else # if defined(SETPGRP_VOID) setpgrp(); # else setpgrp(0, getpid()); # endif /* SETPGRP_VOID */ #endif /* HAVE_SETPGID */ pr_fsio_chdir(daemon_dir, 0); } static pid_t prom_exporter_start(pool *p, const pr_netaddr_t *exporter_addr, const char *username, const char *password) { pid_t exporter_pid; struct prom_dbh *dbh; char *exporter_chroot = NULL; exporter_pid = fork(); switch (exporter_pid) { case -1: pr_log_pri(PR_LOG_ALERT, MOD_PROMETHEUS_VERSION ": unable to fork: %s", strerror(errno)); return 0; case 0: /* We're the child. */ break; default: /* We're the parent. */ return exporter_pid; } /* Reset the cached PID, so that it is correctly reflected in the logs. */ session.pid = getpid(); pr_trace_msg(trace_channel, 3, "forked exporter PID %lu", (unsigned long) session.pid); prom_daemonize(prometheus_tables_dir); /* Install our own signal handlers (mostly to ignore signals) */ (void) signal(SIGALRM, SIG_IGN); (void) signal(SIGHUP, SIG_IGN); (void) signal(SIGUSR1, SIG_IGN); (void) signal(SIGUSR2, SIG_IGN); /* Remove our event listeners. */ pr_event_unregister(&prometheus_module, NULL, NULL); /* Close any database handle inherited from our parent, and open a new * one, per SQLite3 recommendation. */ (void) prom_db_close(prometheus_pool, prometheus_dbh); prometheus_dbh = NULL; dbh = prom_metric_db_open(prometheus_pool, prometheus_tables_dir); if (dbh == NULL) { pr_trace_msg(trace_channel, 3, "exporter error opening '%s' database: %s", prometheus_tables_dir, strerror(errno)); } if (prom_registry_set_dbh(prometheus_registry, dbh) < 0) { pr_trace_msg(trace_channel, 3, "exporter error setting registry dbh: %s", strerror(errno)); } PRIVS_ROOT if (getuid() == PR_ROOT_UID) { int res; /* Chroot to the PrometheusTables/empty/ directory before dropping * root privs. */ exporter_chroot = pdircat(prometheus_pool, prometheus_tables_dir, "empty", NULL); res = chroot(exporter_chroot); if (res < 0) { int xerrno = errno; PRIVS_RELINQUISH (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "unable to chroot to PrometheusTables/empty/ directory '%s': %s", exporter_chroot, strerror(xerrno)); exit(0); } if (chdir("/") < 0) { int xerrno = errno; PRIVS_RELINQUISH (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "unable to chdir to root directory within chroot: %s", strerror(xerrno)); exit(0); } } pr_proctitle_set("(listening for Prometheus requests)"); /* Make the exporter process have the identity of the configured daemon * User/Group. */ session.uid = geteuid(); session.gid = getegid(); PRIVS_REVOKE prometheus_exporter_http = prom_http_start(p, exporter_addr, prometheus_registry, username, password); if (prometheus_exporter_http == NULL) { return 0; } if (exporter_chroot != NULL) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "exporter process running with UID %s, GID %s, restricted to '%s'", pr_uid2str(prometheus_pool, getuid()), pr_gid2str(prometheus_pool, getgid()), exporter_chroot); } else { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "exporter process running with UID %s, GID %s, located in '%s'", pr_uid2str(prometheus_pool, getuid()), pr_gid2str(prometheus_pool, getgid()), getcwd(NULL, 0)); } /* This function will exit once the exporter finishes. */ prom_http_run_loop(p, prometheus_exporter_http); pr_trace_msg(trace_channel, 3, "exporter PID %lu exiting", (unsigned long) session.pid); exit(0); } static void prom_exporter_stop(pid_t exporter_pid) { int res, status; time_t start_time = time(NULL); if (exporter_pid == 0) { /* Nothing to do. */ return; } pr_trace_msg(trace_channel, 3, "stopping exporter PID %lu", (unsigned long) exporter_pid); /* Litmus test: is the exporter process still around? If not, there's * nothing for us to do. */ res = kill(exporter_pid, 0); if (res < 0 && errno == ESRCH) { return; } if (prom_http_stop(prometheus_pool, prometheus_exporter_http) < 0) { pr_trace_msg(trace_channel, 3, "error stopping exporter http listener: %s", strerror(errno)); } res = kill(exporter_pid, SIGTERM); if (res < 0) { int xerrno = errno; (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error sending SIGTERM (signal %d) to exporter process ID %lu: %s", SIGTERM, (unsigned long) exporter_pid, strerror(xerrno)); } /* Poll every 500 millsecs. */ pr_timer_usleep(500 * 1000); res = waitpid(exporter_pid, &status, WNOHANG); while (res <= 0) { if (res < 0) { if (errno == EINTR) { pr_signals_handle(); continue; } if (errno == ECHILD) { /* XXX Maybe we shouldn't be using waitpid(2) here, since the * main SIGCHLD handler may handle the termination of the exporter * process? */ return; } if (errno != EINTR) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error waiting for exporter process ID %lu: %s", (unsigned long) exporter_pid, strerror(errno)); status = -1; break; } } /* Check the time elapsed since we started. */ if ((time(NULL) - start_time) > prometheus_exporter_timeout) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "exporter process ID %lu took longer than timeout (%lu secs) to " "stop, sending SIGKILL (signal %d)", (unsigned long) exporter_pid, prometheus_exporter_timeout, SIGKILL); res = kill(exporter_pid, SIGKILL); if (res < 0) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "error sending SIGKILL (signal %d) to exporter process ID %lu: %s", SIGKILL, (unsigned long) exporter_pid, strerror(errno)); } break; } /* Poll every 500 millsecs. */ pr_timer_usleep(500 * 1000); } if (WIFEXITED(status)) { int exit_status; exit_status = WEXITSTATUS(status); (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "exporter process ID %lu terminated normally, with exit status %d", (unsigned long) exporter_pid, exit_status); } if (WIFSIGNALED(status)) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "exporter process ID %lu died from signal %d", (unsigned long) exporter_pid, WTERMSIG(status)); if (WCOREDUMP(status)) { (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "exporter process ID %lu created a coredump", (unsigned long) exporter_pid); } } exporter_pid = 0; prometheus_exporter_http = NULL; } static pr_table_t *prom_get_labels(pool *p) { pr_table_t *labels; labels = pr_table_nalloc(p, 0, 2); (void) pr_table_add(labels, "protocol", pr_session_get_protocol(0), 0); return labels; } static const struct prom_metric *prom_metric_with_labels(pool *p, const char *metric_name, pr_table_t *labels, va_list ap) { char *key; const struct prom_metric *metric; metric = prom_registry_get_metric(prometheus_registry, metric_name); if (metric == NULL) { pr_trace_msg(trace_channel, 17, "unknown metric name '%s' requested", metric_name); return NULL; } key = va_arg(ap, char *); while (key != NULL) { char *val; pr_signals_handle(); val = va_arg(ap, char *); /* Any labels provided by the caller take precedence. */ if (pr_table_exists(labels, key) > 0) { (void) pr_table_set(labels, key, pstrdup(p, val), 0); } else { (void) pr_table_add_dup(labels, key, val, 0); } key = va_arg(ap, char *); } va_end(ap); return metric; } static void prom_event_decr(const char *metric_name, uint32_t decr, ...) { int res; pool *tmp_pool; va_list ap; const struct prom_metric *metric; pr_table_t *labels; if (session.pool != NULL) { tmp_pool = make_sub_pool(session.pool); } else { tmp_pool = make_sub_pool(prometheus_pool); } labels = prom_get_labels(tmp_pool); va_start(ap, decr); metric = prom_metric_with_labels(tmp_pool, metric_name, labels, ap); va_end(ap); if (metric == NULL) { destroy_pool(tmp_pool); return; } res = prom_metric_decr(tmp_pool, metric, decr, labels); if (res < 0) { pr_trace_msg(trace_channel, 19, "error decrementing %s: %s", metric_name, strerror(errno)); } destroy_pool(tmp_pool); } static void prom_event_incr(const char *metric_name, uint32_t incr, ...) { int res; pool *tmp_pool; va_list ap; const struct prom_metric *metric; pr_table_t *labels; if (session.pool != NULL) { tmp_pool = make_sub_pool(session.pool); } else { tmp_pool = make_sub_pool(prometheus_pool); } labels = prom_get_labels(tmp_pool); va_start(ap, incr); metric = prom_metric_with_labels(tmp_pool, metric_name, labels, ap); va_end(ap); if (metric == NULL) { destroy_pool(tmp_pool); return; } res = prom_metric_incr(tmp_pool, metric, incr, labels); if (res < 0) { pr_trace_msg(trace_channel, 19, "error incrementing %s: %s", metric_name, strerror(errno)); } destroy_pool(tmp_pool); } static void prom_event_observe(const char *metric_name, double observed, ...) { int res; pool *tmp_pool; va_list ap; const struct prom_metric *metric; pr_table_t *labels; if (session.pool != NULL) { tmp_pool = make_sub_pool(session.pool); } else { tmp_pool = make_sub_pool(prometheus_pool); } labels = prom_get_labels(tmp_pool); va_start(ap, observed); metric = prom_metric_with_labels(tmp_pool, metric_name, labels, ap); va_end(ap); if (metric == NULL) { destroy_pool(tmp_pool); return; } res = prom_metric_observe(tmp_pool, metric, observed, labels); if (res < 0) { pr_trace_msg(trace_channel, 19, "error observing %s: %s", metric_name, strerror(errno)); } destroy_pool(tmp_pool); } /* Configuration handlers */ /* usage: PrometheusEngine on|off */ MODRET set_prometheusengine(cmd_rec *cmd) { int engine = -1; config_rec *c; CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT); engine = get_boolean(cmd, 1); if (engine == -1) { CONF_ERROR(cmd, "expected Boolean parameter"); } c = add_config_param(cmd->argv[0], 1, NULL); c->argv[0] = pcalloc(c->pool, sizeof(int)); *((int *) c->argv[0]) = engine; return PR_HANDLED(cmd); } /* usage: PrometheusExporter address[:port] [username password] */ MODRET set_prometheusexporter(cmd_rec *cmd) { char *addr, *ptr; size_t addrlen; config_rec *c; pr_netaddr_t *exporter_addr; int exporter_port = PROMETHEUS_DEFAULT_EXPORTER_PORT; if (cmd->argc < 2 || cmd->argc > 4) { CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT); c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL); /* Separate the port out from the address, if present. */ ptr = strrchr(cmd->argv[1], ':'); if (ptr != NULL) { char *ptr2; /* We need to handle the following possibilities: * * ipv4-addr * ipv4-addr:port * [ipv6-addr] * [ipv6-addr]:port * * Thus we check to see if the last ':' occurs before, or after, * a ']' for an IPv6 address. */ ptr2 = strrchr(cmd->argv[1], ']'); if (ptr2 != NULL) { if (ptr2 > ptr) { /* The found ':' is part of an IPv6 address, not a port delimiter. */ ptr = NULL; } } if (ptr != NULL) { *ptr = '\0'; exporter_port = atoi(ptr + 1); if (exporter_port < 1 || exporter_port > 65535) { CONF_ERROR(cmd, "port must be between 1-65535"); } } } addr = cmd->argv[1]; addrlen = strlen(addr); /* Make sure we can handle an IPv6 address here, e.g.: * * [::1]:162 */ if (addrlen > 0 && (addr[0] == '[' && addr[addrlen-1] == ']')) { addr = pstrndup(cmd->pool, addr + 1, addrlen - 2); } /* Watch for wildcard addresses. */ if (strcmp(addr, "0.0.0.0") == 0) { exporter_addr = pr_netaddr_alloc(c->pool); pr_netaddr_set_family(exporter_addr, AF_INET); pr_netaddr_set_sockaddr_any(exporter_addr); #if defined(PR_USE_IPV6) } else if (strcmp(addr, "::") == 0) { exporter_addr = pr_netaddr_alloc(c->pool); pr_netaddr_set_family(exporter_addr, AF_INET6); pr_netaddr_set_sockaddr_any(exporter_addr); #endif /* PR_USE_IPV6 */ } else { exporter_addr = (pr_netaddr_t *) pr_netaddr_get_addr(c->pool, addr, NULL); if (exporter_addr == NULL) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to resolve \"", addr, "\"", NULL)); } } pr_netaddr_set_port2(exporter_addr, exporter_port); c->argv[0] = exporter_addr; if (cmd->argc > 2) { if (cmd->argc == 3) { /* Only username provided? Why? */ CONF_ERROR(cmd, "wrong number of parameters"); } c->argv[1] = pstrdup(c->pool, cmd->argv[2]); c->argv[2] = pstrdup(c->pool, cmd->argv[3]); } return PR_HANDLED(cmd); } /* usage: PrometheusLog path|"none" */ MODRET set_prometheuslog(cmd_rec *cmd) { CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT); (void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]); return PR_HANDLED(cmd); } /* usage: PrometheusOptions opt1 ... optN */ MODRET set_prometheusoptions(cmd_rec *cmd) { config_rec *c = NULL; register unsigned int i; unsigned long opts = 0UL; if (cmd->argc-1 == 0) { CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT); c = add_config_param(cmd->argv[0], 1, NULL); for (i = 1; i < cmd->argc; i++) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown PrometheusOption '", cmd->argv[i], "'", NULL)); } c->argv[0] = pcalloc(c->pool, sizeof(unsigned long)); *((unsigned long *) c->argv[0]) = opts; return PR_HANDLED(cmd); } /* usage: PrometheusTables path */ MODRET set_prometheustables(cmd_rec *cmd) { int res; struct stat st; char *path; CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT); path = cmd->argv[1]; if (*path != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "must be a full path: '", path, "'", NULL)); } res = stat(path, &st); if (res < 0) { char *exporter_chroot; if (errno != ENOENT) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to stat '", path, "': ", strerror(errno), NULL)); } pr_log_debug(DEBUG0, MOD_PROMETHEUS_VERSION ": PrometheusTables directory '%s' does not exist, creating it", path); /* Create the directory. */ res = prom_mkpath(cmd->tmp_pool, path, geteuid(), getegid(), 0755); if (res < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to create directory '", path, "': ", strerror(errno), NULL)); } /* Also create the empty/ directory underneath, for the chroot. */ exporter_chroot = pdircat(cmd->tmp_pool, path, "empty", NULL); res = prom_mkpath(cmd->tmp_pool, exporter_chroot, geteuid(), getegid(), 0111); if (res < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to create directory '", exporter_chroot, "': ", strerror(errno), NULL)); } pr_log_debug(DEBUG2, MOD_PROMETHEUS_VERSION ": created PrometheusTables directory '%s'", path); } else { char *exporter_chroot; if (!S_ISDIR(st.st_mode)) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use '", path, ": Not a directory", NULL)); } /* See if the chroot directory empty/ already exists as well. And enforce * the permissions on that directory. */ exporter_chroot = pdircat(cmd->tmp_pool, path, "empty", NULL); res = stat(exporter_chroot, &st); if (res < 0) { if (errno != ENOENT) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to stat '", exporter_chroot, "': ", strerror(errno), NULL)); } res = prom_mkpath(cmd->tmp_pool, exporter_chroot, geteuid(), getegid(), 0111); if (res < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to create directory '", exporter_chroot, "': ", strerror(errno), NULL)); } } else { mode_t dir_mode, expected_mode; dir_mode = st.st_mode; dir_mode &= ~S_IFMT; expected_mode = (S_IXUSR|S_IXGRP|S_IXOTH); if (dir_mode != expected_mode) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "directory '", exporter_chroot, "' has incorrect permissions (not 0111 as required)", NULL)); } } } (void) add_config_param_str(cmd->argv[0], 1, path); return PR_HANDLED(cmd); } /* Command handlers */ static void prom_cmd_decr(cmd_rec *cmd, const char *metric_name, pr_table_t *labels) { const struct prom_metric *metric; metric = prom_registry_get_metric(prometheus_registry, metric_name); if (metric != NULL) { if (labels == NULL) { labels = prom_get_labels(cmd->tmp_pool); } prom_metric_decr(cmd->tmp_pool, metric, 1, labels); } else { pr_trace_msg(trace_channel, 19, "%s: unknown '%s' metric requested", (char *) cmd->argv[0], metric_name); } } static void prom_cmd_incr_type(cmd_rec *cmd, const char *metric_name, pr_table_t *labels, int metric_type) { const struct prom_metric *metric; metric = prom_registry_get_metric(prometheus_registry, metric_name); if (metric != NULL) { if (labels == NULL) { labels = prom_get_labels(cmd->tmp_pool); } prom_metric_incr_type(cmd->tmp_pool, metric, 1, labels, metric_type); } else { pr_trace_msg(trace_channel, 19, "%s: unknown '%s' metric requested", (char *) cmd->argv[0], metric_name); } } static void prom_cmd_observe(cmd_rec *cmd, const char *metric_name, double val, pr_table_t *labels) { const struct prom_metric *metric; metric = prom_registry_get_metric(prometheus_registry, metric_name); if (metric != NULL) { if (labels == NULL) { labels = prom_get_labels(cmd->tmp_pool); } prom_metric_observe(cmd->tmp_pool, metric, val, labels); } else { pr_trace_msg(trace_channel, 19, "%s: unknown '%s' metric requested", (char *) cmd->argv[0], metric_name); } } MODRET prom_pre_list(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } prom_cmd_incr_type(cmd, "directory_list", NULL, PROM_METRIC_TYPE_GAUGE); return PR_DECLINED(cmd); } MODRET prom_log_list(cmd_rec *cmd) { const char *metric_name; if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } metric_name = "directory_list"; prom_cmd_incr_type(cmd, metric_name, NULL, PROM_METRIC_TYPE_COUNTER); prom_cmd_decr(cmd, metric_name, NULL); return PR_DECLINED(cmd); } MODRET prom_err_list(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } prom_cmd_incr_type(cmd, "directory_list_error", NULL, PROM_METRIC_TYPE_COUNTER); prom_cmd_decr(cmd, "directory_list", NULL); return PR_DECLINED(cmd); } MODRET prom_pre_user(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } /* Logins begin at the first USER command seen; subsequent USER commands * are ignored, for purposes of the gauge. * * Logins end either at successful login, or end of connection. */ if (prometheus_saw_user_cmd == FALSE) { prom_cmd_incr_type(cmd, "login", NULL, PROM_METRIC_TYPE_GAUGE); prometheus_saw_user_cmd = TRUE; prometheus_saw_pass_cmd = FALSE; } return PR_DECLINED(cmd); } MODRET prom_pre_pass(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } if (prometheus_saw_user_cmd == FALSE) { return PR_DECLINED(cmd); } /* Used for tracking "incomplete" logins. */ prometheus_saw_pass_cmd = TRUE; return PR_DECLINED(cmd); } MODRET prom_log_pass(cmd_rec *cmd) { const char *metric_name; pr_table_t *labels; uint64_t now_ms = 0; if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } if (prometheus_saw_user_cmd == FALSE) { return PR_DECLINED(cmd); } /* Easiest way for us to check for anonymous logins is here; the * auth flow does not use the "mod_auth.authentication-code" event. */ if (session.sf_flags & SF_ANON) { prom_event_incr("auth", 1, "method", "anonymous", NULL); } metric_name = "login"; labels = prom_get_labels(cmd->tmp_pool); prom_cmd_incr_type(cmd, metric_name, labels, PROM_METRIC_TYPE_COUNTER); prom_cmd_decr(cmd, metric_name, labels); pr_gettimeofday_millis(&now_ms); prom_cmd_observe(cmd, metric_name, (double) ((now_ms - prometheus_connected_ms) / 1000), labels); return PR_DECLINED(cmd); } MODRET prom_err_login(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } prom_cmd_incr_type(cmd, "login_error", NULL, PROM_METRIC_TYPE_COUNTER); /* Note that we never decrement the "login" gauge here. Why not? A * failed USER or PASS command could happen for multiple reasons (bad * sequence, wrong password that will be followed by a correct one, etc). * Thus the "login" gauge should only be decremented by a successful login, * or end of connection. */ return PR_DECLINED(cmd); } MODRET prom_pre_retr(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } prom_cmd_incr_type(cmd, "file_download", NULL, PROM_METRIC_TYPE_GAUGE); return PR_DECLINED(cmd); } MODRET prom_log_retr(cmd_rec *cmd) { const char *metric_name; pr_table_t *labels; if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } metric_name = "file_download"; labels = prom_get_labels(cmd->tmp_pool); prom_cmd_incr_type(cmd, metric_name, labels, PROM_METRIC_TYPE_COUNTER); prom_cmd_decr(cmd, metric_name, labels); prom_cmd_observe(cmd, metric_name, session.xfer.total_bytes, labels); return PR_DECLINED(cmd); } MODRET prom_err_retr(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } prom_cmd_incr_type(cmd, "file_download_error", NULL, PROM_METRIC_TYPE_COUNTER); prom_cmd_decr(cmd, "file_download", NULL); return PR_DECLINED(cmd); } MODRET prom_pre_stor(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } prom_cmd_incr_type(cmd, "file_upload", NULL, PROM_METRIC_TYPE_GAUGE); return PR_DECLINED(cmd); } MODRET prom_log_stor(cmd_rec *cmd) { const char *metric_name; pr_table_t *labels; if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } metric_name = "file_upload"; labels = prom_get_labels(cmd->tmp_pool); prom_cmd_incr_type(cmd, metric_name, labels, PROM_METRIC_TYPE_COUNTER); prom_cmd_decr(cmd, metric_name, labels); prom_cmd_observe(cmd, metric_name, session.xfer.total_bytes, labels); return PR_DECLINED(cmd); } MODRET prom_err_stor(cmd_rec *cmd) { if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } prom_cmd_incr_type(cmd, "file_upload_error", NULL, PROM_METRIC_TYPE_COUNTER); prom_cmd_decr(cmd, "file_upload", NULL); return PR_DECLINED(cmd); } MODRET prom_log_auth(cmd_rec *cmd) { const char *metric_name; const struct prom_metric *metric; if (prometheus_engine == FALSE) { return PR_DECLINED(cmd); } /* Note: we are not currently properly incrementing * session{protocol="ftps"} for FTPS connections accepted using the * UseImplicitSSL TLSOption. * * The issue is that for those connections, the protocol will be set to * "ftps" in mod_tls' sess_init callback. But here in mod_prometheus, we * are not guaranteed to being called AFTER mod_tls, due to module load * ordering. Thus we do not have a good way of determining when to * increment those counts for implicit FTPS connections. */ metric_name = "tls_protocol"; metric = prom_registry_get_metric(prometheus_registry, metric_name); if (metric != NULL) { pr_table_t *labels; const char *tls_version; labels = prom_get_labels(cmd->tmp_pool); tls_version = pr_table_get(session.notes, "TLS_PROTOCOL", NULL); if (tls_version == NULL) { /* Try the environment. */ tls_version = pr_env_get(cmd->tmp_pool, "TLS_PROTOCOL"); } if (tls_version != NULL) { (void) pr_table_add_dup(labels, "version", tls_version, 0); } prom_metric_incr(cmd->tmp_pool, metric, 1, labels); } else { pr_trace_msg(trace_channel, 19, "%s: unknown '%s' metric requested", (char *) cmd->argv[0], metric_name); } return PR_DECLINED(cmd); } /* Event listeners */ static void prom_auth_code_ev(const void *event_data, void *user_data) { int auth_code; if (prometheus_engine == FALSE) { return; } auth_code = *((int *) event_data); switch (auth_code) { case PR_AUTH_OK_NO_PASS: prom_event_incr("auth", 1, "method", session.rfc2228_mech, NULL); break; case PR_AUTH_RFC2228_OK: prom_event_incr("auth", 1, "method", "certificate", NULL); break; case PR_AUTH_OK: prom_event_incr("auth", 1, "method", "password", NULL); break; case PR_AUTH_NOPWD: prom_event_incr("auth_error", 1, "reason", "unknown user", NULL); break; case PR_AUTH_BADPWD: prom_event_incr("auth_error", 1, "reason", "bad password", NULL); break; default: prom_event_incr("auth_error", 1, NULL); break; } } static void prom_connect_ev(const void *event_data, void *user_data) { int flags; struct prom_dbh *dbh; /* Close any database handle inherited from our parent, and open a new * one, per SQLite3 recommendation. * * NOTE: session.pool does NOT exist yet. */ (void) prom_db_close(prometheus_pool, prometheus_dbh); prometheus_dbh = NULL; flags = PROM_DB_OPEN_FL_VACUUM|PROM_DB_OPEN_FL_SKIP_TABLE_INIT; dbh = prom_metric_db_init(prometheus_pool, prometheus_tables_dir, flags); if (dbh == NULL) { pr_trace_msg(trace_channel, 1, "error initializing '%s' metrics db at connect time: %s", prometheus_tables_dir, strerror(errno)); } else { if (prom_registry_set_dbh(prometheus_registry, dbh) < 0) { pr_trace_msg(trace_channel, 3, "error setting registry dbh: %s", strerror(errno)); } } } static void prom_exit_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } switch (session.disconnect_reason) { case PR_SESS_DISCONNECT_BANNED: case PR_SESS_DISCONNECT_CONFIG_ACL: case PR_SESS_DISCONNECT_MODULE_ACL: case PR_SESS_DISCONNECT_SESSION_INIT_FAILED: { const void *reason; reason = pr_table_get(session.notes, "core.disconnect-details", NULL); if (reason != NULL) { prom_event_incr("connection_refused", 1, "reason", reason, NULL); } else { prom_event_incr("connection_refused", 1, NULL); } break; } case PR_SESS_DISCONNECT_SEGFAULT: prom_event_decr("connection", 1, NULL); prom_event_incr("segfault", 1, NULL); break; default: { uint64_t now_ms = 0; if (prometheus_saw_user_cmd == TRUE && session.user == NULL) { /* Login was started, but not completed. */ prom_event_decr("login", 1, NULL); if (prometheus_saw_pass_cmd == FALSE) { prom_event_incr("auth_error", 1, "reason", "incomplete", NULL); } } prom_event_decr("connection", 1, NULL); pr_gettimeofday_millis(&now_ms); prom_event_observe("connection", (double) ((now_ms - prometheus_connected_ms) / 1000), NULL); break; } } prom_http_free(); if (prometheus_logfd >= 0) { (void) close(prometheus_logfd); prometheus_logfd = -1; } } static void prom_log_msg_ev(const void *event_data, void *user_data) { pool *tmp_pool; int res; const char *metric_name, *level_text = NULL; const struct prom_metric *metric; const pr_log_event_t *le; pr_table_t *labels; metric_name = "log_message"; metric = prom_registry_get_metric(prometheus_registry, metric_name); if (metric == NULL) { pr_trace_msg(trace_channel, 17, "unknown metric name '%s' requested", metric_name); return; } le = event_data; switch (le->log_level) { case PR_LOG_EMERG: level_text = "emerg"; break; case PR_LOG_ALERT: level_text = "alert"; break; case PR_LOG_CRIT: level_text = "crit"; break; case PR_LOG_ERR: level_text = "error"; break; case PR_LOG_WARNING: level_text = "warn"; break; case PR_LOG_NOTICE: level_text = "notice"; break; case PR_LOG_INFO: level_text = "info"; break; case PR_LOG_DEBUG: level_text = "debug"; break; default: level_text = NULL; break; } if (level_text == NULL) { return; } if (session.pool != NULL) { tmp_pool = make_sub_pool(session.pool); } else { tmp_pool = make_sub_pool(prometheus_pool); } labels = prom_get_labels(tmp_pool); (void) pr_table_add_dup(labels, "level", level_text, 0); res = prom_metric_incr(tmp_pool, metric, 1, labels); if (res < 0) { pr_trace_msg(trace_channel, 19, "error increment %s: %s", metric_name, strerror(errno)); } destroy_pool(tmp_pool); } #if defined(PR_SHARED_MODULE) static void prom_mod_unload_ev(const void *event_data, void *user_data) { if (strcmp((const char *) event_data, "mod_prometheus.c") != 0) { return; } /* Unregister ourselves from all events. */ pr_event_unregister(&prometheus_module, NULL, NULL); (void) prom_db_close(prometheus_pool, prometheus_dbh); prometheus_dbh = NULL; prometheus_exporter_http = NULL; (void) prom_registry_free(prometheus_registry); prometheus_registry = NULL; prometheus_tables_dir = NULL; destroy_pool(prometheus_pool); prometheus_pool = NULL; (void) close(prometheus_logfd); prometheus_logfd = -1; } #endif /* PR_SHARED_MODULE */ static void create_session_metrics(pool *p, struct prom_dbh *dbh) { int res; struct prom_metric *metric; /* Session metrics: * * auth * auth_error * connection * directory_list * directory_list_error * file_download * file_download_error * file_upload * file_upload_error * login * login_error * timeout * handshake_error * tls_protocol * sftp_protocol */ metric = prom_metric_create(prometheus_pool, "auth", dbh); prom_metric_add_counter(metric, "total", "Number of successful authentications"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "auth_error", dbh); prom_metric_add_counter(metric, "total", "Number of failed authentications"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "connection", dbh); prom_metric_add_counter(metric, "total", "Number of connections"); prom_metric_add_gauge(metric, "count", "Current count of connections"); /* Create histogram buckets for connection duration of: * 1s, 5s, 10s, 30s, 1m, 5m, 10m, 1h, 6h, 1d */ prom_metric_add_histogram(metric, "duration_seconds", "Connection durations in seconds", 11, (double) 1, (double) 5, (double) 10, (double) 30, (double) 60, (double) 300, (double) 600, (double) 1800, (double) 3600, (double) 21600, (double) 86400); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "directory_list", dbh); prom_metric_add_counter(metric, "total", "Number of succesful directory listings"); prom_metric_add_gauge(metric, "count", "Current count of directory listings"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "directory_list_error", dbh); prom_metric_add_counter(metric, "total", "Number of failed directory listings"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "file_download", dbh); prom_metric_add_counter(metric, "total", "Number of successful file downloads"); prom_metric_add_gauge(metric, "count", "Current count of file downloads"); /* Create histogram buckets for file download bytes of: * 10K, 50K, 100K, 1M, 10M, 50M, 100M, 500M, 1G, 100G */ prom_metric_add_histogram(metric, "bytes", "Amount of data downloaded in bytes", 10, (double) 10240, (double) 51200, (double) 102400, (double) 1048576, (double) 10485760, (double) 52428800, (double) 104857600, (double) 524288000, (double) 1073741824, (double) 107374182400); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "file_download_error", dbh); prom_metric_add_counter(metric, "total", "Number of failed file downloads"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "file_upload", dbh); prom_metric_add_counter(metric, "total", "Number of successful file uploads"); prom_metric_add_gauge(metric, "count", "Current count of file uploads"); /* Create histogram buckets for file upload bytes of: * 10K, 50K, 100K, 1M, 10M, 50M, 100M, 500M, 1G, 100G */ prom_metric_add_histogram(metric, "bytes", "Amount of data uploaded in bytes", 10, (double) 10240, (double) 51200, (double) 102400, (double) 1048576, (double) 10485760, (double) 52428800, (double) 104857600, (double) 524288000, (double) 1073741824, (double) 107374182400); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "file_upload_error", dbh); prom_metric_add_counter(metric, "total", "Number of failed file uploads"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "login", dbh); prom_metric_add_counter(metric, "total", "Number of successful logins"); prom_metric_add_gauge(metric, "count", "Current count of logins"); /* Create histogram buckets for login duration of: * 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 2.5s, 5s, 10s, 30s */ prom_metric_add_histogram(metric, "delay_seconds", "Delay before login in seconds", 11, (double) 0.01, (double) 0.025, (double) 0.05, (double) 0.1, (double) 0.25, (double) 0.5, (double) 1.0, (double) 2.5, (double) 5.0, (double) 10.0, (double) 30.0); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "login_error", dbh); prom_metric_add_counter(metric, "total", "Number of failed logins"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "timeout", dbh); prom_metric_add_counter(metric, "total", "Number of timeouts"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "handshake_error", dbh); prom_metric_add_counter(metric, "total", "Number of failed SFTP/TLS handshakes"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "sftp_protocol", dbh); prom_metric_add_counter(metric, "total", "Number of SFTP sessions by protocol version"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "tls_protocol", dbh); prom_metric_add_counter(metric, "total", "Number of TLS sessions by protocol version"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } } static void create_server_metrics(pool *p, struct prom_dbh *dbh) { int res; struct prom_metric *metric; /* Server metrics: * * connection_refused * log_message * segfault */ metric = prom_metric_create(prometheus_pool, "connection_refused", dbh); prom_metric_add_counter(metric, "total", "Number of refused connections"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "log_message", dbh); prom_metric_add_counter(metric, "total", "Number of log_messages"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } metric = prom_metric_create(prometheus_pool, "segfault", dbh); prom_metric_add_counter(metric, "total", "Number of segfaults"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } } static void create_metrics(struct prom_dbh *dbh) { pool *tmp_pool; int res; struct prom_metric *metric; tmp_pool = make_sub_pool(prometheus_pool); pr_pool_tag(tmp_pool, "Prometheus metrics creation pool"); metric = prom_metric_create(prometheus_pool, "build_info", dbh); prom_metric_add_counter(metric, NULL, "ProFTPD build information"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } else { pr_table_t *labels; labels = pr_table_nalloc(tmp_pool, 0, 2); (void) pr_table_add_dup(labels, "proftpd_version", pr_version_get_str(), 0); (void) pr_table_add_dup(labels, "mod_prometheus_version", MOD_PROMETHEUS_VERSION, 0); res = prom_metric_incr(tmp_pool, metric, 1, labels); if (res < 0) { pr_trace_msg(trace_channel, 3, "error incrementing metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } } metric = prom_metric_create(prometheus_pool, "startup_time", dbh); prom_metric_add_counter(metric, NULL, "ProFTPD startup time, in unixtime seconds"); res = prom_registry_add_metric(prometheus_registry, metric); if (res < 0) { pr_trace_msg(trace_channel, 1, "error registering metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } else { time_t now; now = time(NULL); res = prom_metric_incr(tmp_pool, metric, now, NULL); if (res < 0) { pr_trace_msg(trace_channel, 3, "error incrementing metric '%s': %s", prom_metric_get_name(metric), strerror(errno)); } } create_server_metrics(tmp_pool, dbh); create_session_metrics(tmp_pool, dbh); res = prom_registry_sort_metrics(prometheus_registry); if (res < 0) { pr_trace_msg(trace_channel, 3, "error sorting registry metrics: %s", strerror(errno)); } destroy_pool(tmp_pool); } static void prom_postparse_ev(const void *event_data, void *user_data) { config_rec *c; pr_netaddr_t *exporter_addr; const char *exporter_username, *exporter_password; c = find_config(main_server->conf, CONF_PARAM, "PrometheusEngine", FALSE); if (c != NULL) { prometheus_engine = *((int *) c->argv[0]); } if (prometheus_engine == FALSE) { return; } prom_openlog(); c = find_config(main_server->conf, CONF_PARAM, "PrometheusOptions", FALSE); while (c != NULL) { unsigned long opts = 0; pr_signals_handle(); opts = *((unsigned long *) c->argv[0]); prometheus_opts |= opts; c = find_config_next(c, c->next, CONF_PARAM, "PrometheusOptions", FALSE); } c = find_config(main_server->conf, CONF_PARAM, "PrometheusTables", FALSE); if (c == NULL) { /* No PrometheusTables configured, mod_prometheus cannot run. */ (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "no PrometheusTables configured, disabling module"); prometheus_engine = FALSE; return; } prometheus_tables_dir = c->argv[0]; prometheus_dbh = prom_metric_init(prometheus_pool, prometheus_tables_dir); if (prometheus_dbh == NULL) { pr_log_pri(PR_LOG_WARNING, MOD_PROMETHEUS_VERSION ": unable to initialize metrics, failing to start up: %s", strerror(errno)); pr_session_disconnect(&prometheus_module, PR_SESS_DISCONNECT_BAD_CONFIG, "Failed metrics initialization"); } prometheus_registry = prom_registry_init(prometheus_pool, "proftpd"); /* Create our known metrics, and register them. */ create_metrics(prometheus_dbh); c = find_config(main_server->conf, CONF_PARAM, "PrometheusExporter", FALSE); if (c == NULL) { prometheus_engine = FALSE; pr_log_debug(DEBUG0, MOD_PROMETHEUS_VERSION ": missing required PrometheusExporter directive, disabling module"); prom_metric_free(prometheus_pool, prometheus_dbh); prometheus_dbh = NULL; prom_registry_free(prometheus_registry); prometheus_registry = NULL; return; } if (prom_http_init(prometheus_pool) < 0) { prom_metric_free(prometheus_pool, prometheus_dbh); prometheus_dbh = NULL; prom_registry_free(prometheus_registry); prometheus_registry = NULL; pr_log_pri(PR_LOG_ERR, MOD_PROMETHEUS_VERSION ": unable to initialize HTTP API, failing to start up: %s", strerror(errno)); pr_session_disconnect(&prometheus_module, PR_SESS_DISCONNECT_BAD_CONFIG, "Failed HTTP initialization"); } exporter_addr = c->argv[0]; exporter_username = c->argv[1]; exporter_password = c->argv[2]; /* Look for the exporter credentials environment variables, too. */ if (exporter_username == NULL) { exporter_username = pr_env_get(c->pool, "PROMETHEUS_USERNAME"); exporter_password = pr_env_get(c->pool, "PROMETHEUS_PASSWORD"); } prometheus_exporter_pid = prom_exporter_start(prometheus_pool, exporter_addr, exporter_username, exporter_password); if (prometheus_exporter_pid == 0) { prometheus_engine = FALSE; pr_log_debug(DEBUG0, MOD_PROMETHEUS_VERSION ": failed to start exporter process, disabling module"); prom_metric_free(prometheus_pool, prometheus_dbh); prometheus_dbh = NULL; prom_registry_free(prometheus_registry); prometheus_registry = NULL; } } static void prom_restart_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } pr_trace_msg(trace_channel, 17, "restart event received, resetting counters"); prom_exporter_stop(prometheus_exporter_pid); (void) prom_db_close(prometheus_pool, prometheus_dbh); prometheus_dbh = NULL; prometheus_exporter_http = NULL; (void) prom_registry_free(prometheus_registry); prometheus_registry = NULL; prometheus_tables_dir = NULL; /* Close the PrometheusLog file descriptor; it will be reopened in the * postparse event listener. */ (void) close(prometheus_logfd); prometheus_logfd = -1; } static void prom_shutdown_ev(const void *event_data, void *user_data) { prom_exporter_stop(prometheus_exporter_pid); (void) prom_db_close(prometheus_pool, prometheus_dbh); prometheus_dbh = NULL; destroy_pool(prometheus_pool); prometheus_pool = NULL; (void) close(prometheus_logfd); prometheus_logfd = -1; } static void prom_startup_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } if (ServerType == SERVER_INETD) { pr_log_debug(DEBUG0, MOD_PROMETHEUS_VERSION ": cannot support Prometheus for ServerType inetd, disabling module"); prometheus_engine = FALSE; return; } } static void prom_timeout_idle_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("timeout", 1, "reason", "TimeoutIdle", NULL); } static void prom_timeout_login_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("timeout", 1, "reason", "TimeoutLogin", NULL); } static void prom_timeout_noxfer_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("timeout", 1, "reason", "TimeoutNoTransfer", NULL); } static void prom_timeout_session_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("timeout", 1, "reason", "TimeoutSession", NULL); } static void prom_timeout_stalled_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("timeout", 1, "reason", "TimeoutStalled", NULL); } /* mod_tls-generated events */ static void prom_tls_ctrl_handshake_err_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } /* Note that we explicitly set the "protocol" label here to "ftps". * Otherwise, it would show up as "ftp", since the TLS handshake did * not actually succeed, and that "ftp" label would be surprising. */ prom_event_incr("handshake_error", 1, "connection", "ctrl", "protocol", "ftps", NULL); } static void prom_tls_data_handshake_err_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("handshake_error", 1, "connection", "data", NULL); } /* mod_sftp-generated events */ static void prom_ssh2_kex_err_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("handshake_error", 1, NULL); } static void prom_ssh2_auth_hostbased_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("auth", 1, "method", "hostbased", NULL); } static void prom_ssh2_auth_hostbased_err_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("auth_error", 1, "method", "hostbased", NULL); } static void prom_ssh2_auth_kbdint_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("auth", 1, "method", "keyboard-interactive", NULL); } static void prom_ssh2_auth_kbdint_err_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("auth_error", 1, "method", "keyboard-interactive", NULL); } static void prom_ssh2_auth_passwd_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("auth", 1, "method", "password", NULL); } static void prom_ssh2_auth_passwd_err_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("auth_error", 1, "method", "password", NULL); } static void prom_ssh2_auth_publickey_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("auth", 1, "method", "publickey", NULL); } static void prom_ssh2_auth_publickey_err_ev(const void *event_data, void *user_data) { if (prometheus_engine == FALSE) { return; } prom_event_incr("auth_error", 1, "method", "publickey", NULL); } static void prom_ssh2_sftp_proto_version_ev(const void *event_data, void *user_data) { unsigned long protocol_version; if (prometheus_engine == FALSE) { return; } if (event_data == NULL) { /* Missing required data. */ return; } protocol_version = *((unsigned long *) event_data); switch (protocol_version) { case 3: prom_event_incr("sftp_protocol", 1, "version", "3", NULL); break; case 4: prom_event_incr("sftp_protocol", 1, "version", "4", NULL); break; case 5: prom_event_incr("sftp_protocol", 1, "version", "5", NULL); break; case 6: prom_event_incr("sftp_protocol", 1, "version", "6", NULL); break; default: (void) pr_log_writefile(prometheus_logfd, MOD_PROMETHEUS_VERSION, "unknown SFTP protocol version %lu, ignoring", protocol_version); } } /* Initialization routines */ static int prom_init(void) { prometheus_pool = make_sub_pool(permanent_pool); pr_pool_tag(prometheus_pool, MOD_PROMETHEUS_VERSION); pr_event_register(&prometheus_module, "core.connect", prom_connect_ev, NULL); #if defined(PR_SHARED_MODULE) pr_event_register(&prometheus_module, "core.module-unload", prom_mod_unload_ev, NULL); #endif /* PR_SHARED_MODULE */ pr_event_register(&prometheus_module, "core.postparse", prom_postparse_ev, NULL); pr_event_register(&prometheus_module, "core.restart", prom_restart_ev, NULL); pr_event_register(&prometheus_module, "core.shutdown", prom_shutdown_ev, NULL); pr_event_register(&prometheus_module, "core.startup", prom_startup_ev, NULL); /* Normally we should register the 'core.exit' event listener in the * sess_init callback. However, we use this listener to listen for * refused connections, e.g. connections refused by other modules' * sess_init callbacks. And depending on the module load order, another * module might refuse the connection before mod_prometheus's sess_init * callback is invoked, which would prevent mod_prometheus from registering * its ' core.exit' event listener. * * Thus to work around this timing issue, we register our 'core.exit' event * listener here, in the daemon process. It should not hurt anything. */ pr_event_register(&prometheus_module, "core.exit", prom_exit_ev, NULL); return 0; } static int prom_sess_init(void) { const char *metric_name; const struct prom_metric *metric; if (prometheus_engine == FALSE) { return 0; } pr_event_register(&prometheus_module, "core.timeout-idle", prom_timeout_idle_ev, NULL); pr_event_register(&prometheus_module, "core.timeout-login", prom_timeout_login_ev, NULL); pr_event_register(&prometheus_module, "core.timeout-no-transfer", prom_timeout_noxfer_ev, NULL); pr_event_register(&prometheus_module, "core.timeout-session", prom_timeout_session_ev, NULL); pr_event_register(&prometheus_module, "core.timeout-stalled", prom_timeout_stalled_ev, NULL); pr_event_register(&prometheus_module, "mod_auth.authentication-code", prom_auth_code_ev, NULL); pr_event_register(&prometheus_module, "core.log.syslog", prom_log_msg_ev, NULL); pr_event_register(&prometheus_module, "core.log.systemlog", prom_log_msg_ev, NULL); if (pr_module_exists("mod_tls.c") == TRUE) { /* mod_tls events */ pr_event_register(&prometheus_module, "mod_tls.ctrl-handshake-failed", prom_tls_ctrl_handshake_err_ev, NULL); pr_event_register(&prometheus_module, "mod_tls.data-handshake-failed", prom_tls_data_handshake_err_ev, NULL); } if (pr_module_exists("mod_sftp.c") == TRUE) { /* mod_sftp events */ pr_event_register(&prometheus_module, "mod_sftp.ssh2.kex.failed", prom_ssh2_kex_err_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.ssh2.auth-hostbased", prom_ssh2_auth_hostbased_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.ssh2.auth-hostbased.failed", prom_ssh2_auth_hostbased_err_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.ssh2.auth-kbdint", prom_ssh2_auth_kbdint_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.ssh2.auth-kbdint.failed", prom_ssh2_auth_kbdint_err_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.ssh2.auth-password", prom_ssh2_auth_passwd_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.ssh2.auth-password.failed", prom_ssh2_auth_passwd_err_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.ssh2.auth-publickey", prom_ssh2_auth_publickey_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.ssh2.auth-publickey.failed", prom_ssh2_auth_publickey_err_ev, NULL); pr_event_register(&prometheus_module, "mod_sftp.sftp.protocol-version", prom_ssh2_sftp_proto_version_ev, NULL); } metric_name = "connection"; metric = prom_registry_get_metric(prometheus_registry, metric_name); if (metric != NULL) { pool *tmp_pool; pr_table_t *labels; pr_gettimeofday_millis(&prometheus_connected_ms); tmp_pool = make_sub_pool(session.pool); labels = prom_get_labels(tmp_pool); prom_metric_incr(tmp_pool, metric, 1, labels); destroy_pool(tmp_pool); } else { pr_trace_msg(trace_channel, 19, "CONNECT: unknown '%s' metric requested", metric_name); } return 0; } /* Module API tables */ static conftable prometheus_conftab[] = { { "PrometheusEngine", set_prometheusengine, NULL }, { "PrometheusExporter", set_prometheusexporter, NULL }, { "PrometheusLog", set_prometheuslog, NULL }, { "PrometheusOptions", set_prometheusoptions, NULL }, { "PrometheusTables", set_prometheustables, NULL }, { NULL } }; static cmdtable prometheus_cmdtab[] = { { PRE_CMD, C_LIST, G_NONE, prom_pre_list, FALSE, FALSE }, { LOG_CMD, C_LIST, G_NONE, prom_log_list, FALSE, FALSE }, { LOG_CMD_ERR, C_LIST, G_NONE, prom_err_list, FALSE, FALSE }, { PRE_CMD, C_MLSD, G_NONE, prom_pre_list, FALSE, FALSE }, { LOG_CMD, C_MLSD, G_NONE, prom_log_list, FALSE, FALSE }, { LOG_CMD_ERR, C_MLSD, G_NONE, prom_err_list, FALSE, FALSE }, { PRE_CMD, C_MLST, G_NONE, prom_pre_list, FALSE, FALSE }, { LOG_CMD, C_MLST, G_NONE, prom_log_list, FALSE, FALSE }, { LOG_CMD_ERR, C_MLST, G_NONE, prom_err_list, FALSE, FALSE }, { PRE_CMD, C_NLST, G_NONE, prom_pre_list, FALSE, FALSE }, { LOG_CMD, C_NLST, G_NONE, prom_log_list, FALSE, FALSE }, { LOG_CMD_ERR, C_NLST, G_NONE, prom_err_list, FALSE, FALSE }, { PRE_CMD, C_USER, G_NONE, prom_pre_user, FALSE, FALSE }, { LOG_CMD_ERR, C_USER, G_NONE, prom_err_login, FALSE, FALSE }, { PRE_CMD, C_PASS, G_NONE, prom_pre_pass, FALSE, FALSE }, { LOG_CMD, C_PASS, G_NONE, prom_log_pass, FALSE, FALSE }, { LOG_CMD_ERR, C_PASS, G_NONE, prom_err_login, FALSE, FALSE }, { PRE_CMD, C_RETR, G_NONE, prom_pre_retr, FALSE, FALSE }, { LOG_CMD, C_RETR, G_NONE, prom_log_retr, FALSE, FALSE }, { LOG_CMD_ERR, C_RETR, G_NONE, prom_err_retr, FALSE, FALSE }, { PRE_CMD, C_STOR, G_NONE, prom_pre_stor, FALSE, FALSE }, { LOG_CMD, C_STOR, G_NONE, prom_log_stor, FALSE, FALSE }, { LOG_CMD_ERR, C_STOR, G_NONE, prom_err_stor, FALSE, FALSE }, /* For mod_tls */ { LOG_CMD, C_AUTH, G_NONE, prom_log_auth, FALSE, FALSE }, { 0, NULL } }; module prometheus_module = { /* Always NULL */ NULL, NULL, /* Module API version */ 0x20, /* Module name */ "prometheus", /* Module configuration handler table */ prometheus_conftab, /* Module command handler table */ prometheus_cmdtab, /* Module authentication handler table */ NULL, /* Module initialization */ prom_init, /* Session initialization */ prom_sess_init, /* Module version */ MOD_PROMETHEUS_VERSION }; proftpd-mod_prometheus-0.2/mod_prometheus.h.in000066400000000000000000000041211410622367000216700ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #ifndef MOD_PROMETHEUS_H #define MOD_PROMETHEUS_H #include "conf.h" #include "privs.h" /* Define if you have the microhttpd.h header. */ #undef HAVE_MICROHTTPD_H #if !defined(HAVE_MICROHTTPD_H) # error "libmicrohttpd library/headers required" #endif /* Define if you have the sqlite3.h header. */ #undef HAVE_SQLITE3_H #if !defined(HAVE_SQLITE3_H) # error "SQLite library/headers required" #endif /* Define if you have the zlib.h header. */ #undef HAVE_ZLIB_H /* Define if you have the sqlite3_stmt_readonly() function. */ #undef HAVE_SQLITE3_STMT_READONLY /* Define if you have the sqlite3_trace() function. */ #undef HAVE_SQLITE3_TRACE /* Define if you have the sqlite3_trace_v2() function. */ #undef HAVE_SQLITE3_TRACE_V2 #define MOD_PROMETHEUS_VERSION "mod_prometheus/0.2" /* Make sure the version of proftpd is as necessary. */ #if PROFTPD_VERSION_NUMBER < 0x0001030706 # error "ProFTPD 1.3.7a or later required" #endif /* Miscellaneous */ extern int prometheus_logfd; extern module prometheus_module; extern pool *prometheus_pool; #endif /* MOD_PROMETHEUS_H */ proftpd-mod_prometheus-0.2/mod_prometheus.html000066400000000000000000000222471410622367000220110ustar00rootroot00000000000000 ProFTPD module mod_prometheus

ProFTPD module mod_prometheus



The purpose of the mod_prometheus module is to provide metrics for scraping by Prometheus.

Installation instructions are discussed here. Note that mod_prometheus requires ProFTPD 1.3.7a or later. Detailed notes on best practices for using this module are here.

The most current version of mod_prometheu can be found at:

  https://github.com/Castaglia/proftpd-mod_prometheus.git

Author

Please contact TJ Saunders <tj at castaglia.org> with any questions, concerns, or suggestions regarding this module.

Directives


PrometheusEngine

Syntax: PrometheusEngine on|off
Default: off
Context: server config
Module: mod_prometheus
Compatibility: 1.3.7a and later

The PrometheusEngine directive controls whether the mod_prometheus module lists for Prometheus HTTP requests for scraping metrics.


PrometheusExporter

Syntax: PrometheusExporter address[:port] [username password]
Default: None
Context: server config
Module: mod_prometheus
Compatibility: 1.3.7a and later

The PrometheusExporter directive configures the mod_prometheus module to act as an exporter for scraping by Prometheus.

Note that the PrometheusExporter directive is required.

The address parameter can be an IP address or a DNS name; this parameter configures the address/port on which mod_prometheus will listen for Prometheus scrape requests. By default, a port of 9273 is assumed; use address:port to specify an alternate port, e.g.:

  PrometheusExporter 0.0.0.0:19273
Note that IPv6 addresses should be enclosed in square brackets, as they can contain colons as well, e.g.:
  PrometheusExporter [::]:19273

HTTP basic authentication can be required by providing the optional username and password parameters. The following environment variables can also be used, instead of configuring these credentials in the config file:

  • PROMETHEUS_USERNAME
  • PROMETHEUS_PASSWORD


PrometheusLog

Syntax: PrometheusLog path|"none"
Default: None
Context: server config
Module: mod_prometheus
Compatibility: 1.3.7a and later

The PrometheusLog directive is used to specify a log file for mod_prometheus's reporting. The path parameter given must be the full path to the file to use for logging.

Note that this path must not be to a world-writable directory and, unless AllowLogSymlinks is explicitly set to on (generally a bad idea), the path must not be a symbolic link.


PrometheusTables

Syntax: PrometheusTables path
Default: None
Context: server config
Module: mod_prometheus
Compatibility: 1.3.7a and later

The PrometheusTables directive is used to specify a directory that mod_prometheus will use for storing its database files; these files are used for tracking the various statistics reported via Prometheus.

Note that the PrometheusTables directive is required.


Usage

Important Security Considerations
Do not configure mod_prometheus to listen on a public Internet address. The information provided via mod_prometheus can be used by attackers to gain more information about your running proftpd, including being able to determine whether their logins fail due to a wrong password (in which case, they know that that user name is valid) or not. It is highly recommended that you configure mod_prometheus to only listen on internal/LAN addresses. Furthermore, you should employ a firewall rule that rejects any TCP connections from the public Internet to your mod_prometheus exporter.

That said, things are a little different when running in an e.g. Kubernetes cluster, with an isolated network. In these cases, given that Kubernetes pods will have dynamic IP addresses, the recommended configuration is to listen on the wildcard address, i.e. "0.0.0.0". This works because the Kubernetes cluster network is already isolated from the public Internet by default.

Prometheus Exporter Process
When proftpd starts up with mod_prometheus enabled, the mod_prometheus module will fork a new process that acts as the Prometheus exporter, receiving and responding to all scrape requests. This exporter listening process automatically switches to the privileges configured by the User and Group directives, and will also automatically chroot itself to a subdirectory of the PrometheusTables directory, after which all root privileges are permanently dropped.

Example Configuration
The mod_prometheus module uses an HTTP server for listening for Prometheus scrape requests. Thus it does not require any separate <VirtualHost> sections, and does not interfere with the normal FTP operations.

Here is an example configuration for mod_prometheus:

  <IfModule mod_prometheus.c>
    PrometheusEngine on
    PrometheusLog /etc/proftpd/prometheus/prometheus.log

    # Configure the exporter to listen on the default port
    PrometheusExporter 0.0.0.0

    # Configure the directory that mod_prometheus will use for its database files
    PrometheusTables /var/proftpd/prometheus
  </IfModule>

Logging
The mod_prometheus module supports different forms of logging. The main module logging is done via the PrometheusLog directive. For debugging purposes, the module also uses trace logging, via the module-specific channels:

  • prometheus
  • prometheus.db
  • prometheus.http
  • prometheus.metric
  • prometheus.metric.db
  • prometheus.registry
  • prometheus.text

Thus for trace logging, to aid in debugging, you would use the following in your proftpd.conf:

  TraceLog /path/to/proftpd-trace.log
  Trace prometheus:20
This trace logging can generate large files; it is intended for debugging use only, and should be removed from any production configuration.


Installation

To install mod_prometheus, go to the third-party module area in the proftpd source code and unpack the mod_prometheus source tarball:
  $ cd proftpd-dir/contrib/
  $ tar zxvf /path/to/mod_prometheus-version.tar.gz
after unpacking the latest proftpd-1.3.x source code. For including mod_prometheus as a statically linked module:
  $ ./configure --with-modules=mod_prometheus:...
To build mod_prometheus as a DSO module:
  $ ./configure --enable-dso --with-shared=mod_prometheus:...
Then follow the usual steps:
  $ make
  $ make install
Note: mod_prometheus uses the SQLite library; thus the sqlite3 development library/headers must be installed for building mod_prometheus.

It is highly recommended that SQLite 3.8.5 or later be used. Problems have been reported with mod_prometheus when SQLite 3.6.20 is used; these problems disappeared once SQLite was upgraded to a newer version. Note: mod_prometheus uses the libmicrohttpd library; thus the libmicrohttpd development library/headers must be installed for building mod_prometheus.


© Copyright 2021 TJ Saunders
All Rights Reserved

proftpd-mod_prometheus-0.2/t/000077500000000000000000000000001410622367000163255ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/Makefile.in000066400000000000000000000035511410622367000203760ustar00rootroot00000000000000CC=@CC@ @SET_MAKE@ top_builddir=../../.. top_srcdir=../../.. module_srcdir=.. srcdir=@srcdir@ VPATH=@srcdir@ include $(top_srcdir)/Make.rules # Necessary redefinitions INCLUDES=-I. -I.. -I$(module_srcdir)/include -I../../.. -I../../../include @INCLUDES@ TEST_CPPFLAGS=$(ADDL_CPPFLAGS) -DHAVE_CONFIG_H $(DEFAULT_PATHS) $(PLATFORM) $(INCLUDES) TEST_LDFLAGS=-L$(top_srcdir)/lib @LIBDIRS@ EXEEXT=@EXEEXT@ TEST_API_DEPS=\ $(top_srcdir)/lib/prbase.a \ $(top_srcdir)/src/pool.o \ $(top_srcdir)/src/privs.o \ $(top_srcdir)/src/str.o \ $(top_srcdir)/src/sets.o \ $(top_srcdir)/src/table.o \ $(top_srcdir)/src/event.o \ $(top_srcdir)/src/timers.o \ $(top_srcdir)/src/stash.o \ $(top_srcdir)/src/modules.o \ $(top_srcdir)/src/configdb.o \ $(top_srcdir)/src/parser.o \ $(top_srcdir)/src/fsio.o \ $(top_srcdir)/src/netio.o \ $(top_srcdir)/src/inet.o \ $(top_srcdir)/src/netaddr.o \ $(top_srcdir)/src/response.o \ $(top_srcdir)/src/auth.o \ $(top_srcdir)/src/env.o \ $(top_srcdir)/src/trace.o \ $(top_srcdir)/src/support.o \ $(top_srcdir)/src/error.o \ $(module_srcdir)/lib/prometheus/db.o \ $(module_srcdir)/lib/prometheus/http.o \ $(module_srcdir)/lib/prometheus/metric.o \ $(module_srcdir)/lib/prometheus/metric/db.o \ $(module_srcdir)/lib/prometheus/registry.o \ $(module_srcdir)/lib/prometheus/text.o TEST_API_LIBS=-lcheck -lm @MODULE_LIBS@ TEST_API_OBJS=\ api/db.o \ api/metric.o \ api/metric/db.o \ api/text.o \ api/registry.o \ api/http.o \ api/stubs.o \ api/tests.o dummy: api/.c.o: $(CC) $(CPPFLAGS) $(TEST_CPPFLAGS) $(CFLAGS) -c $< api-tests$(EXEEXT): $(TEST_API_OBJS) $(TEST_API_DEPS) $(LIBTOOL) --mode=link --tag=CC $(CC) $(LDFLAGS) $(TEST_LDFLAGS) -o $@ $(TEST_API_DEPS) $(TEST_API_OBJS) $(TEST_API_LIBS) $(LIBS) ./$@ clean: $(LIBTOOL) --mode=clean $(RM) *.o api/*.o api/*/*.o api-tests$(EXEEXT) api-tests.log proftpd-mod_prometheus-0.2/t/api/000077500000000000000000000000001410622367000170765ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/api/db.c000066400000000000000000000720501410622367000176330ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ /* Database API tests. */ #include "tests.h" #include "prometheus/db.h" static pool *p = NULL; static const char *db_test_table = "/tmp/prt-mod_prometheus-db.dat"; static void set_up(void) { (void) unlink(db_test_table); if (p == NULL) { p = permanent_pool = make_sub_pool(NULL); session.c = NULL; session.notes = NULL; } if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.db", 1, 20); } mark_point(); prom_db_init(p); } static void tear_down(void) { prom_db_free(); if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.db", 0, 0); } if (p != NULL) { destroy_pool(p); p = permanent_pool = NULL; session.c = NULL; session.notes = NULL; } (void) unlink(db_test_table); } START_TEST (db_close_test) { int res; mark_point(); res = prom_db_close(NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); mark_point(); res = prom_db_close(p, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); } END_TEST START_TEST (db_open_test) { int res; const char *table_path, *schema_name; struct prom_dbh *dbh; mark_point(); dbh = prom_db_open(NULL, NULL, NULL); fail_unless(dbh == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); mark_point(); dbh = prom_db_open(p, NULL, NULL); fail_unless(dbh == NULL, "Failed to handle null table path"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open(p, table_path, NULL); fail_unless(dbh == NULL, "Failed to handle null schema name"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close table '%s': %s", table_path, strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_open_readonly_test) { int res; const char *table_path, *schema_name; struct prom_dbh *dbh; mark_point(); dbh = prom_db_open_readonly(NULL, NULL, NULL); fail_unless(dbh == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); mark_point(); dbh = prom_db_open_readonly(p, NULL, NULL); fail_unless(dbh == NULL, "Failed to handle null table path"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open_readonly(p, table_path, NULL); fail_unless(dbh == NULL, "Failed to handle null schema name"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_db_open_readonly(p, table_path, schema_name); fail_unless(dbh == NULL, "Failed to handle missing table"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s': %s", table_path, schema_name, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); mark_point(); dbh = prom_db_open_readonly(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close table '%s': %s", table_path, strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_open_with_version_test) { int res, flags = 0; struct prom_dbh *dbh; const char *table_path, *schema_name; unsigned int schema_version; mark_point(); dbh = prom_db_open_with_version(NULL, NULL, NULL, 0, 0); fail_unless(dbh == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; schema_version = 0; mark_point(); dbh = prom_db_open_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); flags |= PROM_DB_OPEN_FL_INTEGRITY_CHECK; mark_point(); dbh = prom_db_open_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); if (getenv("CI") == NULL && getenv("TRAVIS") == NULL) { /* Enable the vacuuming for these tests. */ flags |= PROM_DB_OPEN_FL_VACUUM; mark_point(); dbh = prom_db_open_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); flags &= ~PROM_DB_OPEN_FL_VACUUM; } flags &= ~PROM_DB_OPEN_FL_INTEGRITY_CHECK; mark_point(); schema_version = 76; flags |= PROM_DB_OPEN_FL_SCHEMA_VERSION_CHECK|PROM_DB_OPEN_FL_ERROR_ON_SCHEMA_VERSION_SKEW; dbh = prom_db_open_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh == NULL, "Opened table with version skew unexpectedly"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); mark_point(); flags &= ~PROM_DB_OPEN_FL_ERROR_ON_SCHEMA_VERSION_SKEW; dbh = prom_db_open_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); mark_point(); schema_version = 76; dbh = prom_db_open_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close databas: %s", strerror(errno)); mark_point(); schema_version = 99; dbh = prom_db_open_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_open_readonly_with_version_test) { int res, flags = 0; struct prom_dbh *dbh; const char *table_path, *schema_name; unsigned int schema_version; mark_point(); dbh = prom_db_open_readonly_with_version(NULL, NULL, NULL, 0, 0); fail_unless(dbh == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %s (%d)", strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; schema_version = 0; mark_point(); dbh = prom_db_open_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); mark_point(); dbh = prom_db_open_readonly_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); flags |= PROM_DB_OPEN_FL_INTEGRITY_CHECK; mark_point(); dbh = prom_db_open_readonly_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); if (getenv("CI") == NULL && getenv("TRAVIS") == NULL) { /* Enable the vacuuming for these tests. */ flags |= PROM_DB_OPEN_FL_VACUUM; mark_point(); dbh = prom_db_open_readonly_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); flags &= ~PROM_DB_OPEN_FL_VACUUM; } flags &= ~PROM_DB_OPEN_FL_INTEGRITY_CHECK; mark_point(); schema_version = 76; flags |= PROM_DB_OPEN_FL_SCHEMA_VERSION_CHECK|PROM_DB_OPEN_FL_ERROR_ON_SCHEMA_VERSION_SKEW; dbh = prom_db_open_readonly_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh == NULL, "Opened table with version skew unexpectedly"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); mark_point(); flags &= ~PROM_DB_OPEN_FL_ERROR_ON_SCHEMA_VERSION_SKEW; dbh = prom_db_open_readonly_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); mark_point(); schema_version = 76; dbh = prom_db_open_readonly_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close databas: %s", strerror(errno)); mark_point(); schema_version = 99; dbh = prom_db_open_readonly_with_version(p, table_path, schema_name, schema_version, flags); fail_unless(dbh != NULL, "Failed to open table '%s', schema '%s', version %u: %s", table_path, schema_name, schema_version, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_exec_stmt_test) { int res; const char *table_path, *schema_name, *stmt, *errstr; struct prom_dbh *dbh; mark_point(); res = prom_db_exec_stmt(NULL, NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_db_exec_stmt(p, NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); res = prom_db_exec_stmt(p, dbh, NULL, NULL); fail_unless(res < 0, "Failed to handle null statement"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); stmt = "SELECT COUNT(*) FROM foo;"; errstr = NULL; res = prom_db_exec_stmt(p, dbh, stmt, &errstr); fail_unless(res < 0, "Failed to execute statement '%s'", stmt); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST static int create_table(pool *stmt_pool, struct prom_dbh *dbh, const char *table_name) { int res; const char *stmt, *errstr = NULL; stmt = pstrcat(stmt_pool, "CREATE TABLE ", table_name, " (id INTEGER, name TEXT);", NULL); res = prom_db_exec_stmt(stmt_pool, dbh, stmt, &errstr); return res; } START_TEST (db_prepare_stmt_test) { int res; const char *table_path, *schema_name, *stmt; struct prom_dbh *dbh; mark_point(); res = prom_db_prepare_stmt(NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_db_prepare_stmt(p, NULL, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); res = prom_db_prepare_stmt(p, dbh, NULL); fail_unless(res < 0, "Failed to handle null statement"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); stmt = "foo bar baz?"; res = prom_db_prepare_stmt(p, dbh, stmt); fail_unless(res < 0, "Prepared invalid statement '%s' unexpectedly", stmt); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); res = create_table(p, dbh, "foo"); fail_unless(res == 0, "Failed to create table 'foo': %s", strerror(errno)); mark_point(); stmt = "SELECT COUNT(*) FROM foo;"; res = prom_db_prepare_stmt(p, dbh, stmt); fail_unless(res == 0, "Failed to prepare statement '%s': %s", stmt, strerror(errno)); mark_point(); res = prom_db_finish_stmt(p, dbh, stmt); fail_unless(res == 0, "Failed to finish statement '%s': %s", stmt, strerror(errno)); res = create_table(p, dbh, "bar"); fail_unless(res == 0, "Failed to create table 'bar': %s", strerror(errno)); mark_point(); stmt = "SELECT COUNT(*) FROM bar;"; res = prom_db_prepare_stmt(p, dbh, stmt); fail_unless(res == 0, "Failed to prepare statement '%s': %s", stmt, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_finish_stmt_test) { int res; const char *table_path, *schema_name, *stmt; struct prom_dbh *dbh; mark_point(); res = prom_db_finish_stmt(NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_db_finish_stmt(p, NULL, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); res = prom_db_finish_stmt(p, dbh, NULL); fail_unless(res < 0, "Failed to handle null statement"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); stmt = "SELECT COUNT(*) FROM foo"; res = prom_db_finish_stmt(p, dbh, stmt); fail_unless(res < 0, "Failed to handle unprepared statement"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got '%s' (%d)", ENOENT, strerror(errno), errno); res = create_table(p, dbh, "foo"); fail_unless(res == 0, "Failed to create table 'foo': %s", strerror(errno)); mark_point(); res = prom_db_prepare_stmt(p, dbh, stmt); fail_unless(res == 0, "Failed to prepare statement '%s': %s", stmt, strerror(errno)); mark_point(); res = prom_db_finish_stmt(p, dbh, stmt); fail_unless(res == 0, "Failed to finish statement '%s': %s", stmt, strerror(errno)); mark_point(); res = prom_db_finish_stmt(p, dbh, stmt); fail_unless(res < 0, "Failed to handle unprepared statement"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got '%s' (%d)", ENOENT, strerror(errno), errno); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_bind_stmt_test) { int res; const char *table_path, *schema_name, *stmt; struct prom_dbh *dbh; int idx, int_val; long long_val; double double_val; char *text_val; mark_point(); res = prom_db_bind_stmt(NULL, NULL, NULL, -1, -1, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_db_bind_stmt(p, NULL, NULL, -1, -1, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); res = prom_db_bind_stmt(p, dbh, NULL, -1, -1, NULL); fail_unless(res < 0, "Failed to handle null statement"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); stmt = "SELECT COUNT(*) FROM table"; idx = -1; res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_INT, NULL); fail_unless(res < 0, "Failed to handle invalid index %d", idx); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); idx = 1; res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_INT, NULL); fail_unless(res < 0, "Failed to handle unprepared statement"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got '%s' (%d)", ENOENT, strerror(errno), errno); res = create_table(p, dbh, "foo"); fail_unless(res == 0, "Failed to create table 'foo': %s", strerror(errno)); mark_point(); stmt = "SELECT COUNT(*) FROM foo;"; res = prom_db_prepare_stmt(p, dbh, stmt); fail_unless(res == 0, "Failed to prepare statement '%s': %s", stmt, strerror(errno)); mark_point(); res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_INT, NULL); fail_unless(res < 0, "Failed to handle missing INT value"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); int_val = 7; res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_INT, &int_val); fail_unless(res < 0, "Failed to handle invalid index value"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_LONG, NULL); fail_unless(res < 0, "Failed to handle missing LONG value"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); long_val = 7; res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_LONG, &long_val); fail_unless(res < 0, "Failed to handle invalid index value"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); mark_point(); double_val = 7.0; res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_DOUBLE, &double_val); fail_unless(res < 0, "Failed to handle invalid index value"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_TEXT, NULL); fail_unless(res < 0, "Failed to handle missing TEXT value"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); text_val = "testing"; res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_TEXT, text_val); fail_unless(res < 0, "Failed to handle invalid index value"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_NULL, NULL); fail_unless(res < 0, "Failed to handle invalid NULL value"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); mark_point(); stmt = "SELECT COUNT(*) FROM foo WHERE id = ?;"; res = prom_db_prepare_stmt(p, dbh, stmt); fail_unless(res == 0, "Failed to prepare statement '%s': %s", stmt, strerror(errno)); mark_point(); int_val = 7; res = prom_db_bind_stmt(p, dbh, stmt, idx, PROM_DB_BIND_TYPE_INT, &int_val); fail_unless(res == 0, "Failed to bind INT value: %s", strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_exec_prepared_stmt_test) { int res; array_header *results; const char *table_path, *schema_name, *stmt, *errstr = NULL; struct prom_dbh *dbh; mark_point(); results = prom_db_exec_prepared_stmt(NULL, NULL, NULL, NULL); fail_unless(results == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); results = prom_db_exec_prepared_stmt(p, NULL, NULL, NULL); fail_unless(results == NULL, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); results = prom_db_exec_prepared_stmt(p, dbh, NULL, NULL); fail_unless(results == NULL, "Failed to handle null statement"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); stmt = "SELECT COUNT(*) FROM foo;"; results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); fail_unless(results == NULL, "Failed to handle unprepared statement"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got '%s' (%d)", ENOENT, strerror(errno), errno); res = create_table(p, dbh, "foo"); fail_unless(res == 0, "Failed to create table 'foo': %s", strerror(errno)); mark_point(); res = prom_db_prepare_stmt(p, dbh, stmt); fail_unless(res == 0, "Failed to prepare statement '%s': %s", stmt, strerror(errno)); mark_point(); results = prom_db_exec_prepared_stmt(p, dbh, stmt, &errstr); fail_unless(results != NULL, "Failed to execute prepared statement '%s': %s (%s)", stmt, errstr, strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_reindex_test) { int res; const char *table_path, *schema_name, *index_name, *errstr = NULL; struct prom_dbh *dbh; mark_point(); res = prom_db_reindex(NULL, NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_db_reindex(p, NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); res = prom_db_reindex(p, dbh, NULL, NULL); fail_unless(res < 0, "Failed to handle null index name"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); mark_point(); index_name = "test_idx"; res = prom_db_reindex(p, dbh, index_name, &errstr); fail_unless(res < 0, "Failed to handle invalid index"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); fail_unless(errstr != NULL, "Failed to provide error string"); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST START_TEST (db_last_row_id_test) { int res; const char *table_path, *schema_name; int64_t row_id = 0; struct prom_dbh *dbh; mark_point(); res = prom_db_last_row_id(NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_db_last_row_id(p, NULL, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); (void) unlink(db_test_table); table_path = db_test_table; schema_name = "prometheus_test"; mark_point(); dbh = prom_db_open(p, table_path, schema_name); fail_unless(dbh != NULL, "Failed to open table '%s': %s", table_path, strerror(errno)); mark_point(); res = prom_db_last_row_id(p, dbh, NULL); fail_unless(res < 0, "Failed to handle null row_id"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_db_last_row_id(p, dbh, &row_id); fail_unless(res == 0, "Failed to get last row ID: %s", strerror(errno)); res = prom_db_close(p, dbh); fail_unless(res == 0, "Failed to close database: %s", strerror(errno)); (void) unlink(db_test_table); } END_TEST Suite *tests_get_db_suite(void) { Suite *suite; TCase *testcase; suite = suite_create("db"); testcase = tcase_create("base"); tcase_add_checked_fixture(testcase, set_up, tear_down); tcase_add_test(testcase, db_close_test); tcase_add_test(testcase, db_open_test); tcase_add_test(testcase, db_open_readonly_test); tcase_add_test(testcase, db_open_with_version_test); tcase_add_test(testcase, db_open_readonly_with_version_test); tcase_add_test(testcase, db_exec_stmt_test); tcase_add_test(testcase, db_prepare_stmt_test); tcase_add_test(testcase, db_finish_stmt_test); tcase_add_test(testcase, db_bind_stmt_test); tcase_add_test(testcase, db_exec_prepared_stmt_test); tcase_add_test(testcase, db_reindex_test); tcase_add_test(testcase, db_last_row_id_test); suite_add_tcase(suite, testcase); return suite; } proftpd-mod_prometheus-0.2/t/api/http.c000066400000000000000000000116151410622367000202250ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ /* HTTP API tests. */ #include "tests.h" #include "prometheus/http.h" #include "prometheus/registry.h" static pool *p = NULL; static void set_up(void) { if (p == NULL) { p = permanent_pool = make_sub_pool(NULL); } if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.http", 1, 20); } mark_point(); prom_http_init(p); } static void tear_down(void) { prom_http_free(); if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.http", 0, 0); } if (p != NULL) { destroy_pool(p); p = permanent_pool = NULL; } } START_TEST (http_init_test) { int res; mark_point(); res = prom_http_init(NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_http_init(p); fail_unless(res == 0, "Failed to init HTTP API: %s", strerror(errno)); } END_TEST START_TEST (http_free_test) { int res; mark_point(); res = prom_http_free(); fail_unless(res == 0, "Failed to free HTTP API: %s", strerror(errno)); } END_TEST START_TEST (http_stop_test) { int res; mark_point(); res = prom_http_stop(NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_http_stop(p, NULL); fail_unless(res < 0, "Failed to handle null http"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); } END_TEST START_TEST (http_start_test) { int res; pr_netaddr_t *addr; struct prom_http *http; struct prom_registry *registry; mark_point(); http = prom_http_start(NULL, NULL, NULL, NULL, NULL); fail_unless(http == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); http = prom_http_start(p, NULL, NULL, NULL, NULL); fail_unless(http == NULL, "Failed to handle null addr"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); addr = pr_netaddr_alloc(p); pr_netaddr_set_family(addr, AF_INET); pr_netaddr_set_sockaddr_any(addr); pr_netaddr_set_port2(addr, 0); http = prom_http_start(p, addr, NULL, NULL, NULL); fail_unless(http == NULL, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); /* Note: We don't need a real registry here, just a non-null pointer. */ registry = pcalloc(p, 8); http = prom_http_start(p, addr, registry, NULL, NULL); fail_unless(http != NULL, "Failed to start http: %s", strerror(errno)); mark_point(); res = prom_http_stop(p, http); fail_unless(res == 0, "Failed to stop http: %s", strerror(errno)); } END_TEST START_TEST (http_run_loop_test) { int res; mark_point(); res = prom_http_run_loop(NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_http_run_loop(p, NULL); fail_unless(res < 0, "Failed to handle null http"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); } END_TEST Suite *tests_get_http_suite(void) { Suite *suite; TCase *testcase; suite = suite_create("http"); testcase = tcase_create("base"); tcase_add_checked_fixture(testcase, set_up, tear_down); tcase_add_test(testcase, http_init_test); tcase_add_test(testcase, http_free_test); tcase_add_test(testcase, http_stop_test); tcase_add_test(testcase, http_start_test); tcase_add_test(testcase, http_run_loop_test); suite_add_tcase(suite, testcase); return suite; } proftpd-mod_prometheus-0.2/t/api/metric.c000066400000000000000000001052371410622367000205350ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ /* Metric API tests. */ #include "tests.h" #include "prometheus/db.h" #include "prometheus/metric.h" static pool *p = NULL; static const char *test_dir = "/tmp/prt-mod_prometheus-test-metrics"; static void set_up(void) { if (p == NULL) { p = permanent_pool = make_sub_pool(NULL); } (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.db", 1, 20); pr_trace_set_levels("prometheus.metric", 1, 20); pr_trace_set_levels("prometheus.metric.db", 1, 20); } mark_point(); prom_db_init(p); } static void tear_down(void) { if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.db", 0, 0); pr_trace_set_levels("prometheus.metric", 0, 0); pr_trace_set_levels("prometheus.metric.db", 0, 0); } prom_db_free(); (void) tests_rmpath(p, test_dir); if (p != NULL) { destroy_pool(p); p = permanent_pool = NULL; } } START_TEST (metric_free_test) { int res; mark_point(); res = prom_metric_free(NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_free(p, NULL); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); } END_TEST START_TEST (metric_init_test) { int res; struct prom_dbh *dbh; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); dbh = prom_metric_init(NULL, NULL); fail_unless(dbh == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, NULL); fail_unless(dbh == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_destroy_test) { int res; mark_point(); res = prom_metric_destroy(NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_destroy(p, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); } END_TEST START_TEST (metric_create_test) { int res; const char *name, *expected; struct prom_dbh *dbh; struct prom_metric *metric; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); metric = prom_metric_create(NULL, NULL, NULL); fail_unless(metric == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); metric = prom_metric_create(p, NULL, NULL); fail_unless(metric == NULL, "Failed to handle null name"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); name = "test"; metric = prom_metric_create(p, name, NULL); fail_unless(metric == NULL, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); expected = "test"; fail_unless(strcmp(prom_metric_get_name(metric), expected) == 0, "Expected metric name '%s', got '%s'", expected, prom_metric_get_name(metric)); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_add_counter_test) { int res; const char *name, *suffix; struct prom_dbh *dbh; struct prom_metric *metric; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_add_counter(NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_counter(metric, NULL, NULL); fail_unless(res < 0, "Failed to handle null help"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); suffix = "total"; res = prom_metric_add_counter(metric, suffix, "testing"); fail_unless(res == 0, "Failed to add counter to metric: %s", strerror(errno)); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_add_gauge_test) { int res; const char *name, *suffix; struct prom_dbh *dbh; struct prom_metric *metric; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_add_gauge(NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_gauge(metric, NULL, NULL); fail_unless(res < 0, "Failed to handle null help"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); suffix = "count"; res = prom_metric_add_gauge(metric, suffix, "testing"); fail_unless(res == 0, "Failed to add gauge to metric: %s", strerror(errno)); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_add_histogram_test) { int res; const char *name, *suffix; struct prom_dbh *dbh; struct prom_metric *metric; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_add_histogram(NULL, NULL, NULL, 0); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_histogram(metric, NULL, NULL, 0); fail_unless(res < 0, "Failed to handle null help"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); suffix = "weight"; res = prom_metric_add_histogram(metric, suffix, "testing", 0); fail_unless(res == 0, "Failed to add histogram to metric: %s", strerror(errno)); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_set_dbh_test) { int res; struct prom_metric *metric; struct prom_dbh *dbh; mark_point(); res = prom_metric_set_dbh(NULL, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); /* For purposes of testing, this does not have to be a real dbh. */ mark_point(); dbh = palloc(p, 8); metric = prom_metric_create(p, "test", dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_set_dbh(metric, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_set_dbh(metric, dbh); fail_unless(res == 0, "Failed to set dbh: %s", strerror(errno)); prom_metric_destroy(p, metric); } END_TEST START_TEST (metric_get_test) { int res; const char *name; struct prom_dbh *dbh; struct prom_metric *metric; const array_header *results; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); results = prom_metric_get(NULL, NULL, 0, NULL, NULL); fail_unless(results == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); results = prom_metric_get(p, NULL, 0, NULL, NULL); fail_unless(results == NULL, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, -1, NULL, NULL); fail_unless(results == NULL, "Failed to handle unknown metric type"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_COUNTER, NULL, NULL); fail_unless(results == NULL, "Failed to handle counter-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_GAUGE, NULL, NULL); fail_unless(results == NULL, "Failed to handle gauge-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_HISTOGRAM, NULL, NULL); fail_unless(results == NULL, "Failed to handle histogram-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_decr_test) { int res; const char *name; struct prom_dbh *dbh; struct prom_metric *metric; uint32_t decr_val = 32; pr_table_t *labels; const array_header *results; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_decr(NULL, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_decr(p, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_decr(p, metric, decr_val, NULL); fail_unless(res < 0, "Failed to handle gauge-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_metric_add_gauge(metric, "count", "testing"); fail_unless(res == 0, "Failed to add gauge to metric: %s", strerror(errno)); mark_point(); res = prom_metric_decr(p, metric, decr_val, NULL); fail_unless(res == 0, "Failed to decrement metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_GAUGE, NULL, NULL); fail_unless(results != NULL, "Failed to get label-less gauge samples: %s", strerror(errno)); fail_unless(results->nelts == 2, "Expected 2 results, got %d", results->nelts); /* Now, provide labels. */ labels = pr_table_nalloc(p, 0, 2); (void) pr_table_add_dup(labels, "protocol", "ftp", 0); (void) pr_table_add_dup(labels, "foo", "BAR", 0); mark_point(); res = prom_metric_decr(p, metric, decr_val, labels); fail_unless(res == 0, "Failed to decrement metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_GAUGE, NULL, NULL); fail_unless(results != NULL, "Failed to get labeled gauge samples: %s", strerror(errno)); fail_unless(results->nelts == 4, "Expected 4 results, got %d", results->nelts); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_incr_type_test) { int res; const char *name; struct prom_dbh *dbh; struct prom_metric *metric; uint32_t incr_val = 66; pr_table_t *labels; const array_header *results; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_incr_type(NULL, NULL, 0, NULL, 0); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_incr_type(p, NULL, 0, NULL, 0); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_incr_type(p, metric, incr_val, NULL, 0); fail_unless(res < 0, "Failed to handle unknown metric type"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_incr_type(p, metric, incr_val, NULL, PROM_METRIC_TYPE_COUNTER); fail_unless(res < 0, "Failed to handle counter-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_metric_incr_type(p, metric, incr_val, NULL, PROM_METRIC_TYPE_GAUGE); fail_unless(res < 0, "Failed to handle gauge-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_metric_add_counter(metric, "total", "testing"); fail_unless(res == 0, "Failed to add counter to metric: %s", strerror(errno)); mark_point(); res = prom_metric_incr_type(p, metric, incr_val, NULL, PROM_METRIC_TYPE_COUNTER); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_COUNTER, NULL, NULL); fail_unless(results != NULL, "Failed to get label-less counter samples: %s", strerror(errno)); fail_unless(results->nelts == 2, "Expected 2 results, got %d", results->nelts); /* Now, provide labels. */ labels = pr_table_nalloc(p, 0, 2); (void) pr_table_add_dup(labels, "protocol", "ftp", 0); (void) pr_table_add_dup(labels, "foo", "BAR", 0); mark_point(); res = prom_metric_incr_type(p, metric, incr_val, labels, PROM_METRIC_TYPE_COUNTER); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_COUNTER, NULL, NULL); fail_unless(results != NULL, "Failed to get labeled counter samples: %s", strerror(errno)); fail_unless(results->nelts == 4, "Expected 4 results, got %d", results->nelts); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_incr_test) { int res; const char *name; struct prom_dbh *dbh; struct prom_metric *metric; uint32_t incr_val = 66; pr_table_t *labels; const array_header *results; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_incr(NULL, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_incr(p, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_incr(p, metric, incr_val, NULL); fail_unless(res < 0, "Failed to handle counter-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_metric_add_counter(metric, "total", "testing"); fail_unless(res == 0, "Failed to add counter to metric: %s", strerror(errno)); mark_point(); res = prom_metric_incr(p, metric, incr_val, NULL); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_COUNTER, NULL, NULL); fail_unless(results != NULL, "Failed to get label-less counter samples: %s", strerror(errno)); fail_unless(results->nelts == 2, "Expected 2 results, got %d", results->nelts); /* Now, provide labels. */ labels = pr_table_nalloc(p, 0, 2); (void) pr_table_add_dup(labels, "protocol", "ftp", 0); (void) pr_table_add_dup(labels, "foo", "BAR", 0); mark_point(); res = prom_metric_incr(p, metric, incr_val, labels); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_COUNTER, NULL, NULL); fail_unless(results != NULL, "Failed to get labeled counter samples: %s", strerror(errno)); fail_unless(results->nelts == 4, "Expected 4 results, got %d", results->nelts); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_incr_counter_gauge_test) { int res; const char *name; struct prom_dbh *dbh; struct prom_metric *metric; uint32_t incr_val = 66; pr_table_t *labels; const array_header *results; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_counter(metric, "total", "testing"); fail_unless(res == 0, "Failed to add counter to metric: %s", strerror(errno)); res = prom_metric_add_gauge(metric, "count", "testing"); fail_unless(res == 0, "Failed to add gauge to metric: %s", strerror(errno)); mark_point(); res = prom_metric_incr(p, metric, incr_val, NULL); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_COUNTER, NULL, NULL); fail_unless(results != NULL, "Failed to get label-less counter samples: %s", strerror(errno)); fail_unless(results->nelts == 2, "Expected 2 results, got %d", results->nelts); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_GAUGE, NULL, NULL); fail_unless(results != NULL, "Failed to get label-less gauge samples: %s", strerror(errno)); fail_unless(results->nelts == 2, "Expected 2 results, got %d", results->nelts); /* Now, provide labels. */ labels = pr_table_nalloc(p, 0, 2); (void) pr_table_add_dup(labels, "protocol", "ftp", 0); (void) pr_table_add_dup(labels, "foo", "BAR", 0); mark_point(); res = prom_metric_incr(p, metric, incr_val, labels); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_COUNTER, NULL, NULL); fail_unless(results != NULL, "Failed to get labeled counter samples: %s", strerror(errno)); fail_unless(results->nelts == 4, "Expected 4 results, got %d", results->nelts); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_GAUGE, NULL, NULL); fail_unless(results != NULL, "Failed to get labeled gauge samples: %s", strerror(errno)); fail_unless(results->nelts == 4, "Expected 4 results, got %d", results->nelts); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_observe_test) { int res; const char *name; struct prom_dbh *dbh; struct prom_metric *metric; double observed_val = 3.1415; pr_table_t *labels; const array_header *results, *counts = NULL, *sums = NULL; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_observe(NULL, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_observe(p, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_observe(p, metric, observed_val, NULL); fail_unless(res < 0, "Failed to handle histogram-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_metric_add_histogram(metric, "units", "testing", 0); fail_unless(res == 0, "Failed to add histogram to metric: %s", strerror(errno)); mark_point(); res = prom_metric_observe(p, metric, observed_val, NULL); fail_unless(res == 0, "Failed to observe metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_HISTOGRAM, &counts, &sums); fail_unless(results != NULL, "Failed get histogram results: %s", strerror(errno)); fail_unless(results->nelts == 2, "Expected 2 bucket results, got %d", results->nelts); fail_unless(counts != NULL, "Failed get histogram count results: %s", strerror(errno)); fail_unless(counts->nelts == 2, "Expected 2 count results, got %d", counts->nelts); fail_unless(sums != NULL, "Failed get histogram sum results: %s", strerror(errno)); fail_unless(sums->nelts == 2, "Expected 2 sum results, got %d", sums->nelts); /* Now, provide labels. */ labels = pr_table_nalloc(p, 0, 2); (void) pr_table_add_dup(labels, "protocol", "ftp", 0); (void) pr_table_add_dup(labels, "foo", "BAR", 0); mark_point(); res = prom_metric_observe(p, metric, observed_val, labels); fail_unless(res == 0, "Failed to observe metric: %s", strerror(errno)); mark_point(); counts = sums = NULL; results = prom_metric_get(p, metric, PROM_METRIC_TYPE_HISTOGRAM, &counts, &sums); fail_unless(results != NULL, "Failed to get histogram results: %s", strerror(errno)); fail_unless(results->nelts == 4, "Expected 4 bucket results, got %d", results->nelts); fail_unless(counts != NULL, "Failed to get histogram count results: %s", strerror(errno)); fail_unless(counts->nelts == 4, "Expected 4 count results, got %d", counts->nelts); fail_unless(sums != NULL, "Failed to get histogram sum results: %s", strerror(errno)); fail_unless(sums->nelts == 4, "Expected 4 sum results, got %d", sums->nelts); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_set_test) { int res; const char *name; struct prom_dbh *dbh; struct prom_metric *metric; uint32_t set_val = 42; pr_table_t *labels; const array_header *results; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_set(NULL, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_set(p, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_set(p, metric, set_val, NULL); fail_unless(res < 0, "Failed to handle gauge-less metric"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); res = prom_metric_add_gauge(metric, "count", "testing"); fail_unless(res == 0, "Failed to add gauge to metric: %s", strerror(errno)); mark_point(); res = prom_metric_set(p, metric, set_val, NULL); fail_unless(res == 0, "Failed to set metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_GAUGE, NULL, NULL); fail_unless(results != NULL, "Failed to get label-less gauge samples: %s", strerror(errno)); fail_unless(results->nelts == 2, "Expected 2 results, got %d", results->nelts); /* Now, provide labels. */ labels = pr_table_nalloc(p, 0, 2); (void) pr_table_add_dup(labels, "protocol", "ftp", 0); (void) pr_table_add_dup(labels, "foo", "BAR", 0); mark_point(); res = prom_metric_set(p, metric, set_val, labels); fail_unless(res == 0, "Failed to set metric: %s", strerror(errno)); mark_point(); results = prom_metric_get(p, metric, PROM_METRIC_TYPE_GAUGE, NULL, NULL); fail_unless(results != NULL, "Failed to get labeled gauge samples: %s", strerror(errno)); fail_unless(results->nelts == 4, "Expected 4 results, got %d", results->nelts); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_get_text_test) { int res; const char *name, *text; size_t textlen = 0; struct prom_dbh *dbh; struct prom_metric *metric; pr_table_t *labels; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); name = "test"; metric = prom_metric_create(p, name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_counter(metric, "total", "counter testing"); fail_unless(res == 0, "Failed to add counter to metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_gauge(metric, "count", "gauge testing"); fail_unless(res == 0, "Failed to add gauge to metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_histogram(metric, "weight", "histogram testing", 0); fail_unless(res == 0, "Failed to add histogram to metric: %s", strerror(errno)); mark_point(); res = prom_metric_incr(p, metric, 6, NULL); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); /* Now, provide labels. */ labels = pr_table_nalloc(p, 0, 2); (void) pr_table_add_dup(labels, "protocol", "ftp", 0); (void) pr_table_add_dup(labels, "foo", "BAR", 0); mark_point(); res = prom_metric_incr(p, metric, 8, labels); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); res = prom_metric_observe(p, metric, 76.42, labels); fail_unless(res == 0, "Failed to observe metric: %s", strerror(errno)); mark_point(); text = prom_metric_get_text(p, metric, "prt", &textlen); fail_unless(text != NULL, "Failed to get metric text: %s", strerror(errno)); fail_unless(textlen > 0, "Expected text data, got %lu", (unsigned long) textlen); /* Use strstr(3) to assert bits of text. */ fail_unless(strstr(text, "# HELP prt_test_total") != NULL, "Expected counter HELP text"); fail_unless(strstr(text, "# TYPE prt_test_total counter") != NULL, "Expected counter TYPE text"); fail_unless(strstr(text, "prt_test_total 6") != NULL, "Expected label-less counter sample"); fail_unless( strstr(text, "prt_test_total{foo=\"BAR\",protocol=\"ftp\"} 8") != NULL, "Expected labeled counter sample"); fail_unless(strstr(text, "# HELP prt_test_count") != NULL, "Expected gauge HELP text"); fail_unless(strstr(text, "# TYPE prt_test_count gauge") != NULL, "Expected gauge TYPE text"); fail_unless(strstr(text, "prt_test_count 6") != NULL, "Expected label-less gauge sample"); fail_unless( strstr(text, "prt_test_count{foo=\"BAR\",protocol=\"ftp\"} 8") != NULL, "Expected labeled gauge sample"); fail_unless(strstr(text, "# HELP prt_test_weight") != NULL, "Expected histogram HELP text"); fail_unless(strstr(text, "# TYPE prt_test_weight histogram") != NULL, "Expected histogram TYPE text"); fail_unless( strstr(text, "prt_test_weight_bucket{foo=\"BAR\",le=\"+Inf\",protocol=\"ftp\"} 1") != NULL, "Expected labeled histogram bucket sample"); fail_unless( strstr(text, "prt_test_weight_count{foo=\"BAR\",protocol=\"ftp\"} 1") != NULL, "Expected labeled histogram count sample"); fail_unless( strstr(text, "prt_test_weight_sum{foo=\"BAR\",protocol=\"ftp\"} 76.42") != NULL, "Expected labeled histogram sum sample"); mark_point(); res = prom_metric_destroy(p, metric); fail_unless(res == 0, "Failed to destroy metric: %s", strerror(errno)); res = prom_metric_free(p, dbh); fail_unless(res == 0, "Failed to free metrics: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST Suite *tests_get_metric_suite(void) { Suite *suite; TCase *testcase; suite = suite_create("metric"); testcase = tcase_create("base"); tcase_add_checked_fixture(testcase, set_up, tear_down); tcase_add_test(testcase, metric_free_test); tcase_add_test(testcase, metric_init_test); tcase_add_test(testcase, metric_destroy_test); tcase_add_test(testcase, metric_create_test); tcase_add_test(testcase, metric_add_counter_test); tcase_add_test(testcase, metric_add_gauge_test); tcase_add_test(testcase, metric_add_histogram_test); tcase_add_test(testcase, metric_set_dbh_test); tcase_add_test(testcase, metric_get_test); tcase_add_test(testcase, metric_decr_test); tcase_add_test(testcase, metric_incr_type_test); tcase_add_test(testcase, metric_incr_test); tcase_add_test(testcase, metric_incr_counter_gauge_test); tcase_add_test(testcase, metric_observe_test); tcase_add_test(testcase, metric_set_test); tcase_add_test(testcase, metric_get_text_test); suite_add_tcase(suite, testcase); return suite; } proftpd-mod_prometheus-0.2/t/api/metric/000077500000000000000000000000001410622367000203615ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/api/metric/db.c000066400000000000000000000401711410622367000211150ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ /* Metric database API tests. */ #include "../tests.h" #include "prometheus/db.h" #include "prometheus/metric.h" #include "prometheus/metric/db.h" static pool *p = NULL; static const char *test_dir = "/tmp/prt-mod_prometheus-test-db"; static void set_up(void) { if (p == NULL) { p = permanent_pool = make_sub_pool(NULL); } (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.db", 1, 20); pr_trace_set_levels("prometheus.metric.db", 1, 20); } mark_point(); prom_db_init(p); } static void tear_down(void) { if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.db", 0, 0); pr_trace_set_levels("prometheus.metric.db", 0, 0); } prom_db_free(); (void) tests_rmpath(p, test_dir); if (p != NULL) { destroy_pool(p); p = permanent_pool = NULL; } } START_TEST (metric_db_close_test) { int res; mark_point(); res = prom_metric_db_close(NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_close(p, NULL); fail_unless(res == 0, "Failed to handle null dbh"); } END_TEST START_TEST (metric_db_init_test) { int res, flags = PROM_DB_OPEN_FL_SKIP_VACUUM; struct prom_dbh *dbh; mark_point(); dbh = prom_metric_db_init(NULL, NULL, 0); fail_unless(dbh == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, NULL, 0); fail_unless(dbh == NULL, "Failed to handle null path"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, test_dir, flags); fail_unless(dbh != NULL, "Failed to init metrics db: %s", strerror(errno)); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_db_open_test) { int res, flags = PROM_DB_OPEN_FL_SKIP_VACUUM; struct prom_dbh *dbh; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); dbh = prom_metric_db_open(NULL, NULL); fail_unless(dbh == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_open(p, NULL); fail_unless(dbh == NULL, "Failed to handle null path"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_open(p, test_dir); fail_unless(dbh == NULL, "Failed to handle missing database"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, test_dir, flags); fail_unless(dbh != NULL, "Failed to init metrics db: %s", strerror(errno)); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); mark_point(); dbh = prom_metric_db_open(p, test_dir); fail_unless(dbh != NULL, "Failed to open metrics db: %s", strerror(errno)); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_db_exists_test) { int res, flags = PROM_DB_OPEN_FL_SKIP_VACUUM; const char *metric_name = "test_metric"; struct prom_dbh *dbh; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_db_exists(NULL, NULL, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_exists(p, NULL, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, test_dir, flags); fail_unless(dbh != NULL, "Failed to init metrics db: %s", strerror(errno)); mark_point(); res = prom_metric_db_exists(p, dbh, metric_name); fail_unless(res < 0, "Failed to handle nonexistent metric"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, strerror(errno), errno); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_db_create_test) { int res, flags = PROM_DB_OPEN_FL_SKIP_VACUUM, metric_type = 1; const char *metric_name = "test_metric"; int64_t metric_id = 0; struct prom_dbh *dbh; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_db_create(NULL, NULL, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_create(p, NULL, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, test_dir, flags); fail_unless(dbh != NULL, "Failed to init metrics db: %s", strerror(errno)); mark_point(); res = prom_metric_db_create(p, dbh, NULL, 0, NULL); fail_unless(res < 0, "Failed to handle null metric name"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_create(p, dbh, metric_name, metric_type, &metric_id); fail_unless(res == 0, "Failed to add db metric '%s': %s", metric_name, strerror(errno)); mark_point(); res = prom_metric_db_exists(p, dbh, metric_name); fail_unless(res == 0, "Failed to detect existing metric '%s': %s", metric_name, strerror(errno)); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_db_sample_exists_test) { } END_TEST START_TEST (metric_db_sample_get_test) { int res, flags = PROM_DB_OPEN_FL_SKIP_VACUUM; struct prom_dbh *dbh; const array_header *results; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); results = prom_metric_db_sample_get(NULL, NULL, 0); fail_unless(results == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); results = prom_metric_db_sample_get(p, NULL, 0); fail_unless(results == NULL, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, test_dir, flags); fail_unless(dbh != NULL, "Failed to init metrics db: %s", strerror(errno)); mark_point(); results = prom_metric_db_sample_get(p, dbh, 0); fail_unless(results != NULL, "Failed to get metric samples: %s", strerror(errno)); fail_unless(results->nelts == 0, "Expected zero results, got %d", results->nelts); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_db_sample_decr_test) { int res, flags = PROM_DB_OPEN_FL_SKIP_VACUUM; int64_t metric_id = 24; double decr_val = 76.24, sample_val; struct prom_dbh *dbh; const array_header *results; char **elts, *ptr; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_db_sample_decr(NULL, NULL, 0, 0.0, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_sample_decr(p, NULL, 0, 0.0, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, test_dir, flags); fail_unless(dbh != NULL, "Failed to init metrics db: %s", strerror(errno)); mark_point(); res = prom_metric_db_sample_decr(p, dbh, metric_id, decr_val, NULL); fail_unless(res < 0, "Failed to handle null sample labels"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_sample_decr(p, dbh, metric_id, decr_val, ""); fail_unless(res == 0, "Failed to decrement metric ID %ld: %s", metric_id, strerror(errno)); mark_point(); results = prom_metric_db_sample_get(p, dbh, metric_id); fail_unless(results != NULL, "Failed to get samples for metric ID %ld: %s", metric_id, strerror(errno)); fail_unless(results->nelts == 2, "Expected results->nelts = 2, got %d", results->nelts); elts = results->elts; fail_unless(elts[0] != NULL, "Expected sample value, got NULL"); ptr = NULL; sample_val = strtod(elts[0], &ptr); fail_if(ptr != NULL && *ptr, "Expected double sample value, got '%s'", elts[0]); fail_unless((int) -sample_val == (int) decr_val, "Expected sample value %lf, got %lf", decr_val, sample_val); fail_unless(elts[1] != NULL, "Expected sample_labels, got null"); fail_unless(strcmp(elts[1], "") == 0, "Expected sample labels '', got '%s'", elts[1]); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_db_sample_incr_test) { int res, flags = PROM_DB_OPEN_FL_SKIP_VACUUM; int64_t metric_id = 42; double incr_val = 24.76, sample_val; struct prom_dbh *dbh; const array_header *results; char **elts, *ptr; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_db_sample_incr(NULL, NULL, 0, 0.0, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_sample_incr(p, NULL, 0, 0.0, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, test_dir, flags); fail_unless(dbh != NULL, "Failed to init metrics db: %s", strerror(errno)); mark_point(); res = prom_metric_db_sample_incr(p, dbh, metric_id, incr_val, NULL); fail_unless(res < 0, "Failed to handle null sample labels"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_sample_incr(p, dbh, metric_id, incr_val, ""); fail_unless(res == 0, "Failed to increment metric ID %ld: %s", metric_id, strerror(errno)); mark_point(); results = prom_metric_db_sample_get(p, dbh, metric_id); fail_unless(results != NULL, "Failed to get samples for metric ID %ld: %s", metric_id, strerror(errno)); fail_unless(results->nelts == 2, "Expected results->nelts = 2, got %d", results->nelts); elts = results->elts; fail_unless(elts[0] != NULL, "Expected sample value, got NULL"); ptr = NULL; sample_val = strtod(elts[0], &ptr); fail_if(ptr != NULL && *ptr, "Expected double sample value, got '%s'", elts[0]); fail_unless((int) sample_val == (int) incr_val, "Expected sample value %lf, got %lf", incr_val, sample_val); fail_unless(elts[1] != NULL, "Expected sample_labels, got null"); fail_unless(strcmp(elts[1], "") == 0, "Expected sample labels '', got '%s'", elts[1]); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (metric_db_sample_set_test) { int res, flags = PROM_DB_OPEN_FL_SKIP_VACUUM; int64_t metric_id = 84; double set_val = 3.1514, sample_val; struct prom_dbh *dbh; const array_header *results; char **elts, *ptr; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); res = prom_metric_db_sample_set(NULL, NULL, 0, 0.0, NULL); fail_unless(res < 0, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_sample_set(p, NULL, 0, 0.0, NULL); fail_unless(res < 0, "Failed to handle null dbh"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); dbh = prom_metric_db_init(p, test_dir, flags); fail_unless(dbh != NULL, "Failed to init metrics db: %s", strerror(errno)); mark_point(); res = prom_metric_db_sample_set(p, dbh, metric_id, set_val, NULL); fail_unless(res < 0, "Failed to handle null sample labels"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_metric_db_sample_set(p, dbh, metric_id, set_val, ""); fail_unless(res == 0, "Failed to set metric ID %ld: %s", metric_id, strerror(errno)); mark_point(); results = prom_metric_db_sample_get(p, dbh, metric_id); fail_unless(results != NULL, "Failed to get samples for metric ID %ld: %s", metric_id, strerror(errno)); fail_unless(results->nelts == 2, "Expected results->nelts = 2, got %d", results->nelts); elts = results->elts; fail_unless(elts[0] != NULL, "Expected sample value, got NULL"); ptr = NULL; sample_val = strtod(elts[0], &ptr); fail_if(ptr != NULL && *ptr, "Expected double sample value, got '%s'", elts[0]); fail_unless((int) sample_val == (int) set_val, "Expected sample value %lf, got %lf", set_val, sample_val); fail_unless(elts[1] != NULL, "Expected sample_labels, got null"); fail_unless(strcmp(elts[1], "") == 0, "Expected sample labels '', got '%s'", elts[1]); res = prom_metric_db_close(p, dbh); fail_unless(res == 0, "Failed to close metrics db: %s", strerror(errno)); (void) tests_rmpath(p, test_dir); } END_TEST Suite *tests_get_metric_db_suite(void) { Suite *suite; TCase *testcase; suite = suite_create("metric.db"); testcase = tcase_create("base"); tcase_add_checked_fixture(testcase, set_up, tear_down); tcase_add_test(testcase, metric_db_close_test); tcase_add_test(testcase, metric_db_init_test); tcase_add_test(testcase, metric_db_open_test); tcase_add_test(testcase, metric_db_exists_test); tcase_add_test(testcase, metric_db_create_test); tcase_add_test(testcase, metric_db_sample_exists_test); tcase_add_test(testcase, metric_db_sample_get_test); tcase_add_test(testcase, metric_db_sample_decr_test); tcase_add_test(testcase, metric_db_sample_incr_test); tcase_add_test(testcase, metric_db_sample_set_test); suite_add_tcase(suite, testcase); return suite; } proftpd-mod_prometheus-0.2/t/api/registry.c000066400000000000000000000327331410622367000211220ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ /* Registry API tests. */ #include "tests.h" #include "prometheus/registry.h" #include "prometheus/metric/db.h" static pool *p = NULL; static const char *test_dir = "/tmp/prt-mod_prometheus-test-registry"; static void set_up(void) { if (p == NULL) { p = permanent_pool = make_sub_pool(NULL); } (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.db", 1, 20); pr_trace_set_levels("prometheus.registry", 1, 20); } mark_point(); prom_db_init(p); } static void tear_down(void) { if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.db", 0, 0); pr_trace_set_levels("prometheus.registry", 0, 0); } prom_db_free(); (void) tests_rmpath(p, test_dir); if (p != NULL) { destroy_pool(p); p = permanent_pool = NULL; } } START_TEST (registry_free_test) { int res; mark_point(); res = prom_registry_free(NULL); fail_unless(res < 0, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); } END_TEST START_TEST (registry_init_test) { int res; const char *name; struct prom_registry *registry; mark_point(); registry = prom_registry_init(NULL, NULL); fail_unless(registry == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); registry = prom_registry_init(p, NULL); fail_unless(registry == NULL, "Failed to handle null name"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); registry = prom_registry_init(p, "test"); fail_unless(registry != NULL, "Failed to create registry: %s", strerror(errno)); mark_point(); name = prom_registry_get_name(NULL); fail_unless(name == NULL, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); name = prom_registry_get_name(registry); fail_unless(name != NULL, "Failed to get name: %s", strerror(errno)); fail_unless(strcmp(name, "test") == 0, "Expected 'test', got '%s'", name); res = prom_registry_free(registry); fail_unless(res == 0, "Failed to free registry: %s", strerror(errno)); } END_TEST START_TEST (registry_get_metric_test) { struct prom_registry *registry; const struct prom_metric *metric; mark_point(); metric = prom_registry_get_metric(NULL, NULL); fail_unless(metric == NULL, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); registry = prom_registry_init(p, "test"); fail_unless(registry != NULL, "Failed to create registry: %s", strerror(errno)); mark_point(); metric = prom_registry_get_metric(registry, NULL); fail_unless(metric == NULL, "Failed to handle null metric name"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); prom_registry_free(registry); } END_TEST START_TEST (registry_add_metric_test) { int res; struct prom_registry *registry; char *metric_name; struct prom_metric *metric; struct prom_dbh *dbh; mark_point(); res = prom_registry_add_metric(NULL, NULL); fail_unless(res < 0, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); registry = prom_registry_init(p, "test"); fail_unless(registry != NULL, "Failed to create registry: %s", strerror(errno)); mark_point(); res = prom_registry_add_metric(registry, NULL); fail_unless(res < 0, "Failed to handle null metric"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); /* For purposes of testing, we don't need a real dbh here. */ mark_point(); metric_name = "metric"; dbh = palloc(p, 8); metric = prom_metric_create(p, metric_name, dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); res = prom_registry_add_metric(registry, metric); fail_unless(res == 0, "Failed to add metric: %s", strerror(errno)); mark_point(); metric = (struct prom_metric *) prom_registry_get_metric(registry, metric_name); fail_unless(metric != NULL, "Failed to get metric: %s", strerror(errno)); prom_registry_free(registry); } END_TEST START_TEST (registry_sort_metrics_test) { int res; struct prom_registry *registry; struct prom_metric *first_metric, *second_metric; struct prom_dbh *dbh; mark_point(); res = prom_registry_sort_metrics(NULL); fail_unless(res < 0, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); registry = prom_registry_init(p, "test"); fail_unless(registry != NULL, "Failed to create registry: %s", strerror(errno)); /* For purposes of testing, we don't need a real dbh here. */ mark_point(); dbh = palloc(p, 8); first_metric = prom_metric_create(p, "first", dbh); fail_unless(first_metric != NULL, "Failed to create metric: %s", strerror(errno)); res = prom_registry_add_metric(registry, first_metric); fail_unless(res == 0, "Failed to add metric: %s", strerror(errno)); mark_point(); res = prom_registry_sort_metrics(registry); fail_unless(res == 0, "Failed to sort metrics: %s", strerror(errno)); mark_point(); second_metric = prom_metric_create(p, "second", dbh); fail_unless(second_metric != NULL, "Failed to create metric: %s", strerror(errno)); res = prom_registry_add_metric(registry, second_metric); fail_unless(res == 0, "Failed to add metric: %s", strerror(errno)); res = prom_registry_sort_metrics(registry); fail_unless(res == 0, "Failed to sort metrics: %s", strerror(errno)); prom_registry_free(registry); } END_TEST START_TEST (registry_set_dbh_test) { int res; struct prom_registry *registry; struct prom_dbh *dbh; mark_point(); res = prom_registry_set_dbh(NULL, NULL); fail_unless(res < 0, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); registry = prom_registry_init(p, "test"); fail_unless(registry != NULL, "Failed to create registry: %s", strerror(errno)); mark_point(); res = prom_registry_set_dbh(registry, NULL); fail_unless(res < 0, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); /* For purposes of testing, we don't need a real dbh here. */ mark_point(); dbh = palloc(p, 8); res = prom_registry_set_dbh(registry, dbh); fail_unless(res == 0, "Failed to handle set dbh: %s", strerror(errno)); prom_registry_free(registry); } END_TEST START_TEST (registry_get_text_test) { const char *text; struct prom_registry *registry; mark_point(); text = prom_registry_get_text(NULL, NULL); fail_unless(text == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); text = prom_registry_get_text(p, NULL); fail_unless(text == NULL, "Failed to handle null registry"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); registry = prom_registry_init(p, "test"); fail_unless(registry != NULL, "Failed to create registry: %s", strerror(errno)); mark_point(); text = prom_registry_get_text(p, registry); fail_unless(text == NULL, "Failed to handle absent metrics"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, strerror(errno), errno); prom_registry_free(registry); } END_TEST START_TEST (registry_get_text_with_metrics_test) { int res; const char *text; struct prom_registry *registry; struct prom_metric *metric; struct prom_dbh *dbh; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); registry = prom_registry_init(p, "test"); fail_unless(registry != NULL, "Failed to create registry: %s", strerror(errno)); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); metric = prom_metric_create(p, "metric", dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_registry_add_metric(registry, metric); fail_unless(res == 0, "Failed to register metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_counter(metric, "total", "testing"); fail_unless(res == 0, "Failed to add counter to metric: %s", strerror(errno)); mark_point(); res = prom_metric_incr(p, metric, 1, NULL); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); text = prom_registry_get_text(p, registry); fail_unless(text != NULL, "Failed to get registry text: %s", strerror(errno)); /* Use strstr(3) to assert bits of text. */ fail_unless(strstr(text, "# HELP test_metric_total") != NULL, "Expected metric help, got '%s'", text); fail_unless(strstr(text, "# TYPE test_metric_total counter") != NULL, "Expected metric type, got '%s'", text); fail_unless(strstr(text, "test_metric_total 1") != NULL, "Expected metric sample, got '%s'", text); prom_registry_free(registry); prom_db_close(p, dbh); (void) tests_rmpath(p, test_dir); } END_TEST START_TEST (registry_get_text_with_metrics_readonly_test) { int res; const char *text; struct prom_registry *registry; struct prom_metric *metric; struct prom_dbh *dbh; (void) tests_rmpath(p, test_dir); (void) tests_mkpath(p, test_dir); mark_point(); registry = prom_registry_init(p, "test"); fail_unless(registry != NULL, "Failed to create registry: %s", strerror(errno)); mark_point(); dbh = prom_metric_init(p, test_dir); fail_unless(dbh != NULL, "Failed to init metrics: %s", strerror(errno)); mark_point(); metric = prom_metric_create(p, "metric", dbh); fail_unless(metric != NULL, "Failed to create metric: %s", strerror(errno)); mark_point(); res = prom_registry_add_metric(registry, metric); fail_unless(res == 0, "Failed to register metric: %s", strerror(errno)); mark_point(); res = prom_metric_add_counter(metric, "total", "testing"); fail_unless(res == 0, "Failed to add counter to metric: %s", strerror(errno)); mark_point(); res = prom_metric_incr(p, metric, 1, NULL); fail_unless(res == 0, "Failed to increment metric: %s", strerror(errno)); mark_point(); /* Now, close that dbh. Open a readonly one, set it in the registry. * This approximates what happens with the exporter process. */ (void) prom_db_close(p, dbh); dbh = prom_metric_db_open(p, test_dir); fail_unless(dbh != NULL, "Failed to open readonly dbh: %s", strerror(errno)); mark_point(); res = prom_registry_set_dbh(registry, dbh); fail_unless(res == 0, "Failed to set registry dbh: %s", strerror(errno)); mark_point(); text = prom_registry_get_text(p, registry); fail_unless(text != NULL, "Failed to get registry text: %s", strerror(errno)); /* Use strstr(3) to assert bits of text. */ fail_unless(strstr(text, "# HELP test_metric_total") != NULL, "Expected metric help, got '%s'", text); fail_unless(strstr(text, "# TYPE test_metric_total counter") != NULL, "Expected metric type, got '%s'", text); fail_unless(strstr(text, "test_metric_total 1") != NULL, "Expected metric sample, got '%s'", text); prom_registry_free(registry); prom_db_close(p, dbh); (void) tests_rmpath(p, test_dir); } END_TEST Suite *tests_get_registry_suite(void) { Suite *suite; TCase *testcase; suite = suite_create("registry"); testcase = tcase_create("base"); tcase_add_checked_fixture(testcase, set_up, tear_down); tcase_add_test(testcase, registry_free_test); tcase_add_test(testcase, registry_init_test); tcase_add_test(testcase, registry_get_metric_test); tcase_add_test(testcase, registry_add_metric_test); tcase_add_test(testcase, registry_sort_metrics_test); tcase_add_test(testcase, registry_set_dbh_test); tcase_add_test(testcase, registry_get_text_test); tcase_add_test(testcase, registry_get_text_with_metrics_test); tcase_add_test(testcase, registry_get_text_with_metrics_readonly_test); suite_add_tcase(suite, testcase); return suite; } proftpd-mod_prometheus-0.2/t/api/stubs.c000066400000000000000000000137711410622367000204130ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #include "tests.h" /* Stubs */ session_t session; int ServerUseReverseDNS = FALSE; server_rec *main_server = NULL; pid_t mpid = 1; unsigned char is_master = TRUE; volatile unsigned int recvd_signal_flags = 0; module *static_modules[] = { NULL }; module *loaded_modules = NULL; xaset_t *server_list = NULL; int prometheus_logfd = -1; module prometheus_module; pool *prometheus_pool = NULL; int tests_mkpath(pool *p, const char *path) { int res; mode_t perms; perms = 0770; res = mkdir(path, perms); fail_unless(res == 0, "Failed to create tmp directory '%s': %s", path, strerror(errno)); res = chmod(path, perms); fail_unless(res == 0, "Failed to set perms %04o on directory '%s': %s", perms, path, strerror(errno)); return 0; } int tests_rmpath(pool *p, const char *path) { DIR *dirh; struct dirent *dent; int res, xerrno = 0; if (path == NULL) { errno = EINVAL; return -1; } dirh = opendir(path); if (dirh == NULL) { xerrno = errno; /* Change the permissions in the directory, and try again. */ if (chmod(path, (mode_t) 0755) == 0) { dirh = opendir(path); } if (dirh == NULL) { pr_trace_msg("testsuite", 9, "error opening '%s': %s", path, strerror(xerrno)); errno = xerrno; return -1; } } while ((dent = readdir(dirh)) != NULL) { struct stat st; char *file; pr_signals_handle(); if (strncmp(dent->d_name, ".", 2) == 0 || strncmp(dent->d_name, "..", 3) == 0) { continue; } file = pdircat(p, path, dent->d_name, NULL); if (stat(file, &st) < 0) { pr_trace_msg("testsuite", 9, "unable to stat '%s': %s", file, strerror(errno)); continue; } if (S_ISDIR(st.st_mode)) { res = tests_rmpath(p, file); if (res < 0) { pr_trace_msg("testsuite", 9, "error removing directory '%s': %s", file, strerror(errno)); } } else { res = unlink(file); if (res < 0) { pr_trace_msg("testsuite", 9, "error removing file '%s': %s", file, strerror(errno)); } } } closedir(dirh); res = rmdir(path); if (res < 0) { xerrno = errno; pr_trace_msg("testsuite", 9, "error removing directory '%s': %s", path, strerror(xerrno)); errno = xerrno; } return res; } int pr_config_get_server_xfer_bufsz(int direction) { int bufsz = -1; switch (direction) { case PR_NETIO_IO_RD: bufsz = PR_TUNABLE_DEFAULT_RCVBUFSZ; break; case PR_NETIO_IO_WR: bufsz = PR_TUNABLE_DEFAULT_SNDBUFSZ; break; default: errno = EINVAL; return -1; } return bufsz; } void pr_log_auth(int priority, const char *fmt, ...) { if (getenv("TEST_VERBOSE") != NULL) { va_list msg; fprintf(stderr, "AUTH: "); va_start(msg, fmt); vfprintf(stderr, fmt, msg); va_end(msg); fprintf(stderr, "\n"); } } void pr_log_debug(int level, const char *fmt, ...) { if (getenv("TEST_VERBOSE") != NULL) { va_list msg; fprintf(stderr, "DEBUG%d: ", level); va_start(msg, fmt); vfprintf(stderr, fmt, msg); va_end(msg); fprintf(stderr, "\n"); } } int pr_log_event_generate(unsigned int log_type, int log_fd, int log_level, const char *log_msg, size_t log_msglen) { errno = ENOSYS; return -1; } int pr_log_event_listening(unsigned int log_type) { return FALSE; } int pr_log_openfile(const char *log_file, int *log_fd, mode_t log_mode) { int res; struct stat st; if (log_file == NULL || log_fd == NULL) { errno = EINVAL; return -1; } res = stat(log_file, &st); if (res < 0) { if (errno != ENOENT) { return -1; } } else { if (S_ISDIR(st.st_mode)) { errno = EISDIR; return -1; } } *log_fd = STDERR_FILENO; return 0; } void pr_log_pri(int prio, const char *fmt, ...) { if (getenv("TEST_VERBOSE") != NULL) { va_list msg; fprintf(stderr, "PRI%d: ", prio); va_start(msg, fmt); vfprintf(stderr, fmt, msg); va_end(msg); fprintf(stderr, "\n"); } } void pr_log_stacktrace(int fd, const char *name) { } int pr_log_writefile(int fd, const char *name, const char *fmt, ...) { if (getenv("TEST_VERBOSE") != NULL) { va_list msg; fprintf(stderr, "%s: ", name); va_start(msg, fmt); vfprintf(stderr, fmt, msg); va_end(msg); fprintf(stderr, "\n"); } return 0; } void pr_session_disconnect(module *m, int reason_code, const char *details) { } const char *pr_session_get_protocol(int flags) { return "ftp"; } void pr_signals_handle(void) { } /* Module-specific stubs */ module prometheus_module = { /* Always NULL */ NULL, NULL, /* Module API version */ 0x20, /* Module name */ "prometheus", /* Module configuration handler table */ NULL, /* Module command handler table */ NULL, /* Module authentication handler table */ NULL, /* Module initialization */ NULL, /* Session initialization */ NULL, /* Module version */ MOD_PROMETHEUS_VERSION }; proftpd-mod_prometheus-0.2/t/api/tests.c000066400000000000000000000071721410622367000204130ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ #include "tests.h" struct testsuite_info { const char *name; Suite *(*get_suite)(void); }; static struct testsuite_info suites[] = { { "db", tests_get_db_suite }, { "http", tests_get_http_suite }, { "text", tests_get_text_suite }, { "metric", tests_get_metric_suite }, { "metric.db", tests_get_metric_db_suite }, { "registry", tests_get_registry_suite }, { NULL, NULL } }; static Suite *tests_get_suite(const char *suite) { register unsigned int i; for (i = 0; suites[i].name != NULL; i++) { if (strcmp(suite, suites[i].name) == 0) { return (*suites[i].get_suite)(); } } errno = ENOENT; return NULL; } int main(int argc, char *argv[]) { const char *log_file = "api-tests.log"; int nfailed = 0; SRunner *runner = NULL; char *requested = NULL; runner = srunner_create(NULL); /* XXX This log name should be set outside this code, e.g. via environment * variable or command-line option. */ srunner_set_log(runner, log_file); requested = getenv("PROMETHEUS_TEST_SUITE"); if (requested != NULL) { Suite *suite; suite = tests_get_suite(requested); if (suite != NULL) { srunner_add_suite(runner, suite); } else { fprintf(stderr, "No such test suite ('%s') requested via PROMETHEUS_TEST_SUITE\n", requested); return EXIT_FAILURE; } } else { register unsigned int i; for (i = 0; suites[i].name; i++) { Suite *suite; suite = (suites[i].get_suite)(); if (suite != NULL) { srunner_add_suite(runner, suite); } } } /* Configure the Trace API to write to stderr. */ pr_trace_use_stderr(TRUE); requested = getenv("PROMETHEUS_TEST_NOFORK"); if (requested != NULL) { srunner_set_fork_status(runner, CK_NOFORK); } else { requested = getenv("CK_DEFAULT_TIMEOUT"); if (requested == NULL) { setenv("CK_DEFAULT_TIMEOUT", "60", 1); } } srunner_run_all(runner, CK_NORMAL); nfailed = srunner_ntests_failed(runner); if (runner != NULL) { srunner_free(runner); } if (nfailed != 0) { fprintf(stderr, "-------------------------------------------------\n"); fprintf(stderr, " FAILED %d %s\n\n", nfailed, nfailed != 1 ? "tests" : "test"); fprintf(stderr, " Please send email to:\n\n"); fprintf(stderr, " tj@castaglia.org\n\n"); fprintf(stderr, " containing the `%s' file (in the t/ directory)\n", log_file); fprintf(stderr, " and the output from running `proftpd -V'\n"); fprintf(stderr, "-------------------------------------------------\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } proftpd-mod_prometheus-0.2/t/api/tests.h000066400000000000000000000033521410622367000204140ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ /* Testsuite management */ #ifndef MOD_PROMETHEUS_TESTS_H #define MOD_PROMETHEUS_TESTS_H #include "mod_prometheus.h" #ifdef HAVE_CHECK_H # include #else # error "Missing Check installation; necessary for ProFTPD testsuite" #endif int tests_mkpath(pool *p, const char *path); int tests_rmpath(pool *p, const char *path); Suite *tests_get_db_suite(void); Suite *tests_get_http_suite(void); Suite *tests_get_metric_suite(void); Suite *tests_get_metric_db_suite(void); Suite *tests_get_registry_suite(void); Suite *tests_get_text_suite(void); extern volatile unsigned int recvd_signal_flags; extern pid_t mpid; extern server_rec *main_server; #endif /* MOD_PROMETHEUS_TESTS_H */ proftpd-mod_prometheus-0.2/t/api/text.c000066400000000000000000000163541410622367000202370ustar00rootroot00000000000000/* * ProFTPD - mod_prometheus API testsuite * Copyright (c) 2021 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. */ /* Text API tests. */ #include "tests.h" #include "prometheus/text.h" static pool *p = NULL; static void set_up(void) { if (p == NULL) { p = permanent_pool = make_sub_pool(NULL); } if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.text", 1, 20); } mark_point(); } static void tear_down(void) { if (getenv("TEST_VERBOSE") != NULL) { pr_trace_set_levels("prometheus.text", 0, 0); } if (p != NULL) { destroy_pool(p); p = permanent_pool = NULL; } } START_TEST (text_destroy_test) { int res; mark_point(); res = prom_text_destroy(NULL); fail_unless(res < 0, "Failed to handle null text"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); } END_TEST START_TEST (text_create_test) { int res; struct prom_text *text; mark_point(); text = prom_text_create(NULL); fail_unless(text == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); text = prom_text_create(p); fail_unless(text != NULL, "Failed to create text: %s", strerror(errno)); res = prom_text_destroy(text); fail_unless(res == 0, "Failed to destroy text: %s", strerror(errno)); } END_TEST START_TEST (text_get_str_test) { char *res; struct prom_text *text; mark_point(); res = prom_text_get_str(NULL, NULL, NULL); fail_unless(res == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_text_get_str(p, NULL, NULL); fail_unless(res == NULL, "Failed to handle null text"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); text = prom_text_create(p); res = prom_text_get_str(p, text, NULL); fail_unless(res == NULL, "Failed to handle absent text"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, strerror(errno), errno); prom_text_destroy(text); } END_TEST START_TEST (text_add_byte_test) { int res; char *str; size_t sz; struct prom_text *text; mark_point(); res = prom_text_add_byte(NULL, '"'); fail_unless(res < 0, "Failed to handle null text"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); text = prom_text_create(p); res = prom_text_add_byte(text, '{'); fail_unless(res == 0, "Failed to add byte: %s", strerror(errno)); str = prom_text_get_str(p, text, &sz); fail_unless(str != NULL, "Failed get text: %s", strerror(errno)); fail_unless(sz == 1, "Expected size 1, got %lu", (unsigned long) sz); fail_unless(strcmp(str, "{") == 0, "Expected '{', got '%s'", str); prom_text_destroy(text); } END_TEST START_TEST (text_add_str_test) { int res; char *str, *input; size_t sz; struct prom_text *text; mark_point(); res = prom_text_add_str(NULL, NULL, 0); fail_unless(res < 0, "Failed to handle null text"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); text = prom_text_create(p); res = prom_text_add_str(text, NULL, 0); fail_unless(res < 0, "Failed to handle null str"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); input = "foobar"; res = prom_text_add_str(text, input, 0); fail_unless(res == 0, "Failed to handle zero-length text: %s", strerror(errno)); str = prom_text_get_str(p, text, NULL); fail_unless(str == NULL, "Failed to handle absent text"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, strerror(errno), errno); mark_point(); res = prom_text_add_str(text, input, strlen(input)); fail_unless(res == 0, "Failed to handle text: %s", strerror(errno)); str = prom_text_get_str(p, text, &sz); fail_unless(str != NULL, "Failed get text: %s", strerror(errno)); fail_unless(sz == 6, "Expected size 7, got %lu", (unsigned long) sz); fail_unless(strcmp(str, input) == 0, "Expected '%s', got '%s'", input, str); prom_text_destroy(text); } END_TEST START_TEST (text_from_labels_test) { const char *res, *expected; struct prom_text *text; pr_table_t *labels; mark_point(); res = prom_text_from_labels(NULL, NULL, NULL); fail_unless(res == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); res = prom_text_from_labels(p, NULL, NULL); fail_unless(res == NULL, "Failed to handle null text"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); text = prom_text_create(p); res = prom_text_from_labels(p, text, NULL); fail_unless(res != NULL, "Failed to handle null labels: %s", strerror(errno)); fail_unless(strcmp(res, "") == 0, "Expected '', got '%s'", res); /* Now, with labels. */ mark_point(); labels = pr_table_nalloc(p, 0, 2); res = prom_text_from_labels(p, text, labels); fail_unless(res != NULL, "Failed to handle empty labels: %s", strerror(errno)); fail_unless(strcmp(res, "") == 0, "Expected '', got '%s'", res); mark_point(); (void) pr_table_add_dup(labels, "protocol", "ftp", 0); (void) pr_table_add_dup(labels, "foo", "BAR", 0); res = prom_text_from_labels(p, text, labels); fail_unless(res != NULL, "Failed to handle labels: %s", strerror(errno)); expected = "{foo=\"BAR\",protocol=\"ftp\"}"; fail_unless(strcmp(res, expected) == 0, "Expected '%s', got '%s'", expected, res); prom_text_destroy(text); } END_TEST Suite *tests_get_text_suite(void) { Suite *suite; TCase *testcase; suite = suite_create("text"); testcase = tcase_create("base"); tcase_add_checked_fixture(testcase, set_up, tear_down); tcase_add_test(testcase, text_destroy_test); tcase_add_test(testcase, text_create_test); tcase_add_test(testcase, text_get_str_test); tcase_add_test(testcase, text_add_byte_test); tcase_add_test(testcase, text_add_str_test); tcase_add_test(testcase, text_from_labels_test); suite_add_tcase(suite, testcase); return suite; } proftpd-mod_prometheus-0.2/t/lib/000077500000000000000000000000001410622367000170735ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/lib/ProFTPD/000077500000000000000000000000001410622367000203115ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/lib/ProFTPD/Tests/000077500000000000000000000000001410622367000214135ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/lib/ProFTPD/Tests/Modules/000077500000000000000000000000001410622367000230235ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/lib/ProFTPD/Tests/Modules/mod_prometheus.pm000066400000000000000000005232031410622367000264200ustar00rootroot00000000000000package ProFTPD::Tests::Modules::mod_prometheus; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use Data::Dumper; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::Utils qw(:auth :config :features :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { prom_start_existing_dirs => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_unacceptable_method => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_bad_uri => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_base_uri => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metrics_uri => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metrics_uri_with_gzip => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metrics_uri_with_basic_auth_success => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metrics_uri_with_basic_auth_missing_credentials => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metrics_uri_with_basic_auth_wrong_username => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metrics_uri_with_basic_auth_wrong_password => { order => ++$order, test_class => [qw(forking prometheus)], }, # Basic metrics prom_scrape_metric_build_info => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_startup_time => { order => ++$order, test_class => [qw(forking prometheus)], }, # Server metrics prom_scrape_metric_connection => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_connection_refused => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_log_message => { order => ++$order, test_class => [qw(forking prometheus)], }, # Session metrics prom_scrape_metric_auth_ok => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_auth_anon_ok => { order => ++$order, test_class => [qw(forking prometheus rootprivs)], }, prom_scrape_metric_auth_error_unknown_user => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_auth_error_bad_password => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_auth_error_incomplete => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_directory_list => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_directory_list_error => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_file_download => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_file_download_error => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_file_upload => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_file_upload_error => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_succeeded => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_multiple_times => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_user_quit => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_user_multiple_times => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_pass_multiple_times => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_in_progress => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_error_bad_user => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_error_bad_password => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_error_user_bad_pass_good_pass => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_error_pass_multiple_times => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_login_error_denied_acl => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_timeout_idle => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_timeout_login => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_timeout_notransfer => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_timeout_session => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_scrape_metric_timeout_stalled => { order => ++$order, test_class => [qw(forking prometheus)], }, prom_config_exporter_addr => { order => ++$order, test_class => [qw(forking prometheus)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { # Check for the required Perl modules: # # LWP-UserAgent my $required = [qw( LWP::UserAgent )]; foreach my $req (@$required) { eval "use $req"; if ($@) { print STDERR "\nWARNING:\n + Module '$req' not found, skipping all tests\n "; if ($ENV{TEST_VERBOSE}) { print STDERR "Unable to load $req: $@\n"; } return qw(testsuite_empty_test); } } return testsuite_get_runnable_tests($TESTS); } # Support routines sub saw_expected_content { my $lines = shift; my $expected = shift; my $seen = 0; foreach my $line (@$lines) { if ($line =~ /$expected/) { $seen = 1; last; } } return $seen; } # Test cases sub prom_start_existing_dirs { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.http:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); my $ex; # First, start the server server_start($setup->{config_file}); # ...then stop the server. This means mod_prometheus will have created all # the necessary directories, etc. sleep(2); server_stop($setup->{pid_file}); # Now start the server again. Time time, mod_prometheus will double-check # permissions et al on the already-existing mod_prometheus directories that it # created the first time. sleep(2); server_start($setup->{config_file}); # Stop server sleep(2); eval { server_stop($setup->{pid_file}) }; if ($@) { $ex = $@; } test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_unacceptable_method { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.http:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/foo/bar/baz"; my $resp = $ua->delete($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; } my $expected = 405; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'Method Not Allowed'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_bad_uri { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.http:20 prometheus.http.clf:10', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/foo/bar/baz"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; } my $expected = 400; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'Bad Request'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_base_uri { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.http:20 prometheus.http.clf:10', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metrics_uri { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metrics_uri_with_gzip { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); $ua->default_header('Accept-Encoding' => 'deflate,gzip,foobar'); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->decoded_content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); my $content_encoding = $headers->header('Content-Encoding'); $expected = 'gzip'; $self->assert($expected eq $content_encoding, test_msg("Expected Content-Encoding '$expected', got '$content_encoding'")); my $content = $resp->decoded_content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_build_info .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metrics_uri_with_basic_auth_success { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $exporter_realm = 'proftpd'; my $exporter_username = 'prometheus'; my $exporter_password = 'Pr0m3th3u$'; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port $exporter_username $exporter_password", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); $ua->credentials("127.0.0.1:$exporter_port", $exporter_realm, $exporter_username, $exporter_password); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metrics_uri_with_basic_auth_from_env { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $exporter_realm = 'proftpd'; my $exporter_username = 'prometheus'; my $exporter_password = 'Pr0m3th3u$'; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; $ENV{PROMETHEUS_USERNAME} = $exporter_username; $ENV{PROMETHEUS_PASSWORD} = $exporter_password; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); $ua->credentials("127.0.0.1:$exporter_port", $exporter_realm, $exporter_username, $exporter_password); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metrics_uri_with_basic_auth_missing_credentials { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $exporter_realm = 'proftpd'; my $exporter_username = 'prometheus'; my $exporter_password = 'Pr0m3th3u$'; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port $exporter_username $exporter_password", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 401; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'Unauthorized'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metrics_uri_with_basic_auth_wrong_username { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $exporter_realm = 'proftpd'; my $exporter_username = 'prometheus'; my $exporter_password = 'Pr0m3th3u$'; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port $exporter_username $exporter_password", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); $ua->credentials("127.0.0.1:$exporter_port", $exporter_realm, 'foo', 'bar'); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 401; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'Unauthorized'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metrics_uri_with_basic_auth_wrong_password { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $exporter_realm = 'proftpd'; my $exporter_username = 'prometheus'; my $exporter_password = 'Pr0m3th3u$'; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port $exporter_username $exporter_password", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); $ua->credentials("127.0.0.1:$exporter_port", $exporter_realm, $exporter_username, 'bar'); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 401; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'Unauthorized'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_build_info { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_build_info .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_build_info counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_build_info\{mod_prometheus_version="\S+",proftpd_version="\S+"\} 1$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_startup_time { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_startup_time .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_startup_time counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_startup_time \d+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_connection { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:30 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); sleep(2); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_connection_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_connection_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_connection_total\{protocol="ftp"\} 1$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_connection_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_connection_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Race: sometimes the session has not yet finished. $expected = '^proftpd_connection_count\{protocol="ftp"\} (0|1)$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Histogram $expected = '^# HELP proftpd_connection_duration_seconds .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_connection_duration_seconds histogram$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_connection_duration_seconds_bucket\{le="\+Inf",protocol="ftp"\} \d+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_connection_duration_seconds_count\{protocol="ftp"\} 1$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_connection_duration_seconds_sum\{protocol="ftp"\} \d+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_connection_refused { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, Limit => { LOGIN => { DenyAll => '', }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); eval { ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 1, 1) }; unless ($@) { die("Connection succeeded unexpectedly"); } my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_connection_refused_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_connection_refused_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_connection_refused_total\{protocol="ftp"\} \d+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_log_message { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'event:20 prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_log_message_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_log_message_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_log_message_total\{level="debug",protocol="ftp"\} \d+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_log_message_total\{level="debug",protocol="ftp"\} \d+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_log_message_total\{level="info",protocol="ftp"\} \d+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_auth_ok { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'auth:20 prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_auth_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_auth_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_auth_total\{method="password",protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_auth_anon_ok { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $anon_dir = File::Spec->rel2abs($tmpdir); my ($config_user, $config_group) = config_get_identity(); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'auth:20 prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, Anonymous => { $anon_dir => { User => $config_user, Group => $config_group, UserAlias => "anonymous $config_user", RequireValidShell => 'off', }, }, }; my $port; ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login('anonymous', 'nospam@ftp.org'); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_auth_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_auth_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_auth_total\{method="anonymous",protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_auth_error_unknown_user { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'auth:20 prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); eval { $client->login('foo', 'bar') }; unless ($@) { die("Login succeeded unexpectedly"); } $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_auth_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_auth_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_auth_error_total\{protocol="ftp",reason="unknown user"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_auth_error_bad_password { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'auth:20 prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); eval { $client->login($setup->{user}, 'bar') }; unless ($@) { die("Login succeeded unexpectedly"); } $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_auth_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_auth_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_auth_error_total\{protocol="ftp",reason="bad password"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_auth_error_incomplete { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'auth:20 prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->user('foo'); $client->quit(); # Allow time for the session to end before scraping. sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_auth_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_auth_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_auth_error_total\{protocol="ftp",reason="incomplete"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_directory_list { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); # LIST my $conn = $client->list_raw(); unless ($conn) { die("LIST failed: " . $client->response_code() . " " . $client->response_msg()); } my $buf; $conn->read($buf, 8192, 30); eval { $conn->close() }; # NLST $buf = ''; $conn = $client->nlst_raw(); unless ($conn) { die("NLST failed: " . $client->response_code() . " " . $client->response_msg()); } $conn->read($buf, 8192, 30); eval { $conn->close() }; # MLSD $buf = ''; $conn = $client->mlsd_raw(); unless ($conn) { die("MLSD failed: " . $client->response_code() . " " . $client->response_msg()); } $conn->read($buf, 8192, 30); eval { $conn->close() }; $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_directory_list_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_directory_list_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_directory_list_total\{protocol="ftp"\} 3+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_directory_list_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_directory_list_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_directory_list_count\{protocol="ftp"\} 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_directory_list_error { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); # LIST always succeeds, unfortunately. # NLST my $conn = $client->nlst_raw('/quxx/quzz'); if ($conn) { die("NLST succeeded unexpectedly"); } # MLSD $conn = $client->mlsd_raw('/alef/bet/vet'); if ($conn) { die("MLSD succeeded unexpectedly"); } $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_directory_list_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_directory_list_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_directory_list_error_total\{protocol="ftp"\} 2+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_file_download { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $test_file = File::Spec->rel2abs("$tmpdir/test.dat"); if (open(my $fh, "> $test_file")) { print $fh "AbCd" x 8192; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); my ($resp_code, $resp_msg) = $client->retr($test_file); $self->assert_transfer_ok($resp_code, $resp_msg); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_file_download_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_file_download_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_download_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_file_download_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_file_download_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_download_count\{protocol="ftp"\} 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Histogram $expected = '^# HELP proftpd_file_download_bytes .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_file_download_bytes histogram$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_download_bytes_bucket\{le="\+Inf",protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_download_bytes_count\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_download_bytes_sum\{protocol="ftp"\} 32768+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_file_download_error { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $test_file = File::Spec->rel2abs("$tmpdir/test.dat"); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); eval { $client->retr($test_file) }; unless ($@) { die("RETR $test_file succeeded unexpectedly"); } $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_file_download_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_file_download_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_download_error_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_file_upload { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $test_file = File::Spec->rel2abs("$tmpdir/test.dat"); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); my $conn = $client->stor_raw($test_file); unless ($conn) { die("Failed to STOR: " . $client->response_code() . " " . $client->response_msg()); } my $buf = "Hello World!\n"; $conn->write($buf, length($buf), 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); $self->assert_transfer_ok($resp_code, $resp_msg); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_file_upload_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_file_upload_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_upload_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_file_upload_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_file_upload_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_upload_count\{protocol="ftp"\} 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Histogram $expected = '^# HELP proftpd_file_upload_bytes .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_file_upload_bytes histogram$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_upload_bytes_bucket\{le="\+Inf",protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_upload_bytes_count\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_upload_bytes_sum\{protocol="ftp"\} 13+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_file_upload_error { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $test_file = File::Spec->rel2abs("$tmpdir/sub.d/test.dat"); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); my $conn = $client->stor_raw($test_file); if ($conn) { die("STOR $test_file succeeded unexpectedly"); } $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_file_upload_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_file_upload_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_file_upload_error_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_succeeded { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_login_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_login_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_count\{protocol="ftp"\} 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Histogram $expected = '^# HELP proftpd_login_delay_seconds .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_delay_seconds histogram$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_delay_seconds_bucket\{le="\+Inf",protocol="ftp"\} 1$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_delay_seconds_count\{protocol="ftp"\} 1$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_delay_seconds_sum\{protocol="ftp"\} \d+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_multiple_times { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); eval { $client->login($setup->{user}, $setup->{passwd}) }; eval { $client->login($setup->{user}, $setup->{passwd}) }; $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_login_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_login_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_count\{protocol="ftp"\} 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_user_quit { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->user($setup->{user}); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_login_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_total 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_login_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Race: sometimes the session has not yet finished. $expected = '^proftpd_login_count\{protocol="ftp"\} (0|1)$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_user_multiple_times { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->user($setup->{user}); eval { $client->user($setup->{user}) }; eval { $client->user($setup->{user}) }; $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_login_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_total 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_login_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_count\{protocol="ftp"\} 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_pass_multiple_times { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); eval { $client->pass('foo') }; eval { $client->pass('foo') }; eval { $client->pass('foo') }; $client->user($setup->{user}); eval { $client->pass('foo') }; eval { $client->pass('foo') }; $client->user($setup->{user}); $client->pass($setup->{passwd}); eval { $client->pass('foo') }; $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_login_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_login_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_count\{protocol="ftp"\} 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_in_progress { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->user($setup->{user}); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; # Counter $expected = '^# HELP proftpd_login_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_total 0+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); # Gauge $expected = '^# HELP proftpd_login_count .*?\.$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_count gauge$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_count\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_error_bad_user { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); eval { $client->login('foo', 'bar') }; $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_login_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_error_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_error_bad_password { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); eval { $client->login($setup->{user}, 'bar') }; $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_login_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_error_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_error_user_bad_pass_good_pass { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->user($setup->{user}); eval { $client->pass('foo') }; $client->user($setup->{user}); $client->pass($setup->{passwd}); $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_login_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_error_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_error_pass_multiple_times { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); eval { $client->pass('foo') }; eval { $client->pass('foo') }; eval { $client->pass('foo') }; $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_login_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_error_total\{protocol="ftp"\} 3+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_login_error_denied_acl { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, Limit => { LOGIN => { DenyUser => $setup->{user}, }, } }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); eval { $client->login($setup->{user}, $setup->{passwd}) }; unless ($@) { die("Login succeeded unexpectedly"); } $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_login_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_login_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_login_error_total\{protocol="ftp"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_timeout_idle { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $timeout_idle = 3; my $timeout_delay = $timeout_idle + 2; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, TimeoutIdle => $timeout_idle, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); $client->noop(); # Wait for more than the TimeoutIdle period sleep($timeout_delay); eval { $client->noop() }; unless ($@) { die("NOOP succeeded unexpectedly"); } my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_timeout_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_timeout_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_timeout_total\{protocol="ftp",reason="TimeoutIdle"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_timeout_login { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $timeout_login = 3; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, TimeoutLogin => $timeout_login, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); # Wait for 2s more than the TimeoutLogin period sleep($timeout_login + 2); eval { $client->user($setup->{user}) }; unless ($@) { die("USER succeeded unexpectedly"); } my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_timeout_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_timeout_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_timeout_total\{protocol="ftp",reason="TimeoutLogin"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_timeout_notransfer { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $timeout_notransfer = 2; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, TimeoutNoTransfer => $timeout_notransfer, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); # Wait for 2s more than the TimeoutNoTransfer period for (my $i = 0; $i < $timeout_notransfer; $i++) { sleep(1); eval { $client->noop() }; } sleep(2); eval { $client->noop() }; unless ($@) { die("NOOP succeeded unexpectedly"); } my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_timeout_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_timeout_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_timeout_total\{protocol="ftp",reason="TimeoutNoTransfer"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_timeout_session { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $timeout_session = 3; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, TimeoutSession => $timeout_session, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); # Wait for 2s more than the TimeoutSession period for (my $i = 0; $i < $timeout_session; $i++) { sleep(1); eval { $client->noop() }; } sleep(2); eval { $client->noop() }; unless ($@) { die("NOOP succeeded unexpectedly"); } my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_timeout_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_timeout_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_timeout_total\{protocol="ftp",reason="TimeoutSession"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_timeout_stalled { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $test_file = File::Spec->rel2abs("$tmpdir/test.dat"); if (open(my $fh, "> $test_file")) { print $fh "AbCd" x 8192000; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $timeout_stalled = 2; my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 prometheus.http.clf:10 prometheus.metric:20 prometheus.metric.db:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, TimeoutStalled => $timeout_stalled, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($setup->{user}, $setup->{passwd}); my $conn = $client->retr_raw($test_file); unless ($conn) { die("RETR failed: " . $client->response_code() . " " . $client->response_msg()); } # Wait for 2s more than the stalled period sleep($timeout_stalled + 2); my $buf = ''; $conn->read($buf, 8192, 30); eval { $conn->close() }; eval { $client->noop() }; unless ($@) { die("NOOP succeeded unexpectedly"); } my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_timeout_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_timeout_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_timeout_total\{protocol="ftp",reason="TimeoutStalled"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_config_exporter_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); if ($ENV{TEST_VERBOSE}) { print STDERR "# Using export port = $exporter_port\n"; } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.http:20 prometheus.http.clf:10', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "0.0.0.0:$exporter_port", }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require LWP::UserAgent; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Allow server to start up sleep(2); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $headers = $resp->headers; my $content_type = $headers->header('Content-Type'); $expected = 'text/plain'; $self->assert($expected eq $content_type, test_msg("Expected Content-Type '$expected', got '$content_type'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } 1; proftpd-mod_prometheus-0.2/t/lib/ProFTPD/Tests/Modules/mod_prometheus/000077500000000000000000000000001410622367000260555ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/lib/ProFTPD/Tests/Modules/mod_prometheus/sftp.pm000066400000000000000000000236711410622367000274000ustar00rootroot00000000000000package ProFTPD::Tests::Modules::mod_prometheus::sftp; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use Data::Dumper; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::Utils qw(:auth :config :features :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { prom_scrape_metric_handshake_error_ssh2 => { order => ++$order, test_class => [qw(forking mod_sftp prometheus)], }, prom_scrape_metric_sftp_protocol => { order => ++$order, test_class => [qw(forking mod_sftp prometheus)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { # Check for the required Perl modules: # # LWP-UserAgent # Net-SSH2 # Net-SSH2-SFTP my $required = [qw( LWP::UserAgent Net::SSH2 Net::SSH2::SFTP )]; foreach my $req (@$required) { eval "use $req"; if ($@) { print STDERR "\nWARNING:\n + Module '$req' not found, skipping all tests\n "; if ($ENV{TEST_VERBOSE}) { print STDERR "Unable to load $req: $@\n"; } return qw(testsuite_empty_test); } } return testsuite_get_runnable_tests($TESTS); } sub set_up { my $self = shift; $self->SUPER::set_up(@_); # Make sure that mod_sftp does not complain about permissions on the hostkey # files. my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_sftp/ssh_host_dsa_key"); unless (chmod(0400, $rsa_host_key, $dsa_host_key)) { die("Can't set perms on $rsa_host_key, $dsa_host_key: $!"); } } # Support routines sub saw_expected_content { my $lines = shift; my $expected = shift; my $seen = 0; foreach my $line (@$lines) { if ($line =~ /$expected/) { $seen = 1; last; } } return $seen; } # Test cases sub prom_scrape_metric_handshake_error_ssh2 { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 sftp:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $setup->{log_file}", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); my $cipher = 'arcfour'; $ssh2->method('crypt_cs', $cipher); sleep(2); if ($ssh2->connect('127.0.0.1', $port)) { die("Connected to SSH server unexpectedly"); } my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_handshake_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_handshake_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_handshake_error_total\{protocol="ssh2"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_sftp_protocol { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:20 prometheus.http:20 sftp:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $setup->{log_file}", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(2); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($setup->{user}, $setup->{passwd})) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } $sftp = undef; my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_sftp_protocol_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_sftp_protocol_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_sftp_protocol_total\{protocol="sftp",version="3"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } 1; proftpd-mod_prometheus-0.2/t/lib/ProFTPD/Tests/Modules/mod_prometheus/tls.pm000066400000000000000000000355221410622367000272240ustar00rootroot00000000000000package ProFTPD::Tests::Modules::mod_prometheus::tls; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use Data::Dumper; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::Utils qw(:auth :config :features :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { prom_scrape_metric_handshake_error_tls_ctrl => { order => ++$order, test_class => [qw(forking mod_tls prometheus)], }, prom_scrape_metric_handshake_error_tls_data => { order => ++$order, test_class => [qw(forking mod_tls prometheus)], }, prom_scrape_metric_tls_protocol => { order => ++$order, test_class => [qw(forking mod_tls prometheus)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { # Check for the required Perl modules: # # LWP-UserAgent # Net-FTPSSL my $required = [qw( LWP::UserAgent Net::FTPSSL )]; foreach my $req (@$required) { eval "use $req"; if ($@) { print STDERR "\nWARNING:\n + Module '$req' not found, skipping all tests\n "; if ($ENV{TEST_VERBOSE}) { print STDERR "Unable to load $req: $@\n"; } return qw(testsuite_empty_test); } } return testsuite_get_runnable_tests($TESTS); } # Support routines sub saw_expected_content { my $lines = shift; my $expected = shift; my $seen = 0; foreach my $line (@$lines) { if ($line =~ /$expected/) { $seen = 1; last; } } return $seen; } # Test cases sub prom_scrape_metric_handshake_error_tls_ctrl { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); my $cert_file = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_tls/server-cert.pem"); my $ca_file = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_tls/ca-cert.pem"); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:30 prometheus.http:20 tls:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, 'mod_tls.c' => { TLSEngine => 'on', TLSLog => $setup->{log_file}, TLSRequired => 'on', TLSProtocol => 'TLSv1.2', TLSRSACertificateFile => $cert_file, TLSCACertificateFile => $ca_file, }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::FTPSSL; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Give the server a chance to start up sleep(2); my $ssl_opts = { Encryption => 'E', Port => $port, SSL_version => 'TLSv1', }; if ($ENV{TEST_VERBOSE}) { $ssl_opts->{Debug} = 1; } my $client = Net::FTPSSL->new('127.0.0.1', %$ssl_opts); if ($client) { die("TLS ctrl handshake succeeded unexpectedly"); } my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_handshake_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_handshake_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_handshake_error_total\{connection="ctrl",protocol="ftps"\} 1$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_handshake_error_tls_data { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); my $cert_file = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_tls/server-cert.pem"); my $ca_file = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_tls/ca-cert.pem"); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'event:20 prometheus:20 prometheus.db:30 prometheus.http:20 tls:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, TimeoutLinger => 1, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, 'mod_tls.c' => { TLSEngine => 'on', TLSLog => $setup->{log_file}, TLSRequired => 'on', TLSRSACertificateFile => $cert_file, TLSCACertificateFile => $ca_file, TLSOptions => 'EnableDiags NoSessionReuseRequired', }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::FTPSSL; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Give the server a chance to start up sleep(2); my $ssl_opts = { Encryption => 'E', Port => $port, SSL_version => 'TLSv1', }; if ($ENV{TEST_VERBOSE}) { $ssl_opts->{Debug} = 2; } my $client = Net::FTPSSL->new('127.0.0.1', %$ssl_opts); unless ($client) { die("Can't connect to FTPS server: " . IO::Socket::SSL::errstr()); } my $data_opts = { SSL_cipher_list => 'MD5', # Explicitly disable reuse of the control session; necessary for # our other options to be honored. SSL_reuse_ctx => undef, }; # Note: If we need to use different tricks here, depending on the # OpenSSL version used by Net::SSLeay (because of _e.g._ dropping of # SSLv3 support in OpenSSL 1.1.1x), we can use: # # perl -mNet::SSLeay -e 'print Net::SSLeay::OpenSSL_version('OPENSSL_VERSION'), "\n";' # OpenSSL 1.1.1 11 Sep 2018 # # For now, we assume that the MD5 ciphersuites are sufficiently # disabled on a widespread enough basis to suffice for our needs. unless ($client->set_dc_from_hash($data_opts) == 2) { die("Can't set Net-FTPSSL data conn TLS cipher list"); } unless ($client->login($setup->{user}, $setup->{passwd})) { die("Can't login: " . $client->last_message()); } my $res = $client->list('.'); if ($res) { die("LIST succeeded unexpectedly"); } eval { $client->quit() }; my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_handshake_error_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_handshake_error_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_handshake_error_total\{connection="data",protocol="ftps"\} 1$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } sub prom_scrape_metric_tls_protocol { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'prometheus'); my $table_dir = File::Spec->rel2abs("$tmpdir/var/prometheus"); my $exporter_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); my $cert_file = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_tls/server-cert.pem"); my $ca_file = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/tests/t/etc/modules/mod_tls/ca-cert.pem"); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, TraceLog => $setup->{log_file}, Trace => 'prometheus:20 prometheus.db:30 prometheus.http:20 tls:20', AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_prometheus.c' => { PrometheusEngine => 'on', PrometheusLog => $setup->{log_file}, PrometheusTables => $table_dir, PrometheusExporter => "127.0.0.1:$exporter_port", }, 'mod_tls.c' => { TLSEngine => 'on', TLSLog => $setup->{log_file}, TLSRequired => 'on', TLSRSACertificateFile => $cert_file, TLSCACertificateFile => $ca_file, }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::FTPSSL; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Give the server a chance to start up sleep(2); my $ssl_opts = { Encryption => 'E', Port => $port, }; if ($ENV{TEST_VERBOSE}) { $ssl_opts->{Debug} = 1; } my $client = Net::FTPSSL->new('127.0.0.1', %$ssl_opts); unless ($client) { die("Can't connect to FTPS server: " . IO::Socket::SSL::errstr()); } unless ($client->login($setup->{user}, $setup->{passwd})) { die("Can't login: " . $client->last_message()); } $client->quit(); my $ua = LWP::UserAgent->new(); $ua->timeout(3); my $url = "http://127.0.0.1:$exporter_port/metrics"; my $resp = $ua->get($url); if ($ENV{TEST_VERBOSE}) { print STDERR "# response: ", $resp->status_line, "\n"; print STDERR "# ", $resp->content, "\n"; } my $expected = 200; my $resp_code = $resp->code; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $expected = 'OK'; my $resp_msg = $resp->message; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); my $content = $resp->content; my $lines = [split(/\n/, $content)]; $expected = '^# HELP proftpd_tls_protocol_total .*?\.$'; my $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^# TYPE proftpd_tls_protocol_total counter$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); $expected = '^proftpd_tls_protocol_total\{protocol="ftps",version="TLSv.*?"\} 1+$'; $seen = saw_expected_content($lines, $expected); $self->assert($seen, test_msg("Did not see '$expected' in '$content' as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } 1; proftpd-mod_prometheus-0.2/t/modules/000077500000000000000000000000001410622367000177755ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/modules/mod_prometheus.t000066400000000000000000000002721410622367000232150ustar00rootroot00000000000000#!/usr/bin/env perl use lib qw(t/lib); use strict; use Test::Unit::HarnessUnit; $| = 1; my $r = Test::Unit::HarnessUnit->new(); $r->start("ProFTPD::Tests::Modules::mod_prometheus"); proftpd-mod_prometheus-0.2/t/modules/mod_prometheus/000077500000000000000000000000001410622367000230275ustar00rootroot00000000000000proftpd-mod_prometheus-0.2/t/modules/mod_prometheus/sftp.t000066400000000000000000000003001410622367000241610ustar00rootroot00000000000000#!/usr/bin/env perl use lib qw(t/lib); use strict; use Test::Unit::HarnessUnit; $| = 1; my $r = Test::Unit::HarnessUnit->new(); $r->start("ProFTPD::Tests::Modules::mod_prometheus::sftp"); proftpd-mod_prometheus-0.2/t/modules/mod_prometheus/tls.t000066400000000000000000000002771410622367000240240ustar00rootroot00000000000000#!/usr/bin/env perl use lib qw(t/lib); use strict; use Test::Unit::HarnessUnit; $| = 1; my $r = Test::Unit::HarnessUnit->new(); $r->start("ProFTPD::Tests::Modules::mod_prometheus::tls"); proftpd-mod_prometheus-0.2/tests.pl000066400000000000000000000053121410622367000175620ustar00rootroot00000000000000#!/usr/bin/env perl use strict; use Cwd qw(abs_path); use File::Spec; use Getopt::Long; use Test::Harness qw(&runtests $verbose); my $opts = {}; GetOptions($opts, 'h|help', 'C|class=s@', 'K|keep-tmpfiles', 'F|file-pattern=s', 'V|verbose'); if ($opts->{h}) { usage(); } if ($opts->{K}) { $ENV{KEEP_TMPFILES} = 1; } $verbose = 1; if ($opts->{V}) { $ENV{TEST_VERBOSE} = 1; } # We use this, rather than use(), since use() is equivalent to a BEGIN # block, and we want the module to be loaded at run-time. if ($ENV{PROFTPD_TEST_DIR}) { push(@INC, "$ENV{PROFTPD_TEST_DIR}/tests/t/lib"); } my $test_dir = (File::Spec->splitpath(abs_path(__FILE__)))[1]; push(@INC, "$test_dir/t/lib"); require ProFTPD::TestSuite::Utils; import ProFTPD::TestSuite::Utils qw(:testsuite); # This is to handle the case where this tests.pl script might be # being used to run test files other than those that ship with proftpd, # e.g. to run the tests that come with third-party modules. unless (defined($ENV{PROFTPD_TEST_BIN})) { $ENV{PROFTPD_TEST_BIN} = File::Spec->catfile($test_dir, '..', 'proftpd'); } $| = 1; my $test_files; if (scalar(@ARGV) > 0) { $test_files = [@ARGV]; } else { $test_files = [qw( t/modules/mod_prometheus.t )]; # Now interrogate the build to see which module/feature-specific test files # should be added to the list. my $order = 0; my $FEATURE_TESTS = { 't/modules/mod_prometheus/sftp.t' => { order => ++$order, test_class => [qw(mod_prometheus mod_sftp)], }, 't/modules/mod_prometheus/tls.t' => { order => ++$order, test_class => [qw(mod_prometheus mod_tls)], }, }; my @feature_tests = testsuite_get_runnable_tests($FEATURE_TESTS); my $feature_ntests = scalar(@feature_tests); if ($feature_ntests > 1 || ($feature_ntests == 1 && $feature_tests[0] ne 'testsuite_empty_test')) { push(@$test_files, @feature_tests); } } $ENV{PROFTPD_TEST} = 1; if (defined($opts->{C})) { $ENV{PROFTPD_TEST_ENABLE_CLASS} = join(':', @{ $opts->{C} }); } else { # Disable all 'inprogress' and 'slow' tests by default $ENV{PROFTPD_TEST_DISABLE_CLASS} = 'inprogress:slow'; } if (defined($opts->{F})) { # Using the provided string as a regex, and run only the tests whose # files match the pattern my $file_pattern = $opts->{F}; my $filtered_files = []; foreach my $test_file (@$test_files) { if ($test_file =~ /$file_pattern/) { push(@$filtered_files, $test_file); } } $test_files = $filtered_files; } runtests(@$test_files) if scalar(@$test_files) > 0; exit 0; sub usage { print STDOUT <