diff --git a/.gitignore b/.gitignore index 24f1410..d63edb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # Wildcard + in any subdir. *.o +*.gcno +*.gcda +*.[ch].gcov # Exact filename in any subdir. .deps diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..7f7f47f --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ +Sami Kerola +Sami Kerola diff --git a/Makefile.am b/Makefile.am index 0daa8bf..65b6ba2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,6 @@ ## Makefile.am -- Process this file with automake to produce Makefile.in AUTOMAKE_OPTIONS = gnu -ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = \ .version \ @@ -35,11 +34,12 @@ edit_cmd = sed \ -e 's|@SHELL[@]|$(SHELL)|g' \ -e 's|@VERSION[@]|$(VERSION)|g' \ -e 's|@bindir[@]|$(bindir)|g' \ + -e 's|@docdir[@]|$(docdir)|g' \ -e 's|@top_srcdir[@]|$(top_srcdir)|g' $(PATHFILES): Makefile @ rm -f $@ $@.tmp - $(AM_V_at) test -d $(dir $@) || mkdir -p $(dir $@) + $(AM_V_at) mkdir -p $$(dirname $@) $(AM_V_GEN) srcdir=''; \ test -f ./$@.in || srcdir=$(srcdir)/; \ $(edit_cmd) $${srcdir}$@.in >$@.tmp @@ -48,6 +48,7 @@ $(PATHFILES): Makefile include contrib/Makemodule.am include doc/Makemodule.am include man/Makemodule.am +include samples/Makemodule.am include src/Makemodule.am include tests/Makemodule.am diff --git a/NEWS b/NEWS index 85f09a7..624e0f2 100644 --- a/NEWS +++ b/NEWS @@ -5,7 +5,194 @@ See the end for copying conditions. Please send dhcpd-pools bug reports to kerolasa@iki.fi. -gpg: Signature is crated using RSA key ID FDE9B739. +gpg: Signature is crated using RSA key ID 8ED396E37E38D471A00530D3A9553245FDE9B739. + + +Version 3.3 +Belkacem Daheb (2): + add start, end and hostname printing support for xml and json + getdata output: add start, end and hostname printing support for xml and json + +M. van Brummelen (1): + docs: fix manual page groff warning + +Sami Kerola (9): + update project web page + website: use https + chore: update web links + getdata: only emit warning when config include file cannot be read + gnulib: update bootstrap and gitignore files + build-sys: quote subshell execution in the autotools file + build-sys: update bootstrap from gnulib + output: update html javascripts + release: 3.3 + +luisδμ (1): + fix: avoid generation of unvalid JSON in summary + +Version 3.2 +Jean Benoit (1): + contrib: snmptest.pl SNMPwalk can't access to all variables/wrong sort + +Sami Kerola (5): + contrib: point out where one can find zabbix template + build-sys: update .gitignore files + config: remove unnecessary padding + build-sys: autotools and gnulib related updates + build-sys: routine update + release: 3.2 + +Version 3.1 +Mark Sangster (1): + output: fix warn and crit counts on shared networks + +Sami Kerola (31): + webpage: add instructions how to get output you need + build-sys: add coverage files to .gitignore + tests: improve coverage + docs: fix peoples name in THANKS file to have correct characters + add .mailmap + output: fix implicit conversion + main: simplify option parsing + fix typo + main: move print_mac_addreses to state structure + main: move output_format to state, and rename color_format + webpages: use html sample output + fix switch missing default case warnings + sort: ensure NaN will not trip over comp_double() + drop images, java scripts, and such from web sitemap file + getdata: remote dead code + other: use strftime() to generate date-time string + lib: update .gitignore + fix typos + various: fix few warnings + warnings: ensure optimal packing in structures + other: use IP string lengths from netinet/in.h + mustach: sync with most recent mustach upstream changes + various: tidy up variable scopes, and one name mismatch + build-sys: update .gitignore files + output: add warning and critical threshold counts to mustach + samples: make prometheus template less klunky + misc: fix spelling issues + output: add ethernet address priting support to --mustach + build-sys: update bootstrap from gnulib + output: update javascripts + release: 3.1 + +Version 3.0 +Sami Kerola (56): + release: update web page meta data + update sitemap + output: make output_analysis() to be regular function + getdata: fix typo + output: add output helper functions + output: add color support to text output + output: include earlier missing data to json output + output: add --skip-ok option + other: add --skip-ok to usage() output + output: json nan values need quoting + other: do not use 'else' after 'return' + getdata: do not use 'else' after 'continue' + include: use project specific header guard + output: use range_output_helper() value in output_xml() + output: add mustach templating support + output: add separate first_ip and last_ip to json and mustach outputs + docs: add mustach sample files + tests: add mustach check + output: make mustach processing more robust + output: avoid mixing ntop_ipaddr() output buffers + output: do not skip over first range in mustach output + output: save and reuse output helper results + output: add must_put_err() utility function + output: include stdlib.h to avoid compilation error + gnulib: use nstrftime instead of strftime + build-sys: omit mustach compilation when it cannot work + usage: add error message informing mustach support is not available + output: deduplicate file closing code + clean up: remove unused variable + all files: replace global variables with runtime config state structure + all files: re-indent + analyze: bug fix shared networks counts + other: add Jose Bollo to version output credits + output: improve mustach template parsing error + analysis: shared networks to be linked list + output: make --skip-ok to effect --perfdata + output: move shared net andn range status check to output_helper + output: make warning and critical colors work in html output + output: display more entries in html table by default + output: shared net can be in suppressed state + output: improve html table + hash: include stdlib.h to avoid implicit declarations + clean up: fix couple compiler warnings + thanks: add Troy D. Hanson to credits about uthash + samples: add prometheus text file collector mustach template + output: make --skip to take arguments what will be skipped + misc: move command line option parsing to separate function + misc: move couple enums from global scope to file scope + docs: improve doxygen documentation + output: remove unused variable attribute + output: add more items to mustach tags + output: unify time stamp creations + orther: fix xstrstr_init() memcmp() return value usage + output: add some trivia data to json output + usage: --skip=ignored is actually 'suppressed' + release: 3.0 + +Version 2.29 +Boris Lytochkin (1): + introduce -A arg: treat single subnets as shared-network with CIDR as their name + +Manuel Hachtkemper (1): + alarming: add additional performance data + +Sami Kerola (47): + tell in README when ./bootstrap is needed + add dhcpd-pools website content to a subdirectory + add sitemap url to robots.txt file + use long options in .indent.pro file + webpages: make index page mobile device friendly + webpages: compress sitemap.txt file + getdata: flip ranges if they are in greater smaller order + tests: add range definition flip test + contrib: add archlinux package build file + contrib: remove unnecessary cgi script + contrib: remove awk file duplicate + getdata: get rid of remaining stdbool usage + argument parsing: fix compiler warning + add dhcpd-pools Description Of A Project file + fix doap file git repository and license section + output: check alarming mode can output successfully + build-sys: update bootstrap from gnulib + build-sys: update gnulib .gitignore file + analyze: use while() when for() is less fit to purpose + build-sys: default to ./configure --enable-silent-rules + build-sys: always use restrict found by autoconf + docs: fix couple typos and improve a sentence in README + output: remove unnecessary increment + portability: add gnulib modules earlier missing + remove const and pure function attributes + man: improve synopsis and output limit + man: remove old html table only option argument from manual + getdata: report position in config file when parsing fails + output: fix timestamp localization on html page + output: add include avoid referringt to undefined definition + getdata: remove POSIX_FADV_NOREUSE + style: use same argument names in header and source file + contrib: Klaus Slott told about opensuse package + getdata: fpos_t is not easy to print correctly + lib: update .gitignore + fix typo + variable: add const to print_mac_addreses_tmp + lib: update .gitignore + add --ip-version option to force either IPv4 or IPv6 analysis + lib: update .gitignore + getdata: add cidr range support + build-sys: update bootstrap from gnulib + docs: update doxygen configuration file + docs: tell what needs to be done when releasing new version + docs: add build instruction link to the project web page + docs: update maintainer gpg key + release: 2.29 Version 2.28 Sami Kerola (26): diff --git a/README b/README index 42b6388..5953fba 100644 --- a/README +++ b/README @@ -11,7 +11,7 @@ Quick start. Build the dhcpd-pools project. cd /tmp/dhcpd-pools - ./bootstrap + ./bootstrap # only when building git clone ./configure --with-uthash=/tmp/uthash-master/include make make check @@ -22,13 +22,13 @@ Quick start. ./configure --help - Read the manual to see what options are available, and what they to. + Remember to read the friendly manual page. man ./man/dhcpd-pools.1 Dependencies to other projects. - http://www.gnu.org/software/gnulib/ + https://www.gnu.org/software/gnulib/ You can avoid repeated gnulib downloads by setting GNULIB_SRCDIR environment variable. For example: @@ -39,7 +39,7 @@ Dependencies to other projects. Assumign detached gnulib please remember to git pull the latest updates before building dhcpd-pools. - http://uthash.sourceforge.net/ + https://troydhanson.github.io/uthash/ See quick start. diff --git a/THANKS b/THANKS index 8c8f80b..2aec631 100644 --- a/THANKS +++ b/THANKS @@ -5,23 +5,24 @@ Project is maintained by Sami Kerola People who reported problems, give improvement suggestions or even contributed code. -Otto J. Mäkelä +Otto J. Mäkelä Mika Paananen Frank Bulk Roar Pettersen Jeff Wieland Rusty -Fredrik Vöcks +Fredrik Vöcks Dan Thorson -Stian Øvrevåge +Stian ØvrevÃ¥ge Dominic Germain -Anders Låstad +Anders LÃ¥stad Thor Eivind Brantzeg Ahmed AL Dakhil Adam Ciarcinski -Rezso Gajdóczy +RezsÅ‘ Gajdóczy Robert Viou -Enno Gröper +Enno Gröper +Troy D. Hanson Ryan Malek Cheer Xiao Gilles Bouthenot @@ -29,7 +30,7 @@ Helmut Grohne Joey D. Ryan Steinmetz Dan Pritts -Fredrik Lysén +Fredrik Lysén Conor McCarthy Wolfgang Steudel Aaron Paetznick @@ -37,3 +38,17 @@ Tim Cantin Martijn van Brummelen Anton Tkachev Derrick Lin +Ivanov Ivan +Manuel Hachtkemper +Klaus Slott +Boris Lytochkin +Jeff Bailey +José Bollo +Sebastián Cramatte +Mark Sangster +Brent Swingle +Mathieu Morier +Jean Benoit +Belkacem Daheb +Björn Lässig +Luisδμ diff --git a/TODO b/TODO index fa08675..15c3792 100644 --- a/TODO +++ b/TODO @@ -14,3 +14,20 @@ o When time stamps are part of lease situation evaluation (see bug 1) add a --now switch which will one can use to change when expiry happens. o Add lease time histogram support. + +### When releasing + +o Update gnulib +o Update bootstrap +o Update DataTables, and bootstrap versions; see https://datatables.net/download/index +o Update NEWS, webpages/index.html dateModified & version +o Make annotated git tag +o ./configure --enable-doxygen && make distcheck +o BROWSER=cat man -H ./man/dhcpd-pools.1 | tee ./webpages/man.html +o gpg --armor --detach-sign dhcpd-pools*tar* +* Update sitemap +o Upload files to sourceforge +o mv doc/html webpages/doxygen +o sftp index.html man.html doxygen/* -> sourceforge web + remove old doxygen before uploading new +o Send email to dhcpd-pools-announce@lists.sourceforge.net subject: Version 3.n is released diff --git a/bootstrap b/bootstrap index 60ff8cd..6f289d7 100755 --- a/bootstrap +++ b/bootstrap @@ -1,34 +1,67 @@ #! /bin/sh -# Print a version string. -scriptversion=2013-12-05.23; # UTC +# DO NOT EDIT! GENERATED AUTOMATICALLY! # Bootstrap this package from checked-out sources. -# Copyright (C) 2003-2014 Free Software Foundation, Inc. +scriptversion=2024-07-04.10; # UTC +# Copyright (C) 2003-2025 Free Software Foundation, Inc. +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. - +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # Originally written by Paul Eggert. The canonical version of this -# script is maintained as build-aux/bootstrap in gnulib, however, to -# be useful to your project, you should place a copy of it under -# version control in the top-level directory of your project. The +# script is maintained as top/bootstrap in gnulib. However, to be +# useful to your package, you should place a copy of it under version +# control in the top-level directory of your package. The intent is +# that all customization can be done with a bootstrap.conf file also +# maintained in your version control; gnulib comes with a template +# build-aux/bootstrap.conf to get you started. + +# Please report bugs or propose patches to bug-gnulib@gnu.org. + +me="$0" +medir=`dirname "$me"` + +# Read the function library and the configuration. + +# A library of shell functions for autopull.sh, autogen.sh, and bootstrap. + +scriptlibversion=2025-02-16.12; # UTC + +# Copyright (C) 2003-2025 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Originally written by Paul Eggert. The canonical version of this +# script is maintained as top/bootstrap-funclib.sh in gnulib. However, +# to be useful to your package, you should place a copy of it under +# version control in the top-level directory of your package. The # intent is that all customization can be done with a bootstrap.conf # file also maintained in your version control; gnulib comes with a # template build-aux/bootstrap.conf to get you started. -# Please report bugs or propose patches to bug-gnulib@gnu.org. - nl=' ' @@ -36,46 +69,17 @@ nl=' LC_ALL=C export LC_ALL -# Ensure that CDPATH is not set. Otherwise, the output from cd -# would cause trouble in at least one use below. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH +# Honor $PERL, but work even if there is none. +PERL="${PERL-perl}" -local_gl_dir=gl +default_gnulib_url=https://git.savannah.gnu.org/git/gnulib.git -me=$0 - -usage() { - cat </dev/null) +if test -z "$package"; then + package=$(sed -n "$extract_package_name" configure.ac) \ + || die 'cannot find package name in configure.ac' +fi +package=$(echo "$package" | sed "$normalize_package_name") gnulib_name=lib$package build_aux=build-aux @@ -168,7 +187,15 @@ source_base=lib m4_base=m4 doc_base=doc tests_base=tests -gnulib_extra_files='' +gnulib_extra_files=" + build-aux/install-sh + build-aux/mdate-sh + build-aux/texinfo.tex + build-aux/depcomp + build-aux/config.guess + build-aux/config.sub + doc/INSTALL +" # Additional gnulib-tool options to use. Use "\newline" to break lines. gnulib_tool_option_extras= @@ -188,14 +215,12 @@ COPYRIGHT_HOLDER='Free Software Foundation, Inc.' MSGID_BUGS_ADDRESS=bug-$package@gnu.org # Files we don't want to import. +# XXX Not used. excluded_files= # File that should exist in the top directory of a checked out hierarchy, # but not in a distribution tarball. -checkout_only_file=TODO - -# Whether to use copies instead of symlinks. -copy=false +checkout_only_file=project.doap # Set this to '.cvsignore .gitignore' in bootstrap.conf if you want # those files to be generated in directories like lib/, m4/, and po/. @@ -207,8 +232,13 @@ vc_ignore=auto # default. bootstrap_sync=false -# Use git to update gnulib sources -use_git=true +# Override the default configuration, if necessary. +# Make sure that bootstrap.conf is sourced from the current directory +# if we were invoked as "sh bootstrap". +conffile=`dirname "$me"`/bootstrap.conf +test -r "$conffile" && . "$conffile" + +# ------------------------- Build-time prerequisites ------------------------- check_exists() { if test "$1" = "--verbose"; then @@ -225,6 +255,202 @@ check_exists() { test $? -lt 126 } +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +sort_ver() { # sort -V is not generally available + ver1="$1" + ver2="$2" + + # split on '.' and compare each component + i=1 + while : ; do + p1=$(echo "$ver1" | cut -d. -f$i) + p2=$(echo "$ver2" | cut -d. -f$i) + if [ ! "$p1" ]; then + echo "$1 $2" + break + elif [ ! "$p2" ]; then + echo "$2 $1" + break + elif [ ! "$p1" = "$p2" ]; then + if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison + echo "$2 $1" + elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison + echo "$1 $2" + else # numeric, then lexicographic comparison + lp=$(printf "%s\n%s\n" "$p1" "$p2" | LANG=C sort -n | tail -n1) + if [ "$lp" = "$p2" ]; then + echo "$1 $2" + else + echo "$2 $1" + fi + fi + break + fi + i=$(($i+1)) + done +} + +get_version_sed=' +# Move version to start of line. +s/.*[v ]\([0-9]\)/\1/ + +# Skip lines that do not start with version. +/^[0-9]/!d + +# Remove characters after the version. +s/[^.a-z0-9-].*// + +# The first component must be digits only. +s/^\([0-9]*\)[a-z-].*/\1/ + +#the following essentially does s/5.005/5.5/ +s/\.0*\([1-9]\)/.\1/g +p +q' + +get_version() { + app=$1 + + $app --version >/dev/null 2>&1 || { $app --version; return 1; } + + $app --version 2>&1 | sed -n "$get_version_sed" +} + +check_versions() { + ret=0 + + while read app req_ver; do + # We only need libtoolize from the libtool package. + if test "$app" = libtool; then + app=libtoolize + fi + # Exempt git if git is not needed. + if test "$app" = git; then + $check_git || continue + fi + # Honor $APP variables ($TAR, $AUTOCONF, etc.) + appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_') + test "$appvar" = TAR && appvar=AMTAR + case $appvar in + GZIP) ;; # Do not use $GZIP: it contains gzip options. + PERL::*) ;; # Keep perl modules as-is + *) eval "app=\${$appvar-$app}" ;; + esac + + # Handle the still-experimental Automake-NG programs specially. + # They remain named as the mainstream Automake programs ("automake", + # and "aclocal") to avoid gratuitous incompatibilities with + # preexisting usages (by, say, autoreconf, or custom autogen.sh + # scripts), but correctly identify themselves (as being part of + # "GNU automake-ng") when asked their version. + case $app in + automake-ng|aclocal-ng) + app=${app%-ng} + ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || { + warn_ "Error: '$app' not found or not from Automake-NG" + ret=1 + continue + } ;; + # Another check is for perl modules. These can be written as + # e.g. perl::XML::XPath in case of XML::XPath module, etc. + perl::*) + # Extract module name + app="${app#perl::}" + if ! $PERL -m"$app" -e 'exit 0' >/dev/null 2>&1; then + warn_ "Error: perl module '$app' not found" + ret=1 + fi + continue + ;; + esac + if [ "$req_ver" = "-" ]; then + # Merely require app to exist; not all prereq apps are well-behaved + # so we have to rely on $? rather than get_version. + if ! check_exists --verbose $app; then + warn_ "Error: '$app' not found" + ret=1 + fi + else + # Require app to produce a new enough version string. + inst_ver=$(get_version $app) + if [ ! "$inst_ver" ]; then + warn_ "Error: '$app' not found" + ret=1 + else + latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2) + if [ ! "$latest_ver" = "$inst_ver" ]; then + warnf_ '%s\n' \ + "Error: '$app' version == $inst_ver is too old" \ + " '$app' version >= $req_ver is required" + ret=1 + fi + fi + fi + done + + return $ret +} + +print_versions() { + echo "Program Min_version" + echo "----------------------" + printf %s "$buildreq" + echo "----------------------" + # can't depend on column -t +} + +# check_build_prerequisites check_git +check_build_prerequisites() +{ + check_git="$1" + + # gnulib-tool requires at least automake and autoconf. + # If either is not listed, add it (with minimum version) as a prerequisite. + case $buildreq in + *automake*) ;; + *) buildreq="automake 1.9 +$buildreq" ;; + esac + case $buildreq in + *autoconf*) ;; + *) buildreq="autoconf 2.59 +$buildreq" ;; + esac + + # When we can deduce that gnulib-tool will require patch, + # and when patch is not already listed as a prerequisite, add it, too. + if test -d "$local_gl_dir" \ + && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then + case $buildreq in + *patch*) ;; + *) buildreq="patch - +$buildreq" ;; + esac + fi + + if ! printf '%s' "$buildreq" | check_versions; then + echo >&2 + if test -f README-prereq; then + die "See README-prereq for how to get the prerequisite programs" + else + die "Please install the prerequisite programs" + fi + fi + + # Warn the user if autom4te appears to be broken; this causes known + # issues with at least gettext 0.18.3. + probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -) + if test "x$probe" != xhi; then + warn_ "WARNING: your autom4te wrapper eats stdin;" + warn_ "if bootstrap fails, consider upgrading your autotools" + fi +} + # find_tool ENVVAR NAMES... # ------------------------- # Search for a required program. Use the value of ENVVAR, if set, @@ -254,67 +480,529 @@ find_tool () eval "export $find_tool_envvar" } -# Override the default configuration, if necessary. -# Make sure that bootstrap.conf is sourced from the current directory -# if we were invoked as "sh bootstrap". -case "$0" in - */*) test -r "$0.conf" && . "$0.conf" ;; - *) test -r "$0.conf" && . ./"$0.conf" ;; -esac +# --------------------- Preparing GNULIB_SRCDIR for use. --------------------- +# This is part of autopull.sh, but bootstrap needs it too, for self-upgrading. -# Extra files from gnulib, which override files from other sources. -test -z "${gnulib_extra_files}" && \ - gnulib_extra_files=" - build-aux/install-sh - build-aux/mdate-sh - build-aux/texinfo.tex - build-aux/depcomp - build-aux/config.guess - build-aux/config.sub - doc/INSTALL -" +# cleanup_gnulib fails, removing the directory $gnulib_path first. +cleanup_gnulib() { + status=$? + rm -fr "$gnulib_path" + exit $status +} -if test "$vc_ignore" = auto; then - vc_ignore= - test -d .git && vc_ignore=.gitignore - test -d CVS && vc_ignore="$vc_ignore .cvsignore" +git_modules_config () { + test -f .gitmodules && git config --file .gitmodules "$@" +} + +prepare_GNULIB_SRCDIR () +{ + if test -n "$GNULIB_SRCDIR"; then + # Use GNULIB_SRCDIR directly. + # We already checked that $GNULIB_SRCDIR references a directory. + # Verify that it contains a gnulib checkout. + test -f "$GNULIB_SRCDIR/gnulib-tool" \ + || die "Error: --gnulib-srcdir or \$GNULIB_SRCDIR is specified," \ + "but does not contain gnulib-tool" + if test -n "$GNULIB_REVISION" && $use_git; then + # The 'git checkout "$GNULIB_REVISION"' command succeeds if the + # GNULIB_REVISION is a commit hash that exists locally, or if it is + # branch name that can be fetched from origin. It fails, however, + # if the GNULIB_REVISION is a commit hash that only exists in + # origin. In this case, we need a 'git fetch' and then retry + # 'git checkout "$GNULIB_REVISION"'. + git -C "$GNULIB_SRCDIR" checkout "$GNULIB_REVISION" 2>/dev/null \ + || { git -C "$GNULIB_SRCDIR" fetch origin \ + && git -C "$GNULIB_SRCDIR" checkout "$GNULIB_REVISION"; } \ + || exit $? + fi + else + if ! $use_git; then + die "Error: --no-git is specified," \ + "but neither --gnulib-srcdir nor \$GNULIB_SRCDIR is specified" + fi + if git submodule -h | grep -- --reference > /dev/null; then + : + else + die "git version is too old, git >= 1.6.4 is required" + fi + gnulib_path=$(git_modules_config submodule.gnulib.path) + if test -n "$gnulib_path"; then + # A submodule 'gnulib' is configured. + # Get gnulib files. Populate $gnulib_path, updating the submodule. + if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git; then + # Use GNULIB_REFDIR as a reference. + echo "$0: getting gnulib files..." + git submodule update --init --reference "$GNULIB_REFDIR" "$gnulib_path"\ + || exit $? + else + # GNULIB_REFDIR is not set or not usable. Ignore it. + if git_modules_config submodule.gnulib.url >/dev/null; then + echo "$0: getting gnulib files..." + git submodule init -- "$gnulib_path" || exit $? + git submodule update -- "$gnulib_path" || exit $? + else + die "Error: submodule 'gnulib' has no configured url" + fi + fi + else + gnulib_path='gnulib' + if test ! -d "$gnulib_path"; then + # The subdirectory 'gnulib' does not yet exist. Clone into it. + echo "$0: getting gnulib files..." + trap cleanup_gnulib HUP INT PIPE TERM + gnulib_url=${GNULIB_URL:-$default_gnulib_url} + if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git; then + # Use GNULIB_REFDIR as a reference. + git clone "$GNULIB_REFDIR" "$gnulib_path" \ + && git -C "$gnulib_path" remote set-url origin "$gnulib_url" \ + && if test -z "$GNULIB_REVISION"; then + git -C "$gnulib_path" pull origin \ + && { + # We want the default branch of "$gnulib_url" (since that's + # the behaviour if GNULIB_REFDIR is not specified), not the + # current branch of "$GNULIB_REFDIR". + default_branch=`LC_ALL=C git -C "$gnulib_path" \ + remote show origin \ + | sed -n -e 's/^ *HEAD branch: //p'` + test -n "$default_branch" || default_branch='master' + git -C "$gnulib_path" checkout "$default_branch" + } + else + # The 'git checkout "$GNULIB_REVISION"' command succeeds if the + # GNULIB_REVISION is a commit hash that exists locally, or if it + # is a branch name that can be fetched from origin. It fails, + # however, if the GNULIB_REVISION is a commit hash that only + # exists in origin. In this case, we need a 'git fetch' and then + # retry 'git checkout "$GNULIB_REVISION"'. + git -C "$gnulib_path" checkout "$GNULIB_REVISION" 2>/dev/null \ + || { git -C "$gnulib_path" fetch origin \ + && git -C "$gnulib_path" checkout "$GNULIB_REVISION"; } + fi \ + || cleanup_gnulib + else + # GNULIB_REFDIR is not set or not usable. Ignore it. + shallow='--depth 2' + if test -z "$GNULIB_REVISION"; then + git clone $shallow "$gnulib_url" "$gnulib_path" \ + || cleanup_gnulib + else + # Only want a shallow checkout of $GNULIB_REVISION, but git does not + # support cloning by commit hash. So attempt a shallow fetch by + # commit hash to minimize the amount of data downloaded and changes + # needed to be processed, which can drastically reduce download and + # processing time for checkout. If the fetch by commit fails, a + # shallow fetch cannot be performed because we do not know what the + # depth of the commit is without fetching all commits. So fall back + # to fetching all commits. + # $GNULIB_REVISION can be a commit id, a tag name, or a branch name. + mkdir -p "$gnulib_path" + # Use a -c option to silence an annoying message + # "hint: Using 'master' as the name for the initial branch." + # (cf. ). + git -C "$gnulib_path" -c init.defaultBranch=master init + git -C "$gnulib_path" remote add origin "$gnulib_url" + if git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION" + then + # "git fetch" of the specific commit succeeded. + git -C "$gnulib_path" reset --hard FETCH_HEAD \ + || cleanup_gnulib + # "git fetch" does not fetch tags (at least in git version 2.43). + # If $GNULIB_REVISION is a tag (not a commit id or branch name), + # add the tag explicitly. + revision=`git -C "$gnulib_path" log -1 --pretty=format:%H` + branch=`LC_ALL=C git -C "$gnulib_path" remote show origin \ + | sed -n -e 's/^ \([^ ]*\) * tracked$/\1/p'` + test "$revision" = "$GNULIB_REVISION" \ + || test "$branch" = "$GNULIB_REVISION" \ + || git -C "$gnulib_path" tag "$GNULIB_REVISION" + else + # Fetch the entire repository. + git -C "$gnulib_path" fetch origin \ + || cleanup_gnulib + git -C "$gnulib_path" checkout "$GNULIB_REVISION" \ + || cleanup_gnulib + fi + fi + fi + trap - HUP INT PIPE TERM + else + # The subdirectory 'gnulib' already exists. + if test -n "$GNULIB_REVISION"; then + if test -d "$gnulib_path/.git"; then + # The 'git checkout "$GNULIB_REVISION"' command succeeds if the + # GNULIB_REVISION is a commit hash that exists locally, or if it + # is a branch name that can be fetched from origin. It fails, + # however, if the GNULIB_REVISION is a commit hash that only + # exists in origin. In this case, we need a 'git fetch' and then + # retry 'git checkout "$GNULIB_REVISION"'. + git -C "$gnulib_path" checkout "$GNULIB_REVISION" 2>/dev/null \ + || { git -C "$gnulib_path" fetch origin \ + && git -C "$gnulib_path" checkout "$GNULIB_REVISION"; } \ + || exit $? + else + die "Error: GNULIB_REVISION is specified in bootstrap.conf," \ + "but '$gnulib_path' contains no git history" + fi + fi + fi + fi + # Verify that $gnulib_path contains a gnulib checkout. + test -f "$gnulib_path/gnulib-tool" \ + || die "Error: '$gnulib_path' is supposed to contain a gnulib checkout," \ + "but does not contain gnulib-tool" + GNULIB_SRCDIR=$gnulib_path + fi + # $GNULIB_SRCDIR now points to the version of gnulib to use, and + # we no longer need to use git or $gnulib_path below here. +} + +# -------- Upgrading bootstrap to the version found in GNULIB_SRCDIR. -------- + +upgrade_bootstrap () +{ + if test -f "$medir"/bootstrap-funclib.sh; then + update_lib=true + { cmp -s "$medir"/bootstrap "$GNULIB_SRCDIR/top/bootstrap" \ + && cmp -s "$medir"/bootstrap-funclib.sh \ + "$GNULIB_SRCDIR/top/bootstrap-funclib.sh" \ + && cmp -s "$medir"/autopull.sh "$GNULIB_SRCDIR/top/autopull.sh" \ + && cmp -s "$medir"/autogen.sh "$GNULIB_SRCDIR/top/autogen.sh"; \ + } + else + update_lib=false + cmp -s "$medir"/bootstrap "$GNULIB_SRCDIR/build-aux/bootstrap" + fi || { + if $update_lib; then + echo "$0: updating bootstrap & companions and restarting..." + else + echo "$0: updating bootstrap and restarting..." + fi + case $(sh -c 'echo "$1"' -- a) in + a) ignored=--;; + *) ignored=ignored;; + esac + u=$update_lib + exec sh -c \ + '{ if '$u' && test -f "$1"; then cp "$1" "$3"; else cp "$2" "$3"; fi; } && + { if '$u' && test -f "$4"; then cp "$4" "$5"; else rm -f "$5"; fi; } && + { if '$u' && test -f "$6"; then cp "$6" "$7"; else rm -f "$7"; fi; } && + { if '$u' && test -f "$8"; then cp "$8" "$9"; else rm -f "$9"; fi; } && + shift && shift && shift && shift && shift && + shift && shift && shift && shift && + exec "${CONFIG_SHELL-/bin/sh}" "$@"' \ + $ignored \ + "$GNULIB_SRCDIR/top/bootstrap" "$GNULIB_SRCDIR/build-aux/bootstrap" \ + "$medir/bootstrap" \ + "$GNULIB_SRCDIR/top/bootstrap-funclib.sh" "$medir/bootstrap-funclib.sh" \ + "$GNULIB_SRCDIR/top/autopull.sh" "$medir/autopull.sh" \ + "$GNULIB_SRCDIR/top/autogen.sh" "$medir/autogen.sh" \ + "$0" "$@" --no-bootstrap-sync + } +} + +# ---------------------------------------------------------------------------- + +if test x"$gnulib_modules$gnulib_files$gnulib_extra_files" = x; then + use_gnulib=false +else + use_gnulib=true fi -# Translate configuration into internal form. +# -------- Fetch auxiliary files from the network. -------------------------- -# Parse options. +autopull_usage() { + cat </dev/null 2>&1 && unset CDPATH + + # Parse options. + + # Use git to update gnulib sources + use_git=true + + for option + do + case $option in + --help) + autopull_usage + return;; + --version) + set -e + echo "autopull.sh $scriptlibversion" + echo "$copyright" + return 0 + ;; + --skip-po) + SKIP_PO=t;; + --force) + checkout_only_file=;; + --bootstrap-sync) + bootstrap_sync=true;; + --no-bootstrap-sync) + bootstrap_sync=false;; + --no-git) + use_git=false;; + *) + bootstrap_option_hook $option || die "$option: unknown option";; + esac + done + + $use_git || test -n "$GNULIB_SRCDIR" \ + || die "Error: --no-git requires \$GNULIB_SRCDIR environment variable" \ + "or --gnulib-srcdir option" + test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \ + || die "Error: \$GNULIB_SRCDIR environment variable" \ + "or --gnulib-srcdir option is specified," \ + "but does not denote a directory" + + if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then + die "Running this script from a non-checked-out distribution is risky." + fi + + check_build_prerequisites $use_git + + if $use_gnulib || $bootstrap_sync; then + prepare_GNULIB_SRCDIR + if $bootstrap_sync; then + upgrade_bootstrap "$@" + fi + fi + + # Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6. + # Also find the compatible sha1 utility on the BSDs + if test x"$SKIP_PO" = x; then + find_tool SHA1SUM sha1sum gsha1sum shasum sha1 + fi + + # See if we can use gnulib's git-merge-changelog merge driver. + if $use_git && test -d .git && check_exists git; then + if git config merge.merge-changelog.driver >/dev/null ; then + : + elif check_exists git-merge-changelog; then + echo "$0: initializing git-merge-changelog driver" + git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' + git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' + else + echo "$0: consider installing git-merge-changelog from gnulib" + fi + fi + + case $SKIP_PO in + '') + if test -d po; then + update_po_files po $package || return + fi + + if test -d runtime-po; then + update_po_files runtime-po $package-runtime || return + fi;; esac -done -$use_git || test -d "$GNULIB_SRCDIR" \ - || die "Error: --no-git requires --gnulib-srcdir" + # --------------------------------------------------------------------------- -if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then - die "Bootstrapping from a non-checked-out distribution is risky." -fi + bootstrap_post_pull_hook \ + || die "bootstrap_post_pull_hook failed" + + # Don't proceed if there are uninitialized submodules. In particular, + # autogen.sh will remove dangling links, which might be links into + # uninitialized submodules. + # But it's OK if the 'gnulib' submodule is uninitialized, as long as + # GNULIB_SRCDIR is set. + if $use_git; then + # Uninitialized submodules are listed with an initial dash. + uninitialized=`git submodule | grep '^-' | awk '{ print $2 }'` + if test -n "$GNULIB_SRCDIR"; then + uninitialized=`echo "$uninitialized" | grep -v '^gnulib$'` + fi + if test -n "$uninitialized"; then + uninit_comma=`echo "$uninitialized" | tr '\n' ',' | sed -e 's|,$|.|'` + die "Some git submodules are not initialized: "$uninit_comma \ + "Either use option '--no-git'," \ + "or run 'git submodule update --init' and bootstrap again." + fi + fi + + if test -f "$medir"/autogen.sh; then + echo "$0: done. Now you can run '$medir/autogen.sh'." + fi +} + +# ----------------------------- Get translations. ----------------------------- + +download_po_files() { + subdir=$1 + domain=$2 + echo "$me: getting translations into $subdir for $domain..." + cmd=$(printf "$po_download_command_format" "$subdir" "$domain") + eval "$cmd" +} + +# Mirror .po files to $po_dir/.reference and copy only the new +# or modified ones into $po_dir. Also update $po_dir/LINGUAS. +# Note po files that exist locally only are left in $po_dir but will +# not be included in LINGUAS and hence will not be distributed. +update_po_files() { + # Directory containing primary .po files. + # Overwrite them only when we're sure a .po file is new. + po_dir=$1 + domain=$2 + + # Mirror *.po files into this dir. + # Usually contains *.s1 checksum files. + ref_po_dir="$po_dir/.reference" + + test -d $ref_po_dir || mkdir $ref_po_dir || return + download_po_files $ref_po_dir $domain \ + && ls "$ref_po_dir"/*.po 2>/dev/null | + sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return + + for po in x $(ls $ref_po_dir | sed -n 's/\.po$//p'); do + case $po in x) continue;; esac + new_po="$ref_po_dir/$po.po" + cksum_file="$ref_po_dir/$po.s1" + if ! test -f "$cksum_file" || + ! test -f "$po_dir/$po.po" || + ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then + echo "$me: updated $po_dir/$po.po..." + cp "$new_po" "$po_dir/$po.po" \ + && $SHA1SUM < "$new_po" > "$cksum_file" || return + fi + done +} + +# -------- Generate files automatically from existing sources. -------------- + +autogen_usage() { + cat < /dev/null 2>&1 + elif test -d .svn; then + svn log -r HEAD "$file" > /dev/null 2>&1 + elif test -d CVS; then + grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null | + grep '^/[^/]*/[0-9]' > /dev/null + else + warn_ "no version control for $file?" + false + fi +} # Strip blank and comment lines to leave significant entries. gitignore_entries() { @@ -357,383 +1045,6 @@ insert_vc_ignore() { insert_if_absent "$vc_ignore_file" "$pattern" } -# Die if there is no AC_CONFIG_AUX_DIR($build_aux) line in configure.ac. -found_aux_dir=no -grep '^[ ]*AC_CONFIG_AUX_DIR(\['"$build_aux"'\])' configure.ac \ - >/dev/null && found_aux_dir=yes -grep '^[ ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \ - >/dev/null && found_aux_dir=yes -test $found_aux_dir = yes \ - || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it" - -# If $build_aux doesn't exist, create it now, otherwise some bits -# below will malfunction. If creating it, also mark it as ignored. -if test ! -d $build_aux; then - mkdir $build_aux - for dot_ig in x $vc_ignore; do - test $dot_ig = x && continue - insert_vc_ignore $dot_ig $build_aux - done -fi - -# Note this deviates from the version comparison in automake -# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a -# but this should suffice as we won't be specifying old -# version formats or redundant trailing .0 in bootstrap.conf. -# If we did want full compatibility then we should probably -# use m4_version_compare from autoconf. -sort_ver() { # sort -V is not generally available - ver1="$1" - ver2="$2" - - # split on '.' and compare each component - i=1 - while : ; do - p1=$(echo "$ver1" | cut -d. -f$i) - p2=$(echo "$ver2" | cut -d. -f$i) - if [ ! "$p1" ]; then - echo "$1 $2" - break - elif [ ! "$p2" ]; then - echo "$2 $1" - break - elif [ ! "$p1" = "$p2" ]; then - if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison - echo "$2 $1" - elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison - echo "$1 $2" - else # numeric, then lexicographic comparison - lp=$(printf "$p1\n$p2\n" | LANG=C sort -n | tail -n1) - if [ "$lp" = "$p2" ]; then - echo "$1 $2" - else - echo "$2 $1" - fi - fi - break - fi - i=$(($i+1)) - done -} - -get_version() { - app=$1 - - $app --version >/dev/null 2>&1 || { $app --version; return 1; } - - $app --version 2>&1 | - sed -n '# Move version to start of line. - s/.*[v ]\([0-9]\)/\1/ - - # Skip lines that do not start with version. - /^[0-9]/!d - - # Remove characters after the version. - s/[^.a-z0-9-].*// - - # The first component must be digits only. - s/^\([0-9]*\)[a-z-].*/\1/ - - #the following essentially does s/5.005/5.5/ - s/\.0*\([1-9]\)/.\1/g - p - q' -} - -check_versions() { - ret=0 - - while read app req_ver; do - # We only need libtoolize from the libtool package. - if test "$app" = libtool; then - app=libtoolize - fi - # Exempt git if --no-git is in effect. - if test "$app" = git; then - $use_git || continue - fi - # Honor $APP variables ($TAR, $AUTOCONF, etc.) - appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_') - test "$appvar" = TAR && appvar=AMTAR - case $appvar in - GZIP) ;; # Do not use $GZIP: it contains gzip options. - *) eval "app=\${$appvar-$app}" ;; - esac - - # Handle the still-experimental Automake-NG programs specially. - # They remain named as the mainstream Automake programs ("automake", - # and "aclocal") to avoid gratuitous incompatibilities with - # pre-existing usages (by, say, autoreconf, or custom autogen.sh - # scripts), but correctly identify themselves (as being part of - # "GNU automake-ng") when asked their version. - case $app in - automake-ng|aclocal-ng) - app=${app%-ng} - ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || { - warn_ "Error: '$app' not found or not from Automake-NG" - ret=1 - continue - } ;; - esac - if [ "$req_ver" = "-" ]; then - # Merely require app to exist; not all prereq apps are well-behaved - # so we have to rely on $? rather than get_version. - if ! check_exists --verbose $app; then - warn_ "Error: '$app' not found" - ret=1 - fi - else - # Require app to produce a new enough version string. - inst_ver=$(get_version $app) - if [ ! "$inst_ver" ]; then - warn_ "Error: '$app' not found" - ret=1 - else - latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2) - if [ ! "$latest_ver" = "$inst_ver" ]; then - warnf_ '%s\n' \ - "Error: '$app' version == $inst_ver is too old" \ - " '$app' version >= $req_ver is required" - ret=1 - fi - fi - fi - done - - return $ret -} - -print_versions() { - echo "Program Min_version" - echo "----------------------" - printf %s "$buildreq" - echo "----------------------" - # can't depend on column -t -} - -# Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6. -# Also find the compatible sha1 utility on the BSDs -if test x"$SKIP_PO" = x; then - find_tool SHA1SUM sha1sum gsha1sum shasum sha1 -fi - -use_libtool=0 -# We'd like to use grep -E, to see if any of LT_INIT, -# AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac, -# but that's not portable enough (e.g., for Solaris). -grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \ - && use_libtool=1 -grep '^[ ]*LT_INIT' configure.ac >/dev/null \ - && use_libtool=1 -if test $use_libtool = 1; then - find_tool LIBTOOLIZE glibtoolize libtoolize -fi - -# gnulib-tool requires at least automake and autoconf. -# If either is not listed, add it (with minimum version) as a prerequisite. -case $buildreq in - *automake*) ;; - *) buildreq="automake 1.9 -$buildreq" ;; -esac -case $buildreq in - *autoconf*) ;; - *) buildreq="autoconf 2.59 -$buildreq" ;; -esac - -# When we can deduce that gnulib-tool will require patch, -# and when patch is not already listed as a prerequisite, add it, too. -if test -d "$local_gl_dir" \ - && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then - case $buildreq in - *patch*) ;; - *) buildreq="patch - -$buildreq" ;; - esac -fi - -if ! printf "$buildreq" | check_versions; then - echo >&2 - if test -f README-prereq; then - die "See README-prereq for how to get the prerequisite programs" - else - die "Please install the prerequisite programs" - fi -fi - -# Warn the user if autom4te appears to be broken; this causes known -# issues with at least gettext 0.18.3. -probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -) -if test "x$probe" != xhi; then - warn_ "WARNING: your autom4te wrapper eats stdin;" - warn_ "if bootstrap fails, consider upgrading your autotools" -fi - -echo "$0: Bootstrapping from checked-out $package sources..." - -# See if we can use gnulib's git-merge-changelog merge driver. -if $use_git && test -d .git && check_exists git; then - if git config merge.merge-changelog.driver >/dev/null ; then - : - elif check_exists git-merge-changelog; then - echo "$0: initializing git-merge-changelog driver" - git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' - git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' - else - echo "$0: consider installing git-merge-changelog from gnulib" - fi -fi - - -cleanup_gnulib() { - status=$? - rm -fr "$gnulib_path" - exit $status -} - -git_modules_config () { - test -f .gitmodules && git config --file .gitmodules "$@" -} - -if $use_git; then - gnulib_path=$(git_modules_config submodule.gnulib.path) - test -z "$gnulib_path" && gnulib_path=gnulib -fi - -# Get gnulib files. Populate $GNULIB_SRCDIR, possibly updating a -# submodule, for use in the rest of the script. - -case ${GNULIB_SRCDIR--} in --) - # Note that $use_git is necessarily true in this case. - if git_modules_config submodule.gnulib.url >/dev/null; then - echo "$0: getting gnulib files..." - git submodule init -- "$gnulib_path" || exit $? - git submodule update -- "$gnulib_path" || exit $? - - elif [ ! -d "$gnulib_path" ]; then - echo "$0: getting gnulib files..." - - trap cleanup_gnulib 1 2 13 15 - - shallow= - git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' - git clone $shallow git://git.sv.gnu.org/gnulib "$gnulib_path" || - cleanup_gnulib - - trap - 1 2 13 15 - fi - GNULIB_SRCDIR=$gnulib_path - ;; -*) - # Use GNULIB_SRCDIR directly or as a reference. - if $use_git && test -d "$GNULIB_SRCDIR"/.git && \ - git_modules_config submodule.gnulib.url >/dev/null; then - echo "$0: getting gnulib files..." - if git submodule -h|grep -- --reference > /dev/null; then - # Prefer the one-liner available in git 1.6.4 or newer. - git submodule update --init --reference "$GNULIB_SRCDIR" \ - "$gnulib_path" || exit $? - else - # This fallback allows at least git 1.5.5. - if test -f "$gnulib_path"/gnulib-tool; then - # Since file already exists, assume submodule init already complete. - git submodule update -- "$gnulib_path" || exit $? - else - # Older git can't clone into an empty directory. - rmdir "$gnulib_path" 2>/dev/null - git clone --reference "$GNULIB_SRCDIR" \ - "$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \ - && git submodule init -- "$gnulib_path" \ - && git submodule update -- "$gnulib_path" \ - || exit $? - fi - fi - GNULIB_SRCDIR=$gnulib_path - fi - ;; -esac - -# $GNULIB_SRCDIR now points to the version of gnulib to use, and -# we no longer need to use git or $gnulib_path below here. - -if $bootstrap_sync; then - cmp -s "$0" "$GNULIB_SRCDIR/build-aux/bootstrap" || { - echo "$0: updating bootstrap and restarting..." - case $(sh -c 'echo "$1"' -- a) in - a) ignored=--;; - *) ignored=ignored;; - esac - exec sh -c \ - 'cp "$1" "$2" && shift && exec "${CONFIG_SHELL-/bin/sh}" "$@"' \ - $ignored "$GNULIB_SRCDIR/build-aux/bootstrap" \ - "$0" "$@" --no-bootstrap-sync - } -fi - -gnulib_tool=$GNULIB_SRCDIR/gnulib-tool -<$gnulib_tool || exit $? - -# Get translations. - -download_po_files() { - subdir=$1 - domain=$2 - echo "$me: getting translations into $subdir for $domain..." - cmd=$(printf "$po_download_command_format" "$domain" "$subdir") - eval "$cmd" && return - # Fallback to HTTP. - cmd=$(printf "$po_download_command_format2" "$subdir" "$domain") - eval "$cmd" -} - -# Mirror .po files to $po_dir/.reference and copy only the new -# or modified ones into $po_dir. Also update $po_dir/LINGUAS. -# Note po files that exist locally only are left in $po_dir but will -# not be included in LINGUAS and hence will not be distributed. -update_po_files() { - # Directory containing primary .po files. - # Overwrite them only when we're sure a .po file is new. - po_dir=$1 - domain=$2 - - # Mirror *.po files into this dir. - # Usually contains *.s1 checksum files. - ref_po_dir="$po_dir/.reference" - - test -d $ref_po_dir || mkdir $ref_po_dir || return - download_po_files $ref_po_dir $domain \ - && ls "$ref_po_dir"/*.po 2>/dev/null | - sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return - - langs=$(cd $ref_po_dir && echo *.po | sed 's/\.po//g') - test "$langs" = '*' && langs=x - for po in $langs; do - case $po in x) continue;; esac - new_po="$ref_po_dir/$po.po" - cksum_file="$ref_po_dir/$po.s1" - if ! test -f "$cksum_file" || - ! test -f "$po_dir/$po.po" || - ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then - echo "$me: updated $po_dir/$po.po..." - cp "$new_po" "$po_dir/$po.po" \ - && $SHA1SUM < "$new_po" > "$cksum_file" || return - fi - done -} - -case $SKIP_PO in -'') - if test -d po; then - update_po_files po $package || exit - fi - - if test -d runtime-po; then - update_po_files runtime-po $package-runtime || exit - fi;; -esac - symlink_to_dir() { src=$1/$2 @@ -754,7 +1065,7 @@ symlink_to_dir() for dot_ig in x $vc_ignore; do test $dot_ig = x && continue ig=$parent/$dot_ig - insert_vc_ignore $ig "${dst_dir##*/}" + insert_vc_ignore $ig "${dst_dir##*/}/" done fi @@ -774,9 +1085,9 @@ symlink_to_dir() # Leave any existing symlink alone, if it already points to the source, # so that broken build tools that care about symlink times # aren't confused into doing unnecessary builds. Conversely, if the - # existing symlink's time stamp is older than the source, make it afresh, + # existing symlink's timestamp is older than the source, make it afresh, # so that broken tools aren't confused into skipping needed builds. See - # . + # . test -h "$dst" && src_ls=$(ls -diL "$src" 2>/dev/null) && set $src_ls && src_i=$1 && dst_ls=$(ls -diL "$dst" 2>/dev/null) && set $dst_ls && dst_i=$1 && @@ -803,209 +1114,518 @@ symlink_to_dir() } } -version_controlled_file() { - parent=$1 - file=$2 - if test -d .git; then - git rm -n "$file" > /dev/null 2>&1 - elif test -d .svn; then - svn log -r HEAD "$file" > /dev/null 2>&1 - elif test -d CVS; then - grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null | - grep '^/[^/]*/[0-9]' > /dev/null - else - warn_ "no version control for $file?" - false +# Regenerate all autogeneratable files that are omitted from the +# version control repository. In particular, regenerate all +# aclocal.m4, config.h.in, Makefile.in, configure files with new +# versions of autoconf or automake. +autogen() +{ + # Ensure that CDPATH is not set. Otherwise, the output from cd + # would cause trouble in at least one use below. + (unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + # Environment variables that may be set by the user. + : "${AUTOPOINT=autopoint}" + : "${AUTORECONF=autoreconf}" + + if test "$vc_ignore" = auto; then + vc_ignore= + test -d .git && vc_ignore=.gitignore + test -d CVS && vc_ignore="$vc_ignore .cvsignore" fi -} -# NOTE: we have to be careful to run both autopoint and libtoolize -# before gnulib-tool, since gnulib-tool is likely to provide newer -# versions of files "installed" by these two programs. -# Then, *after* gnulib-tool (see below), we have to be careful to -# run autoreconf in such a way that it does not run either of these -# two just-pre-run programs. -# Import from gettext. -with_gettext=yes -grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \ - with_gettext=no + # Parse options. -if test $with_gettext = yes || test $use_libtool = 1; then + # Whether to use copies instead of symlinks. + copy=false - tempbase=.bootstrap$$ - trap "rm -f $tempbase.0 $tempbase.1" 1 2 13 15 + for option + do + case $option in + --help) + autogen_usage + return;; + --version) + set -e + echo "autogen.sh $scriptlibversion" + echo "$copyright" + return 0 + ;; + --force) + checkout_only_file=;; + --copy) + copy=true;; + *) + bootstrap_option_hook $option || die "$option: unknown option";; + esac + done - > $tempbase.0 > $tempbase.1 && - find . ! -type d -print | sort > $tempbase.0 || exit + test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \ + || die "Error: \$GNULIB_SRCDIR environment variable or --gnulib-srcdir" \ + "option is specified, but does not denote a directory" + + if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then + die "Running this script from a non-checked-out distribution is risky." + fi + + if $use_gnulib; then + if test -z "$GNULIB_SRCDIR"; then + gnulib_path=$(test -f .gitmodules && + git config --file .gitmodules submodule.gnulib.path) + test -z "$gnulib_path" && gnulib_path=gnulib + GNULIB_SRCDIR=$gnulib_path + fi + fi + + # Die if there is no AC_CONFIG_AUX_DIR($build_aux) line in configure.ac. + found_aux_dir=no + grep '^[ ]*AC_CONFIG_AUX_DIR(\['"$build_aux"'])' configure.ac \ + >/dev/null && found_aux_dir=yes + grep '^[ ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \ + >/dev/null && found_aux_dir=yes + test $found_aux_dir = yes \ + || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it" + + # If $build_aux doesn't exist, create it now, otherwise some bits + # below will malfunction. If creating it, also mark it as ignored. + if test ! -d $build_aux; then + mkdir $build_aux + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + insert_vc_ignore $dot_ig $build_aux/ + done + fi + + check_build_prerequisites false + + use_libtool=0 + # We'd like to use grep -E, to see if any of LT_INIT, + # AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac, + # but that's not portable enough (e.g., for Solaris). + grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \ + && use_libtool=1 + grep '^[ ]*LT_INIT' configure.ac >/dev/null \ + && use_libtool=1 + if test $use_libtool = 1; then + find_tool LIBTOOLIZE glibtoolize libtoolize + fi + + if $use_gnulib; then + gnulib_tool=$GNULIB_SRCDIR/gnulib-tool + <$gnulib_tool || return + fi + + # NOTE: we have to be careful to run both autopoint and libtoolize + # before gnulib-tool, since gnulib-tool is likely to provide newer + # versions of files "installed" by these two programs. + # Then, *after* gnulib-tool (see below), we have to be careful to + # run autoreconf in such a way that it does not run either of these + # two just-pre-run programs. + + # Import from gettext. + with_gettext=yes + grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \ + with_gettext=no + + if test $with_gettext = yes || test $use_libtool = 1; then + + tempbase=.bootstrap$$ + trap "rm -f $tempbase.0 $tempbase.1" HUP INT PIPE TERM + + > $tempbase.0 > $tempbase.1 && + find . ! -type d -print | sort > $tempbase.0 || return + + if test $with_gettext = yes; then + # Released autopoint has the tendency to install macros that have been + # obsoleted in current gnulib, so run this before gnulib-tool. + echo "$0: $AUTOPOINT --force" + $AUTOPOINT --force || return + fi + + # Autoreconf runs aclocal before libtoolize, which causes spurious + # warnings if the initial aclocal is confused by the libtoolized + # (or worse out-of-date) macro directory. + # libtoolize 1.9b added the --install option; but we support back + # to libtoolize 1.5.22, where the install action was default. + if test $use_libtool = 1; then + install= + case $($LIBTOOLIZE --help) in + *--install*) install=--install ;; + esac + echo "running: $LIBTOOLIZE $install --copy" + $LIBTOOLIZE $install --copy + fi + + find . ! -type d -print | sort >$tempbase.1 + old_IFS=$IFS + IFS=$nl + for file in $(comm -13 $tempbase.0 $tempbase.1); do + IFS=$old_IFS + parent=${file%/*} + version_controlled_file "$parent" "$file" || { + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + ig=$parent/$dot_ig + insert_vc_ignore "$ig" "${file##*/}" + done + } + done + IFS=$old_IFS + + rm -f $tempbase.0 $tempbase.1 + trap - HUP INT PIPE TERM + fi + + # Import from gnulib. + + if $use_gnulib; then + gnulib_tool_options="\ + --no-changelog\ + --aux-dir=$build_aux\ + --doc-base=$doc_base\ + --lib=$gnulib_name\ + --m4-base=$m4_base/\ + --source-base=$source_base/\ + --tests-base=$tests_base\ + --local-dir=$local_gl_dir\ + $gnulib_tool_option_extras\ + " + if test $use_libtool = 1; then + case "$gnulib_tool_options " in + *' --libtool '*) ;; + *) gnulib_tool_options="$gnulib_tool_options --libtool" ;; + esac + fi + echo "$0: $gnulib_tool $gnulib_tool_options --import ..." + $gnulib_tool $gnulib_tool_options --import $gnulib_modules \ + || die "gnulib-tool failed" + + if test $with_gettext = yes && test ! -f $m4_base/gettext.m4; then + # The gnulib-tool invocation has removed $m4_base/gettext.m4, that the + # AUTOPOINT invocation had installed. This can occur when the gnulib + # module 'gettext' was previously present but is now not present any more. + # Repeat the AUTOPOINT invocation and the gnulib-tool invocation. + + echo "$0: $AUTOPOINT --force" + $AUTOPOINT --force || return + + echo "$0: $gnulib_tool $gnulib_tool_options --import ..." + $gnulib_tool $gnulib_tool_options --import $gnulib_modules \ + || die "gnulib-tool failed" + fi + + for file in $gnulib_files; do + symlink_to_dir "$GNULIB_SRCDIR" $file \ + || die "failed to symlink $file" + done + fi + + bootstrap_post_import_hook \ + || die "bootstrap_post_import_hook failed" + + # Remove any dangling symlink matching "*.m4" or "*.[ch]" in some + # gnulib-populated directories. Such .m4 files would cause aclocal to fail. + # The following requires GNU find 4.2.3 or newer. Considering the usual + # portability constraints of this script, that may seem a very demanding + # requirement, but it should be ok. Ignore any failure, which is fine, + # since this is only a convenience to help developers avoid the relatively + # unusual case in which a symlinked-to .m4 file is git-removed from gnulib + # between successive runs of this script. + find "$m4_base" "$source_base" \ + -depth \( -name '*.m4' -o -name '*.[ch]' \) \ + -type l -xtype l -delete > /dev/null 2>&1 + + # Invoke autoreconf with --force --install to ensure upgrades of tools + # such as ylwrap. + AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS" + AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive" + + # Tell autoreconf not to invoke autopoint or libtoolize; they were run above. + echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS" + AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \ + || die "autoreconf failed" + + # Get some extra files from gnulib, overriding existing files. + for file in $gnulib_extra_files; do + case $file in + */INSTALL) dst=INSTALL;; + build-aux/*) dst=$build_aux/${file#build-aux/};; + *) dst=$file;; + esac + symlink_to_dir "$GNULIB_SRCDIR" $file $dst \ + || die "failed to symlink $file" + done if test $with_gettext = yes; then - # Released autopoint has the tendency to install macros that have been - # obsoleted in current gnulib, so run this before gnulib-tool. - echo "$0: $AUTOPOINT --force" - $AUTOPOINT --force || exit - fi - - # Autoreconf runs aclocal before libtoolize, which causes spurious - # warnings if the initial aclocal is confused by the libtoolized - # (or worse out-of-date) macro directory. - # libtoolize 1.9b added the --install option; but we support back - # to libtoolize 1.5.22, where the install action was default. - if test $use_libtool = 1; then - install= - case $($LIBTOOLIZE --help) in - *--install*) install=--install ;; - esac - echo "running: $LIBTOOLIZE $install --copy" - $LIBTOOLIZE $install --copy - fi - - find . ! -type d -print | sort >$tempbase.1 - old_IFS=$IFS - IFS=$nl - for file in $(comm -13 $tempbase.0 $tempbase.1); do - IFS=$old_IFS - parent=${file%/*} - version_controlled_file "$parent" "$file" || { - for dot_ig in x $vc_ignore; do - test $dot_ig = x && continue - ig=$parent/$dot_ig - insert_vc_ignore "$ig" "${file##*/}" - done - } - done - IFS=$old_IFS - - rm -f $tempbase.0 $tempbase.1 - trap - 1 2 13 15 -fi - -# Import from gnulib. - -gnulib_tool_options="\ - --import\ - --no-changelog\ - --aux-dir $build_aux\ - --doc-base $doc_base\ - --lib $gnulib_name\ - --m4-base $m4_base/\ - --source-base $source_base/\ - --tests-base $tests_base\ - --local-dir $local_gl_dir\ - $gnulib_tool_option_extras\ -" -if test $use_libtool = 1; then - case "$gnulib_tool_options " in - *' --libtool '*) ;; - *) gnulib_tool_options="$gnulib_tool_options --libtool" ;; - esac -fi -echo "$0: $gnulib_tool $gnulib_tool_options --import ..." -$gnulib_tool $gnulib_tool_options --import $gnulib_modules && - -for file in $gnulib_files; do - symlink_to_dir "$GNULIB_SRCDIR" $file \ - || die "failed to symlink $file" -done - -bootstrap_post_import_hook \ - || die "bootstrap_post_import_hook failed" - -# Remove any dangling symlink matching "*.m4" or "*.[ch]" in some -# gnulib-populated directories. Such .m4 files would cause aclocal to fail. -# The following requires GNU find 4.2.3 or newer. Considering the usual -# portability constraints of this script, that may seem a very demanding -# requirement, but it should be ok. Ignore any failure, which is fine, -# since this is only a convenience to help developers avoid the relatively -# unusual case in which a symlinked-to .m4 file is git-removed from gnulib -# between successive runs of this script. -find "$m4_base" "$source_base" \ - -depth \( -name '*.m4' -o -name '*.[ch]' \) \ - -type l -xtype l -delete > /dev/null 2>&1 - -# Invoke autoreconf with --force --install to ensure upgrades of tools -# such as ylwrap. -AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS" - -# Some systems (RHEL 5) are using ancient autotools, for which the -# --no-recursive option had not been invented. Detect that lack and -# omit the option when it's not supported. FIXME in 2017: remove this -# hack when RHEL 5 autotools are updated, or when they become irrelevant. -case $($AUTORECONF --help) in - *--no-recursive*) AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive";; -esac - -# Tell autoreconf not to invoke autopoint or libtoolize; they were run above. -echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS" -AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \ - || die "autoreconf failed" - -# Get some extra files from gnulib, overriding existing files. -for file in $gnulib_extra_files; do - case $file in - */INSTALL) dst=INSTALL;; - build-aux/*) dst=$build_aux/${file#build-aux/};; - *) dst=$file;; - esac - symlink_to_dir "$GNULIB_SRCDIR" $file $dst \ - || die "failed to symlink $file" -done - -if test $with_gettext = yes; then - # Create gettext configuration. - echo "$0: Creating po/Makevars from po/Makevars.template ..." - rm -f po/Makevars - sed ' - /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/ - /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/ - /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'| - /^XGETTEXT_OPTIONS *=/{ - s/$/ \\/ - a\ - '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+} - } - ' po/Makevars.template >po/Makevars \ - || die 'cannot generate po/Makevars' - - # If the 'gettext' module is in use, grab the latest Makefile.in.in. - # If only the 'gettext-h' module is in use, assume autopoint already - # put the correct version of this file into place. - case $gnulib_modules in - *gettext-h*) ;; - *gettext*) - cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \ - || die "cannot create po/Makefile.in.in" - ;; - esac - - if test -d runtime-po; then - # Similarly for runtime-po/Makevars, but not quite the same. - rm -f runtime-po/Makevars + # Create gettext configuration. + echo "$0: Creating po/Makevars from po/Makevars.template ..." + rm -f po/Makevars sed ' - /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/ - /^subdir *=.*/s/=.*/= runtime-po/ - /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/ + /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/ + /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/ + /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'| /^XGETTEXT_OPTIONS *=/{ s/$/ \\/ a\ - '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+} + '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+} } - ' po/Makevars.template >runtime-po/Makevars \ - || die 'cannot generate runtime-po/Makevars' + ' po/Makevars.template >po/Makevars \ + || die 'cannot generate po/Makevars' - # Copy identical files from po to runtime-po. - (cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po) + # If the 'gettext' module is in use, grab the latest Makefile.in.in. + # If only the 'gettext-h' module is in use, assume autopoint already + # put the correct version of this file into place. + case $gnulib_modules in + *gettext-h*) ;; + *gettext*) + cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \ + || die "cannot create po/Makefile.in.in" + ;; + esac + + if test -d runtime-po; then + # Similarly for runtime-po/Makevars, but not quite the same. + rm -f runtime-po/Makevars + sed ' + /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/ + /^subdir *=.*/s/=.*/= runtime-po/ + /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/ + /^XGETTEXT_OPTIONS *=/{ + s/$/ \\/ + a\ + '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+} + } + ' po/Makevars.template >runtime-po/Makevars \ + || die 'cannot generate runtime-po/Makevars' + + # Copy identical files from po to runtime-po. + cp -p po/Makefile.in.in po/*-quot po/*.header po/*.sed po/*.sin runtime-po + fi fi -fi -bootstrap_epilogue + bootstrap_epilogue -echo "$0: done. Now you can run './configure'." + echo "$0: done. Now you can run './configure'." +} -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" +# ---------------------------------------------------------------------------- + +# Local Variables: +# eval: (add-hook 'before-save-hook 'time-stamp nil t) +# time-stamp-start: "scriptlibversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: + +usage() { + cat <. +# along with this program. If not, see . # gnulib modules used by this package. @@ -29,8 +29,8 @@ gnulib_modules=" getopt-gnu inet_pton isnan - langinfo netinet_in + nstrftime progname quote realloc-gnu @@ -40,8 +40,9 @@ gnulib_modules=" stdlib stpncpy strdup-posix - strftime strstr + strtod + time_r xalloc " @@ -63,7 +64,7 @@ XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\ gettext_external=0 grep '^[ ]*AM_GNU_GETTEXT(external\>' configure.ac > /dev/null && gettext_external=1 -grep '^[ ]*AM_GNU_GETTEXT(\[external\]' configure.ac > /dev/null && +grep '^[ ]*AM_GNU_GETTEXT(\[external]' configure.ac > /dev/null && gettext_external=1 if test $gettext_external = 1; then diff --git a/build-aux/.gitignore b/build-aux/.gitignore index a85ce13..1d51c02 100644 --- a/build-aux/.gitignore +++ b/build-aux/.gitignore @@ -1,6 +1,7 @@ /ar-lib /compile /config.guess +/config.rpath /config.sub /depcomp /install-sh diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen index 3468247..a44f5d5 100755 --- a/build-aux/git-version-gen +++ b/build-aux/git-version-gen @@ -1,12 +1,12 @@ #!/bin/sh # Print a version string. -scriptversion=2012-12-31.23; # UTC +scriptversion=2022-01-27.18; # UTC -# Copyright (C) 2007-2013 Free Software Foundation, Inc. +# Copyright (C) 2007-2022 Free Software Foundation, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or +# 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, @@ -15,9 +15,9 @@ scriptversion=2012-12-31.23; # UTC # 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 . +# along with this program. If not, see . -# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# This script is derived from GIT-VERSION-GEN from GIT: https://git-scm.com/. # It may be run two ways: # - from a git repository in which the "git describe" command below # produces useful output (thus requiring at least one signed tag) @@ -65,19 +65,21 @@ scriptversion=2012-12-31.23; # UTC # EXTRA_DIST = $(top_srcdir)/.version # BUILT_SOURCES = $(top_srcdir)/.version # $(top_srcdir)/.version: -# echo $(VERSION) > $@-t && mv $@-t $@ +# echo '$(VERSION)' > $@-t +# mv $@-t $@ # dist-hook: -# echo $(VERSION) > $(distdir)/.tarball-version +# echo '$(VERSION)' > $(distdir)/.tarball-version me=$0 +year=`expr "$scriptversion" : '\([^-]*\)'` version="git-version-gen $scriptversion -Copyright 2011 Free Software Foundation, Inc. -There is NO warranty. You may redistribute this software -under the terms of the GNU General Public License. -For more information about these matters, see the files named COPYING." +Copyright (C) ${year} Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later . +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law." usage="\ Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT] @@ -85,8 +87,9 @@ Print a version string. Options: - --prefix prefix of git tags (default 'v') - --fallback fallback version to use if \"git --version\" fails + --prefix PREFIX prefix of git tags (default 'v') + --fallback VERSION + fallback version to use if \"git --version\" fails --help display this help and exit --version output version information and exit @@ -100,8 +103,8 @@ while test $# -gt 0; do case $1 in --help) echo "$usage"; exit 0;; --version) echo "$version"; exit 0;; - --prefix) shift; prefix="$1";; - --fallback) shift; fallback="$1";; + --prefix) shift; prefix=${1?};; + --fallback) shift; fallback=${1?};; -*) echo "$0: Unknown option '$1'." >&2 echo "$0: Try '--help' for more information." >&2 @@ -140,11 +143,9 @@ then v=`cat $tarball_version_file` || v= case $v in *$nl*) v= ;; # reject multi-line output - [0-9]*) ;; - *) v= ;; esac test "x$v" = x \ - && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2 + && echo "$0: WARNING: $tarball_version_file is damaged" 1>&2 fi if test "x$v" != x @@ -166,9 +167,10 @@ then # tag or the previous older version that did not? # Newer: v6.10-77-g0f8faeb # Older: v6.10-g0f8faeb - case $v in - *-*-*) : git describe is okay three part flavor ;; - *-*) + vprefix=`expr "X$v" : 'X\(.*\)-g[^-]*$'` || vprefix=$v + case $vprefix in + *-*) : git describe is probably okay three part flavor ;; + *) : git describe is older two part flavor # Recreate the number of commits and rewrite such that the # result is the same as if we were using the newer version @@ -183,9 +185,9 @@ then ;; esac - # Change the first '-' to a '.', so version-comparing tools work properly. - # Remove the "g" in git describe's output string, to save a byte. - v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; + # Change the penultimate "-" to ".", for version-comparing tools. + # Remove the "g" to save a byte. + v=`echo "$v" | sed 's/-\([^-]*\)-g\([^-]*\)$/.\1-\2/'`; v_from_git=1 elif test "x$fallback" = x || git --version >/dev/null 2>&1; then v=UNKNOWN @@ -199,7 +201,7 @@ v=`echo "$v" |sed "s/^$prefix//"` # string we're using came from git. I.e., skip the test if it's "UNKNOWN" # or if it came from .tarball-version. if test "x$v_from_git" != x; then - # Don't declare a version "dirty" merely because a time stamp has changed. + # Don't declare a version "dirty" merely because a timestamp has changed. git update-index --refresh > /dev/null 2>&1 dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty= @@ -214,12 +216,12 @@ if test "x$v_from_git" != x; then fi # Omit the trailing newline, so that m4_esyscmd can use the result directly. -echo "$v" | tr -d "$nl" +printf %s "$v" # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/configure.ac b/configure.ac index ec993c7..818bee9 100644 --- a/configure.ac +++ b/configure.ac @@ -2,11 +2,11 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_MACRO_DIRS([m4]) AC_INIT([dhcpd-pools], [m4_esyscmd([build-aux/git-version-gen .tarball-version])], [kerolasa@iki.fi],[], - [http://dhcpd-pools.sourceforge.net/]) + [https://dhcpd-pools.sourceforge.net/]) PACKAGE_MAINTAINER="Sami Kerola" AC_SUBST([PACKAGE_MAINTAINER]) AC_CONFIG_AUX_DIR([build-aux]) @@ -24,6 +24,9 @@ AM_INIT_AUTOMAKE([ AC_CONFIG_SRCDIR([src/dhcpd-pools.h]) AC_CONFIG_HEADERS([config.h]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])], + [AC_SUBST([AM_DEFAULT_VERBOSITY], [1])]) + # Checks for programs AC_USE_SYSTEM_EXTENSIONS AC_C_RESTRICT @@ -66,8 +69,24 @@ AC_TYPE_UINT32_T AC_FUNC_ERROR_AT_LINE AC_CHECK_FUNCS([\ __fpending \ + open_memstream \ posix_fadvise \ ]) +AC_CHECK_DECL([strndupa]) + +AC_CHECK_FUNCS([fpclassify], [], + [AC_CHECK_LIB([m], [fpclassify], [MATH_LIBS="-lm"])] + [AC_CHECK_LIB([m], [__fpclassify], [MATH_LIBS="-lm"])] +) +AC_SUBST([MATH_LIBS]) + +AS_IF([test "x$ac_cv_func_open_memstream" = "xyes" && test "x$ac_cv_have_decl_strndupa" == "xyes"], [ + build_mustach=yes + AC_DEFINE([BUILD_MUSTACH], [1], [build mustach support]) +], [ + build_mustach=no +]) +AM_CONDITIONAL([ENABLE_MUSTACH], [test "x$build_mustach" = xyes]) AC_MSG_CHECKING([if the compiler supports __builtin_expect]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[ diff --git a/contrib/.gitignore b/contrib/.gitignore index 591c7bd..f28c483 100644 --- a/contrib/.gitignore +++ b/contrib/.gitignore @@ -1,2 +1 @@ -/dhcpd-pools.cgi /nagios.conf diff --git a/contrib/Makemodule.am b/contrib/Makemodule.am index a6bbc1e..289c7f9 100644 --- a/contrib/Makemodule.am +++ b/contrib/Makemodule.am @@ -1,5 +1,5 @@ contribdir = $(datadir)/dhcpd-pools/ -PATHFILES += contrib/dhcpd-pools.cgi contrib/nagios.conf -dist_contrib_SCRIPTS = contrib/dhcpd-pools.cgi contrib/snmptest.pl +PATHFILES += contrib/nagios.conf +dist_contrib_SCRIPTS = contrib/snmptest.pl dist_contrib_DATA = contrib/nagios.conf EXTRA_DIST += contrib/munin_plugins diff --git a/contrib/PKGBUILD b/contrib/PKGBUILD new file mode 100644 index 0000000..08770a4 --- /dev/null +++ b/contrib/PKGBUILD @@ -0,0 +1,36 @@ +# Archlinux package file. Just download this file, and +# makepkg PKGBUILD +# pacman -U ./dhcpd-pools*.pkg.tar.xz + +pkgname=dhcpd-pools +pkgver=0 +pkgrel=1 +pkgdesc="ISC dhcpd lease status utility" +arch=('i686' 'x86_64') +url=https://dhcpd-pools.sourceforge.net/ +license=('BSD') +depends=('pacman') +makedepends=('uthash' 'git') +source=("$pkgname"::'git://git.code.sf.net/p/dhcpd-pools/code') +md5sums=('SKIP') + +pkgver() { + cd "$srcdir/$pkgname" + # Use the tag of the last commit + git describe --long | sed -E 's/([^-]*-g)/r\1/;s/-/./g' +} + +build() { + cd "$srcdir/$pkgname" + ./bootstrap + ./configure \ + --prefix=/usr \ + --bindir=/usr/bin + make +} + +package() { + cd "$srcdir/$pkgname" + make PREFIX=/ DESTDIR="$pkgdir" install + install -D -m644 COPYING "${pkgdir}/usr/share/licenses/${pkgname}/COPYING" +} diff --git a/contrib/dhcpd-pools.cgi.in b/contrib/dhcpd-pools.cgi.in deleted file mode 100755 index 117a7cf..0000000 --- a/contrib/dhcpd-pools.cgi.in +++ /dev/null @@ -1,34 +0,0 @@ -#!@SHELL@ -# -# Simple CGI for dhcpd-pools. - -echo Content-type: text/html -echo - -# To make lease table more fancy use CSS definition something -# like this in your style.css file. -# -# TABLE.dhcpd-pools { -# border-style : groove; -# margin-left : 2px; -# foo : bar; -# } -# -# http://www.w3.org/TR/REC-CSS2/tables.html -# -# And uncomment this line. -# -#echo - -echo "" -echo "" -echo "

This was situation at " -date -echo "

" - -@bindir@/dhcpd-pools --format html - -echo "" -echo "" - -# EOF diff --git a/contrib/kerolasa.gpg b/contrib/kerolasa.gpg index 47d1ed6..97a480d 100644 --- a/contrib/kerolasa.gpg +++ b/contrib/kerolasa.gpg @@ -1,5 +1,4 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v2.0.22 (GNU/Linux) mQINBFLH/jcBEADNgkgZAr7b/qvHzNkme8eSplDA8UHhcCFQmBnQ8HYFEYBi1Y5a GhICcuisRSuZIPLtqRF8ntoOXsOHOUxnn5hVR62HxMLgOC1PQMw8TzREobbkAxMp @@ -12,41 +11,101 @@ v8Bj+uLXc4JT9kTB5E8DT2ZR+61E4eT/zk4rpZ5j6tTktLNaSG58VYc+oQlPkPsB ntraDsORpa5cK9uPUbHIHN1qd4gi35O4UDSqS023XnStQqsUD4vlzICedPDypn28 h9q2nlNQHSDjIRnZFD+z9IHfVoBNTfm5/UH7NKBAvSS6rP5zsgi2fittt5AFdbmQ VBOtpYp5vbcdSt8gNdJRy8FRaiQ/2n74xF1Zso/PGni1xRmseS+qBGI95QARAQAB -tDtTYW1pIEtlcm9sYSAoaHR0cDovL3d3dy5pa2kuZmkva2Vyb2xhc2EvKSA8a2Vy -b2xhc2FAaWtpLmZpPokCPwQTAQIAKQUCUsf+NwIbAwUJCWYBgAcLCQgHAwIBBhUI -AgkKCwQWAgMBAh4BAheAAAoJEKlVMkX96bc5aBwP/3XE1ZW3pp6B337vW5VfPY8v -+8h2JMdL/PfCLqDHbQm6fYgtT+EFbVE3JotXcocSqvl3xjA2LERROoN9a+jdaS9O -+WDWdIxVjnpMheb502ZnTbwFuh1OVasvqT7x4e9Tmq31S/0VFjTFLm8M+anuoUsn -9lfy5jeGnULMT0G737uySmm8M7hP5lSXHhFaiQi2R95wo8titvZ9ckGFJH3/u5qV -8aU34yzIw3MnKG52FeieYjSVfEaso9FrO8thMFIJclHSGzd5fw9CbPggFzMR0zHc -wJl+AEshAIG13+P88wcJ4sqfZGltdjm0vSgcmWgkmrZyDhvyA4Hnb2Rc3faqh3VC -/qU7p82yhoJ9M12xz4PIXQbodPDu/q8Ckzt8zh6h8ROUkqqZ90M2Yc3QoX82qOzR -tMokbhhwtYcgyAZHw0CLvJuJvAME41zjH1Wq9wY+gXHewTppi9Yqbd8E1LJaCvr7 -pMhKr9f08lBoUD5G/M54HGMKW+1R0yvxR9C2lR8P5JfVfTmD9E/g8WbmzZuy1dm1 -sL3fzjmnc3yNNt9MgfJgHQu2W15myNAzK/sEXGjuXJSqn/dpCoxiqQooEc2qokN/ -wQp5g70We305+BZB1fQcjE7wBmF4OEtEcI3p4lSrffyxcsqFFds0ikDU024lA46D -V1nSGsKLk7fme3itszWmuQINBFLH/jcBEADGsDuJyhEGExcInkb4Zy8HlSNaT0nF -xpQK+tI5RlqyC11TnxdUDCQwPPRh/hdNs7RDQ6lHhmXydD04+Sjjn0nkQCxd8TlB -TwOU6BV4vPF7BonskTMHb6mXxOo+6f7+J5q4beQ7XzRSoFqLZ0kiump7B4m6L4WS -qZmcfi8f6EOQZHK1HsTB2jE+cwaPm7vNeNaKXYM6hHojZ7XnDavv4vMxfLRGGx5e -6wNcZexD4t4pkMwmA+A576t5CnIm8JVB2HLj2YZN6DfIfYQ9EcMYSYVLg/EuivPa -MZIoIa9ki5TbeQDANluJqAG+i+CSoIfJX1V1p0WoeSEKJnGBhVFmajU+nek1lCqe -M/VXQF4dWbFCQm8/eKl1M9PY77dwT90xRZDeigM6o6CoB+6rfePAnwsmtA21LyQ9 -WMTCH9qii9SvlWkfCvLK3HnC/2d1ctwsF8yoc6KF+Qwj7RV23/iHYqjxB8OcOAyH -uv+k0sD9o0MLlzAExOftLPkwyWS8ePs7rJVLSA42N3diUYx5TO3NzmU8ifn4hGxb -F9J/sIYQUfCxH9V+DBe2vhVSZ/bQKpVUZTE8KxINL32JvTR3HbEwHKIe0xIFnDOM -GrWP7icPEsaSKj/0vEq/Is/R2nJWfD0Vzs7vil9zYU16vHQtZrv/5SzavzDRFGxL -m7/jDfrzMuNRdQARAQABiQIlBBgBAgAPBQJSx/43AhsMBQkJZgGAAAoJEKlVMkX9 -6bc5cYEQAI8xVRdoza/MY9KFJCRiobii4GjxAJqIXXvbY8mM7rvmiLnzfr5msLzS -op7Epse6tkI8QvYXbY+EjXke27J8rM/zqXyqdKJfcqKVzq6fIQyVVsz9o0hwvuSj -kcSgCV2b8XHKbNygSnNh2VJ5YY/zL2x5tsySa3tLukh46ydKWRQqOSMIxMT0TnCO -iTMr5INwGnX68rB0kEuCSUdkzDuyulW2T0oN7Yx4ASgrJixwSu/UNMO/fXvkG0NY -IWeXQcnn0C8zQv/PAfzJ9O+24Md4m6tzVuDpblSRB6E8xQhusysRc4BEHB/G3Ege -3JR/tumT6solJD7pfFDYkC9rAKhTKK9h1b2VpEGxN9o8qwwjtNljLakEYXuWNpCm -ZxKAR4Agg3UY8CGmgsTKyjsfB9iO2+LXCS9uQMAtIHbnr++mPASBEKD6rP8QIyf1 -2fbI9JRNGWcXQVZBGXWmu4UaKOrXBTVzooj2yG7kZmKO0FoIaCqqTUs2/KhnFj2E -Fvym2j1OMrYxOM/pT6w3cv3CwGb1ILOX0qrTQiEpOExSMa21Mi4678jXt6F6NPWG -/lwlgE1WM9i9jIcua+NU5ZJiqQ/rjuSRMB/WFrQy66/jb9wWNp/G6DXrANgdgoC9 -Dq5oviB+dqSXlU5b0atG4ru506mXCHKmOC+wu0oRlGQ/X6iuuDfS -=U+eW +tB1TYW1pIEtlcm9sYSA8a2Vyb2xhc2FAaWtpLmZpPokCcwQTAQgAXQIbAwULCQgH +AgYVCAkKCwIEFgIDAQIeAQIXgAIZARsYaHR0cDovL2h0dHAta2V5cy5nbnVwZy5u +ZXQWIQSO05bjfjjUcaAFMNOpVTJF/em3OQUCWb42mgUJGcI7YwAKCRCpVTJF/em3 +OWNBD/4/N0OO6cA3bmEAum5qmHdcJVcUor9ClY3oY2QWdTyDCmSJLcTZUNsTrYYA +hY5vQsDxG6ZaKiITvhwp9y4PdJmnqqzRfnqnXusSVEsqG3vGHIVlpQj8Vgurr68d +1x2buHGRvRrjBMWR/Ro1TUeNOmM4HStcvjXRTWlHk9VXi9nk+uMQY9n1lXYNChda +0usj7q5QXXFXbOukKrvEZzt4I1tGbSEv7bBIghr5fMAJg8ZqAMDwfqsUKSM38HF4 +leCUgMZS7XuXuEb35ImjR1VYYCb+L2Q/PFqDOEGQpUwyLOzXcU9TaIDjMhNQY19y +DTUjW1YdQtf+3XmQFa4X1Od0jP1PiVJhw1LNCQLOENs+SRqT9s0W8+nP3qjso/LR +T3vR3FcGSw6KrESMFvmwY7EEYGvOpEgaIv8sIJl7un4BoWHYq/2D6jDHK/YmbpgG +J2FIvZZ4vVYixqgrqCh54USpwTaKd4KQpdm4zZMJ2yIznR6+gXxTi2TyUwKFV67y +RY6o5n3jusWuRejB1BI2eXRbFSeZn6IxAG8QLlLHu1R/XBB1dYDkbtYZ5XuAJRTH +qjCL+s/4rjJIUj02dmmtiyObN3bs/48DTQsDqgCYbA2wmbBjT9lPmRWfncBSlqbx +XSRwyXnDql+UIi6wphBdESn5TNVJjWxYQ16ZRYoZlz4tV2bPl4kCHAQQAQoABgUC +WCOB1QAKCRCB/dUuZfDuelcKD/4iggO8sgYYmTf63y2LJhG7e2d3K/cbQ04Pn/iF +b6zBa5kCsLAWHJmWBPlPkQB3HjZ+j1OSs4sHvwuotuVde2cbtzEvuphQRamFSU83 +lakXwXrk00IU7oVEz1Yo9c7IB6pOY0uDgQMIp/Vitm9pomZXAV3S1bYciYuvXdJC +6SaUbyrHzkVgxEV7TSK2XUgMFGnBCIUhqxNA+chkGg6KS715XH8MJyb1Izinsjux +Q07+tquoiYqRhSntTc+bJ83OpSB8bPlqP1FZdc+ypZjq8OEpFIZK8QtK+849vzYw +vORq19mwFAY9y6XZip7YZzSGzn7Q4v3XfVJlIwGrkHSnUkqOmQXGdoSHAiwX3WhU +nPtpYFZQoFFQjm0SsgdPWbqJmmLj2MjHUFbKzl9PVa0INnwp6RxfzNZjU6OxphoI +ocw+2hmXv/lHzb+cwFf8Nae0WOJkCoGwxk9mYA0omhuKJ/WWVvauu+uaswBkeX6O +1Yf1jSrWgK5u+jbDcmZj0kFL0zdB/pBxshngxCA3efPd1e2dJGcPlaoqkwZAtEmG +hwWDj9Gk5IhSX1/Wpv+ke2wprUPzwv7Z5loXroLub2R29FWyHzymwlnts5hSg7kR +1PzxD3iOHFgmnWcBCchf8UWmAjrutZQJZWLTDkzlC6Vj8rkJcgdb0v14zvYVJTWs +Q/hMDYkCHAQSAQoABgUCWCN/ZwAKCRCRAl9JYWN/KLmND/9k8ydscqaMW86NswOC +drJDXuOTDvJJlnLYhgO1+jKQizbHQqnszIVk7KvXY2/K++TdTNdG/+/LosvtIRKb +2DEvV2hPZhc3UxZ2Gnx9ocqjSCfmd9er9EY34WGLtr57eQnu93CJGe10F3qg4rr3 +8zsgQcLXomvKnB2J5oRWBla01DXZhum1uIbehrqDmBUz9YxAIz9adY0AjDyLNvAB +IghkjobLHWhouQaTV1IT6Koixdrj6eKekjRojWVZ7Po5dcUe525NlA1yBJhI9VDF +TLy6DHEwmJJuAQqDpyRILzSDsyQ0B+wBgpGks5reCDZlwAjogLy0O6nV2qYKfVXK +ziHeXPvzLPEfSmkA0ZutLXnJBLNfod4zELLJrPJA27dDdQO4yImoAjjTklyBpHXy +Fp3r5RlDq5yA9VpNkC58KJQZoiplkUNBaQTjYgcYX4LDaU4bEq++CUUEnisIyqPO +KqJPJAvmlm1d2vOMxhzcjxt3xBAvDZ8ibRIXlV296Y56SEBe5UZhyUqpAQBa0zsp +oQm9MdkqoC+IDBTRVnbGSxsw0GiQRpAJyNOlkTzP6z6ySmRIQ1hH1TqXx7TChPqP +tcLHLOnRTY9/v5Kt8APa+qgn/0UVgGcetkarqXNdtq+zD1x9C0DFNURBl0uH/E1E +Dtyx4xI6xCY3jzv0Cb1y9Afrc7Q7U2FtaSBLZXJvbGEgKGh0dHA6Ly93d3cuaWtp +LmZpL2tlcm9sYXNhLykgPGtlcm9sYXNhQGlraS5maT6JAlYEEwECAEACGwMHCwkI +BwMCAQYVCAIJCgsEFgIDAQIeAQIXgBYhBI7TluN+ONRxoAUw06lVMkX96bc5BQJZ +vjaaBQkZwjtjAAoJEKlVMkX96bc58KYP/jvpHWmoKKFzqClLdDkN8tBo30GgHGoh +WKHbgW859mi/ZcYzTxlx6Dm88QwV5DRulgqAHvLTippncR3LTpL3Ys86ZDoIskF9 +BnWOm/bC+Czl9mDL6BNfFEWWnfeGFsCaMq/Jx8nj8i2o0ezhbTjCeGeng4quWK1D +T6G8Kvnk7Dc+i0n20LUQf2WRCLrtJEl/PMElc+11X/jLm9ypjE/jr24xiNsZck45 +4rdMsdkZqgbSHWNIHyxS66mWmD1vbHp3kq0ic09ISCx/Xc7OQpv7V0hN+lxE+AvA +3Mjp2i7A3ntwUbPwmZkdiWUrBqU4CYee0S4fbgroUjNQ/JhGZmCku5YbcyAg4KrP +quXEftY0HP3qQMMElcwna5ljDQdC9NGuSWkH0OKZsuXlgtIz583wjNTKupzii+jo +AJcNnbdRR4i/U7cI+/ySluTp+Az/odmTNzssGnlsXOSGYWk4o6ruKvU4HzUauMEO +XPrTvI7IlH/GIHvwrOcXAyAU90M0qppC4YjXK8oCawzTnbQVQsbedwoNaEPaD4Bz +N4ka9PtvgKnBzODvOeDsTz1O6vTomttW2C6nficc/PXQvxDkZ3NeJw3eGiiAQxeA +w2Ps3+oIasyQZZVLlvu8dfE2BsPArggPLNgd6bLk3yk/gQGR0D+/TyHuCgPM1nZt +mBG3utrXfMz3iQIcBBABCgAGBQJYI4HVAAoJEIH91S5l8O56YhYQAIfxcRP31w+6 +htYZO5dGS1zF1CReY8VO/AyBa59YJHAlFSbOhqHQY1SAYD+H1IZhv/JCvQScZhyT +giI3KPP1RAPUa1xGZrf4hwl7Yu8ZpFn1Y0Gxd4bj2i9HWJcd1A+ci4kc+dU4rPEB +ac2U06vcIyuyCSjuK2RdYCyd7pQRkoUtZxWen6sxi1khsw3Qucfbr5QZyh/roA4i +hTb3VlFFIiCRcZOAqSYml9a+kbDxpgswbdCE3EMHVYBWtFwWImZOhnsun148H7EA ++b+hAcbkoNpXJOfC8q/EM0qm6C1GhryXe1voLxfeMdKKjXM78dc0z3L/Vz0E+stD +qrT7CG1jLSk/rIjSvRhbnEX/ZTKa+19ytht0B2zxkokjevvzw6cXSM5JjG8Q9k7R +PcPOTS0t7MSwRqCEFvhvswH3JYy0SH0U2nKbPmkY1Awh6BZNyGpf3dxHV648RVZv +lrXUbXv1azD8U+llaMkVx0PTbej0RcRHQM6B5zJ0qu1MC64V/v2uUK3MNl38JdFa +wkB494uf024FRIfJlQ8+WYrsGlHbUBZZsWzsyrPaeaqL0Xcy+hOc7wu7cZPxkqL8 +71O3jbq2dFkBWA4ANsiRdJTh14qoen4YVdc9gjq6HsuFyu0gauDktLaLUHVCX27e +kWThHNxeNoGkcKKHoQdHrN8sII8C1q6fiQIcBBIBCgAGBQJYI39nAAoJEJECX0lh +Y38oIbEP/165gMnaD+g7cfGsHCHQk9/ZrE+4iXfZSwxlhaJ4kK88Ad/tPvoiOxmS +NaUIfH/koGBQNeE/OjjIkPiGlsldg9//4mGmSglM4jLeQBG0QuTLo30aNCl9xU6q +rv9PV/ROfjRUF5NxCAiDdA24uBZ8CftdK5sglD6fazeINQyNk5Wm8e7Jk9PRzIuK +jnZRyaGJl3egx5smWnCZZ8QfjsYXDzbp1uDavmF7Qczp+xbzBhdh6VdSZ0VwvnPr +kN3CzGiR3AuG982R41UGZQ3a+3d38QnmX8cdT1yCqJLrSh4+AJbuGrndOqxBhTN0 +VQBIGHteZ6ezxDHs4kVBwfSc3XOzNimJKFVxWCJDXZQojKawu7PaDPYGxU9OWAjs +OWtJFTGqeEiBDQbnLwncT5l/I1O1zv9MIFhQ4MgJeRbvG/QjuLrP6RoIMGZLkX6K +o35dZ6gO/XAdhJmOhfon6Q5/RPdB11e/UkM58xZMbQLGJ3Gm9vjz039vwMabMjXf +28UE3xKzdzDg8Fb3ykNtIQSFNr/cf5YBZ7KMtfJ6ugExmJQQdWxZLQpHoNNCmknt +OHOajlB628N3J0PKlwPjjt5KveBO2ur/yAly6SSv2Oi/astGscgrJtIyPFKMmKar +gPzEnPv4DfLbHnskHkyzVBrQE16Pmcfx4fAe2LeDIO8KJMqSxKOouQINBFLH/jcB +EADGsDuJyhEGExcInkb4Zy8HlSNaT0nFxpQK+tI5RlqyC11TnxdUDCQwPPRh/hdN +s7RDQ6lHhmXydD04+Sjjn0nkQCxd8TlBTwOU6BV4vPF7BonskTMHb6mXxOo+6f7+ +J5q4beQ7XzRSoFqLZ0kiump7B4m6L4WSqZmcfi8f6EOQZHK1HsTB2jE+cwaPm7vN +eNaKXYM6hHojZ7XnDavv4vMxfLRGGx5e6wNcZexD4t4pkMwmA+A576t5CnIm8JVB +2HLj2YZN6DfIfYQ9EcMYSYVLg/EuivPaMZIoIa9ki5TbeQDANluJqAG+i+CSoIfJ +X1V1p0WoeSEKJnGBhVFmajU+nek1lCqeM/VXQF4dWbFCQm8/eKl1M9PY77dwT90x +RZDeigM6o6CoB+6rfePAnwsmtA21LyQ9WMTCH9qii9SvlWkfCvLK3HnC/2d1ctws +F8yoc6KF+Qwj7RV23/iHYqjxB8OcOAyHuv+k0sD9o0MLlzAExOftLPkwyWS8ePs7 +rJVLSA42N3diUYx5TO3NzmU8ifn4hGxbF9J/sIYQUfCxH9V+DBe2vhVSZ/bQKpVU +ZTE8KxINL32JvTR3HbEwHKIe0xIFnDOMGrWP7icPEsaSKj/0vEq/Is/R2nJWfD0V +zs7vil9zYU16vHQtZrv/5SzavzDRFGxLm7/jDfrzMuNRdQARAQABiQIlBBgBAgAP +BQJSx/43AhsMBQkJZgGAAAoJEKlVMkX96bc5cYEQAI8xVRdoza/MY9KFJCRiobii +4GjxAJqIXXvbY8mM7rvmiLnzfr5msLzSop7Epse6tkI8QvYXbY+EjXke27J8rM/z +qXyqdKJfcqKVzq6fIQyVVsz9o0hwvuSjkcSgCV2b8XHKbNygSnNh2VJ5YY/zL2x5 +tsySa3tLukh46ydKWRQqOSMIxMT0TnCOiTMr5INwGnX68rB0kEuCSUdkzDuyulW2 +T0oN7Yx4ASgrJixwSu/UNMO/fXvkG0NYIWeXQcnn0C8zQv/PAfzJ9O+24Md4m6tz +VuDpblSRB6E8xQhusysRc4BEHB/G3Ege3JR/tumT6solJD7pfFDYkC9rAKhTKK9h +1b2VpEGxN9o8qwwjtNljLakEYXuWNpCmZxKAR4Agg3UY8CGmgsTKyjsfB9iO2+LX +CS9uQMAtIHbnr++mPASBEKD6rP8QIyf12fbI9JRNGWcXQVZBGXWmu4UaKOrXBTVz +ooj2yG7kZmKO0FoIaCqqTUs2/KhnFj2EFvym2j1OMrYxOM/pT6w3cv3CwGb1ILOX +0qrTQiEpOExSMa21Mi4678jXt6F6NPWG/lwlgE1WM9i9jIcua+NU5ZJiqQ/rjuSR +MB/WFrQy66/jb9wWNp/G6DXrANgdgoC9Dq5oviB+dqSXlU5b0atG4ru506mXCHKm +OC+wu0oRlGQ/X6iuuDfS +=fVwo -----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/rpm.spec b/contrib/rpm.spec new file mode 100644 index 0000000..46826d8 --- /dev/null +++ b/contrib/rpm.spec @@ -0,0 +1,4 @@ +Klaus Slott made a package that works for opensuse, and is +a good starting point for whom ever happens to need an rpm. + +https://build.opensuse.org/package/show/home:Mr_Manor/dhcpd-pools diff --git a/contrib/snmptest.pl b/contrib/snmptest.pl index c77b882..54093c7 100644 --- a/contrib/snmptest.pl +++ b/contrib/snmptest.pl @@ -23,9 +23,10 @@ # you set $dbg to 1 then output will be generated in /tmp. use strict; +use NetSNMP::OID; # Version info: -my $SNMPver = "snmp1.0"; +my $SNMPver = "snmp1.1"; my $DHCPver = "dhcp1.0"; my $VERSION = "$SNMPver/$DHCPver"; # @@ -193,7 +194,7 @@ sub ParseDataFile () { } close IN; if ($dbg) { - foreach (sort @validoidlist) { print DBG "ValidOID: $_\n"; } + foreach (sort Oidcmp @validoidlist) { print DBG "ValidOID: $_\n"; } } if ($dbg) { @@ -370,7 +371,7 @@ sub GetData ($) { @userquery = split (/\./, $userqueryoid); my $found = 0; - foreach (sort @validoidlist) { + foreach (sort Oidcmp @validoidlist) { $next = $_; print DBG "Comparing $userqueryoid vs. $_\n" if $dbg; @validoid = split (/\./, $_); @@ -432,6 +433,12 @@ sub Pong { $line = 0; } +sub Oidcmp { + my $oida = new NetSNMP::OID($a); + my $oidb = new NetSNMP::OID($b); + snmp_oid_compare($oida, $oidb); +} + ################################## START ################################## # # Main diff --git a/contrib/zabbix.txt b/contrib/zabbix.txt new file mode 100644 index 0000000..9ea279a --- /dev/null +++ b/contrib/zabbix.txt @@ -0,0 +1,4 @@ +Zabbix 5 template, and instructions how to use it, can be found from +Mathieu's repository. + +https://github.com/mmorier86/dhcpd-pools-zabbix-template diff --git a/doc/doxy.conf.in b/doc/doxy.conf.in index d64080e..776809f 100644 --- a/doc/doxy.conf.in +++ b/doc/doxy.conf.in @@ -1,4 +1,4 @@ -# Doxyfile 1.8.10 +# Doxyfile 1.8.13 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -51,7 +51,7 @@ PROJECT_BRIEF = "ISC dhcpd lease usage analyser" # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = +PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is @@ -118,7 +118,7 @@ REPEAT_BRIEF = YES # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. -ABBREVIATE_BRIEF = +ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief @@ -161,7 +161,7 @@ STRIP_FROM_PATH = src # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -228,13 +228,13 @@ TAB_SIZE = 4 # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. -ALIASES = +ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. -TCL_SUBST = +TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For @@ -242,7 +242,7 @@ TCL_SUBST = # members will be omitted, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored @@ -281,7 +281,7 @@ OPTIMIZE_OUTPUT_VHDL = NO # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. -EXTENSION_MAPPING = +EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable @@ -293,6 +293,15 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -512,7 +521,7 @@ CASE_SENSE_NAMES = YES # scope will be hidden. # The default value is: NO. -HIDE_SCOPE_NAMES = NO +HIDE_SCOPE_NAMES = YES # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to @@ -629,7 +638,7 @@ GENERATE_DEPRECATEDLIST= YES # sections, marked by \if ... \endif and \cond # ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the @@ -671,7 +680,7 @@ SHOW_NAMESPACES = YES # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated @@ -684,7 +693,7 @@ FILE_VERSION_FILTER = # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. -LAYOUT_FILE = +LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib @@ -694,7 +703,7 @@ LAYOUT_FILE = # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. -CITE_BIB_FILES = +CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages @@ -739,6 +748,12 @@ WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -753,7 +768,7 @@ WARN_FORMAT = "$file:$line: $text" # messages should be written. If left blank the output is written to standard # error (stderr). -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -787,8 +802,8 @@ INPUT_ENCODING = UTF-8 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, -# *.vhdl, *.ucf, *.qsf, *.as and *.js. +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. FILE_PATTERNS = *.c \ *.h \ @@ -824,7 +839,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -835,20 +850,20 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = +EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands @@ -861,7 +876,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -877,8 +892,12 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. -INPUT_FILTER = +INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the @@ -886,8 +905,12 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. -FILTER_PATTERNS = +FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for @@ -910,7 +933,7 @@ FILTER_SOURCE_PATTERNS = *.c \ # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1022,7 +1045,7 @@ COLS_IN_ALPHA_INDEX = 5 # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output @@ -1066,7 +1089,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1076,7 +1099,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1088,7 +1111,7 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets @@ -1101,7 +1124,7 @@ HTML_STYLESHEET = # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = +HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1111,7 +1134,7 @@ HTML_EXTRA_STYLESHEET = # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to @@ -1240,7 +1263,7 @@ GENERATE_HTMLHELP = NO # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = +CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, @@ -1248,7 +1271,7 @@ CHM_FILE = # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -HHC_LOCATION = +HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). @@ -1261,7 +1284,7 @@ GENERATE_CHI = NO # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it @@ -1292,7 +1315,7 @@ GENERATE_QHP = NO # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. -QCH_FILE = +QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace @@ -1317,7 +1340,7 @@ QHP_VIRTUAL_FOLDER = doc # filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom @@ -1325,21 +1348,21 @@ QHP_CUST_FILTER_NAME = # filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = +QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To @@ -1472,7 +1495,7 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_EXTENSIONS = +MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site @@ -1480,7 +1503,7 @@ MATHJAX_EXTENSIONS = # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_CODEFILE = +MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and @@ -1540,7 +1563,7 @@ EXTERNAL_SEARCH = NO # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. -SEARCHENGINE_URL = +SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the @@ -1556,7 +1579,7 @@ SEARCHDATA_FILE = searchdata.xml # projects and redirect the results back to the right project. # This tag requires that the tag SEARCHENGINE is set to YES. -EXTERNAL_SEARCH_ID = +EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are @@ -1566,7 +1589,7 @@ EXTERNAL_SEARCH_ID = # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... # This tag requires that the tag SEARCHENGINE is set to YES. -EXTRA_SEARCH_MAPPINGS = +EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output @@ -1630,7 +1653,7 @@ PAPER_TYPE = a4 # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. -EXTRA_PACKAGES = +EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for the # generated LaTeX document. The header should contain everything until the first @@ -1646,7 +1669,7 @@ EXTRA_PACKAGES = # to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_HEADER = +LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last @@ -1657,7 +1680,7 @@ LATEX_HEADER = # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_FOOTER = +LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created @@ -1668,7 +1691,7 @@ LATEX_FOOTER = # list). # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_STYLESHEET = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output @@ -1676,7 +1699,7 @@ LATEX_EXTRA_STYLESHEET = # markers available. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_FILES = +LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will @@ -1729,6 +1752,14 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1776,14 +1807,14 @@ RTF_HYPERLINKS = NO # default style sheet that doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_STYLESHEET_FILE = +RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is # similar to doxygen's config file. A template extensions file can be generated # using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_EXTENSIONS_FILE = +RTF_EXTENSIONS_FILE = # If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code # with syntax highlighting in the RTF output. @@ -1828,7 +1859,7 @@ MAN_EXTENSION = .3 # MAN_EXTENSION with the initial . removed. # This tag requires that the tag GENERATE_MAN is set to YES. -MAN_SUBDIR = +MAN_SUBDIR = # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real @@ -1941,7 +1972,7 @@ PERLMOD_PRETTY = YES # overwrite each other's variables. # This tag requires that the tag GENERATE_PERLMOD is set to YES. -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor @@ -1982,7 +2013,7 @@ SEARCH_INCLUDES = YES # preprocessor. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -1990,7 +2021,7 @@ INCLUDE_PATH = # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. @@ -2000,7 +2031,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2009,7 +2040,7 @@ PREDEFINED = # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have @@ -2038,13 +2069,13 @@ SKIP_FUNCTION_MACROS = YES # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. -TAGFILES = +TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. -GENERATE_TAGFILE = +GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES, all external class will be listed in # the class index. If set to NO, only the inherited external classes will be @@ -2084,7 +2115,7 @@ PERL_PATH = /usr/bin/perl # powerful graphs. # The default value is: YES. -CLASS_DIAGRAMS = YES +CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see: @@ -2093,14 +2124,14 @@ CLASS_DIAGRAMS = YES # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. -MSCGEN_PATH = +MSCGEN_PATH = # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. # If left empty dia is assumed to be found in the default search path. -DIA_PATH = +DIA_PATH = # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. @@ -2149,7 +2180,7 @@ DOT_FONTSIZE = 10 # the path where dot can find it using this tag. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTPATH = +DOT_FONTPATH = # If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for # each documented class showing the direct and indirect inheritance relations. @@ -2293,26 +2324,26 @@ INTERACTIVE_SVG = NO # found. If left blank, it is assumed the dot tool can be found in the path. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_PATH = +DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the \dotfile # command). # This tag requires that the tag HAVE_DOT is set to YES. -DOTFILE_DIRS = +DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the \mscfile # command). -MSCFILE_DIRS = +MSCFILE_DIRS = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile # command). -DIAFILE_DIRS = +DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file. If left blank, it is assumed @@ -2320,12 +2351,17 @@ DIAFILE_DIRS = # generate a warning when it encounters a \startuml command in this case and # will not generate output for the diagram. -PLANTUML_JAR_PATH = +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. -PLANTUML_INCLUDE_PATH = +PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes diff --git a/lib/.gitignore b/lib/.gitignore index b0e5275..e2e741c 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -1,49 +1,51 @@ -/*.lo -/*.o -/.deps/ -/.gitignore~ -/.libs/ -/Makefile -/Makefile.am -/Makefile.in -/_Exit.c /alloca.h /alloca.in.h /arg-nonnull.h /arpa/ +/arpa_inet.c /arpa_inet.in.h -/atexit.c -/c++defs.h +/assert.in.h +/attribute.h +/basename-lgpl.c +/basename-lgpl.h +/c32is-impl.h +/c32isprint.c +/calloc.c /c-ctype.c /c-ctype.h -/c-strcase.h -/c-strcasecmp.c -/c-strcaseeq.h -/c-strncasecmp.c -/calloc.c -/charset.alias -/close-stream.c -/close-stream.h +/c++defs.h +/cdefs.h +/cloexec.c +/cloexec.h /close.c /closeout.c /closeout.h -/config.charset -/configmake.h -/dosname.h +/close-stream.c +/close-stream.h +/c-strcasecmp.c +/c-strcaseeq.h +/c-strcase.h +/c-strncasecmp.c +/dup2.c /errno.in.h /error.c /error.h +/error.in.h /exitfail.c /exitfail.h /fclose.c +/fcntl.c /fcntl.h /fcntl.in.h /fd-hook.c /fd-hook.h /fdopen.c /fflush.c -/float+.h +/filename.h +/flexmember.h /float.c +/float+.h +/float.h /float.in.h /fopen.c /fpending.c @@ -57,112 +59,204 @@ /fstat.c /ftell.c /ftello.c -/getopt.c -/getopt.in.h +/getdtablesize.c +/getlocalename_l-unsafe.c +/getlocalename_l-unsafe.h /getopt1.c +/getopt.c +/getopt-cdefs.in.h +/getopt-core.h +/getopt-ext.h +/getopt.in.h /getopt_int.h +/getopt-pfx-core.h +/getopt-pfx-ext.h +/getprogname.c +/getprogname.h /gettext.h -/gettimeofday.c +/hard-locale.c +/hard-locale.h +/ialloc.c +/ialloc.h +/idx.h /inet_pton.c /intprops.h +/intprops-internal.h +/inttypes.h +/inttypes.in.h /isnan.c /isnand.c /isnanf.c /isnanl.c /itold.c -/langinfo.h -/langinfo.in.h +/lc-charset-dispatch.c +/lc-charset-dispatch.h +/libc-config.h /libdhcpd_pools.la +/libdhcpd_pools_la-arpa_inet.lo +/libdhcpd_pools_la-basename-lgpl.lo +/libdhcpd_pools_la-c32isprint.lo +/libdhcpd_pools_la-c-ctype.lo +/libdhcpd_pools_la-cloexec.lo +/libdhcpd_pools_la-closeout.lo +/libdhcpd_pools_la-close-stream.lo +/libdhcpd_pools_la-c-strcasecmp.lo +/libdhcpd_pools_la-exitfail.lo +/libdhcpd_pools_la-fclose.lo +/libdhcpd_pools_la-fcntl.lo +/libdhcpd_pools_la-fd-hook.lo +/libdhcpd_pools_la-fflush.lo +/libdhcpd_pools_la-float.lo +/libdhcpd_pools_la-fopen.lo +/libdhcpd_pools_la-fpurge.lo +/libdhcpd_pools_la-freading.lo +/libdhcpd_pools_la-fseek.lo +/libdhcpd_pools_la-fseeko.lo +/libdhcpd_pools_la-getprogname.lo +/libdhcpd_pools_la-hard-locale.lo +/libdhcpd_pools_la-ialloc.lo +/libdhcpd_pools_la-localcharset.lo +/libdhcpd_pools_la-malloca.lo +/libdhcpd_pools_la-math.lo +/libdhcpd_pools_la-mbrtoc32.lo +/libdhcpd_pools_la-mbrtowc.lo +/libdhcpd_pools_la-mbszero.lo +/libdhcpd_pools_la-mktime.lo +/libdhcpd_pools_la-nstrftime.lo +/libdhcpd_pools_la-progname.lo +/libdhcpd_pools_la-quotearg.lo +/libdhcpd_pools_la-reallocarray.lo +/libdhcpd_pools_la-realloc.lo +/libdhcpd_pools_la-setlocale_null.lo +/libdhcpd_pools_la-setlocale_null-unlocked.lo +/libdhcpd_pools_la-stat-time.lo +/libdhcpd_pools_la-stdlib.lo +/libdhcpd_pools_la-sys_socket.lo +/libdhcpd_pools_la-timegm.lo +/libdhcpd_pools_la-time_rz.lo +/libdhcpd_pools_la-unistd.lo +/libdhcpd_pools_la-wctype-h.lo +/libdhcpd_pools_la-xalloc-die.lo +/libdhcpd_pools_la-xmalloc.lo +/.libs/ +/limits.h +/limits.in.h /localcharset.c /localcharset.h +/locale.h +/locale.in.h +/localename.h +/localename-unsafe.c /lseek.c -/malloc.c +/Makefile.am /malloca.c /malloca.h -/malloca.valgrind +/malloc.c /math.c /math.h /math.in.h +/mbrtoc32.c /mbrtowc.c +/mbrtowc-impl.h +/mbrtowc-impl-utf8.h /mbsinit.c +/mbszero.c +/mbtowc-lock.c +/mbtowc-lock.h /memchr.c /memchr.valgrind -/memcpy.c -/mktime-internal.h +/minmax.h /mktime.c +/mktime-internal.h /msvc-inval.c /msvc-inval.h /msvc-nothrow.c /msvc-nothrow.h /netinet_in.in.h +/_Noreturn.h +/nstrftime.c +/open.c /pathmax.h /progname.c /progname.h -/quote.h /quotearg.c /quotearg.h +/quote.h +/reallocarray.c /realloc.c -/ref-add.sed -/ref-add.sin -/ref-del.sed -/ref-del.sin /setenv.c +/setlocale-lock.c +/setlocale_null.c +/setlocale_null.h +/setlocale_null-unlocked.c /stat.c -/stdalign.h -/stdalign.in.h -/stdarg.in.h -/stdbool.in.h +/stat-time.c +/stat-time.h +/stat-w32.c +/stat-w32.h +/stdckdint.h +/stdckdint.in.h +/stddef.h /stddef.in.h /stdint.in.h -/stdio-impl.h -/stdio.c /stdio.h +/stdio-impl.h /stdio.in.h +/stdio-read.c +/stdio-write.c +/stdlib.c /stdlib.h /stdlib.in.h /stpncpy.c -/str-two-way.h /strdup.c /streq.h +/strerror.c /strerror-override.c /strerror-override.h -/strerror.c /strftime.c /strftime.h /string.h /string.in.h /strstr.c /strtod.c +/str-two-way.h /sys/ /sys_socket.c /sys_socket.in.h /sys_stat.in.h -/sys_time.in.h /sys_types.in.h /sys_uio.in.h -/time-internal.h +/timegm.c /time.h /time.in.h +/time-internal.h /time_r.c /time_rz.c -/timegm.c +/tzset.c +/uchar.h +/uchar.in.h +/unictype/ +/unictype/bitmap.h +/unictype/ctype_print.c +/unictype/ctype_print.h +/unictype/.gitignore +/unictype.h +/unictype.in.h /unistd.c /unistd.h /unistd.in.h +/unitypes.h +/unitypes.in.h /unsetenv.c -/unused-parameter.h /verify.h /warn-on-use.h /wchar.h /wchar.in.h -/wctype-h.c /wctype.h +/wctype-h.c /wctype.in.h +/windows-initguard.h /xalloc-die.c -/xalloc-oversized.h /xalloc.h +/xalloc-oversized.h /xmalloc.c -/xprintf.c -/xprintf.h -/xstrtod.c -/xstrtod.h diff --git a/man/dhcpd-pools.1.in b/man/dhcpd-pools.1.in index df78f84..5001e24 100644 --- a/man/dhcpd-pools.1.in +++ b/man/dhcpd-pools.1.in @@ -1,9 +1,28 @@ -.TH DHCPD-POOLS "1" "2015-09-04" "@VERSION@" "User Commands" +'\" t +.TH DHCPD-POOLS "1" "2024-08-09" "@VERSION@" "User Commands" .SH NAME dhcpd-pools \- ISC dhcpd pools usage analysis .SH SYNOPSIS -.B dhcpd-pools -[options] +.SY dhcpd-pools +.OP \-\-config file +.OP \-\-leases file +.OP \-\-sort nimcptTe +.OP \-\-reverse +.OP \-\-format tHcxXjJ +.OP \-\-mustach template +.OP \-\-output file +.OP \-\-limit nr +.OP \-\-color when +.OP \-\-warning percent +.OP \-\-critical percent +.OP \-\-warn\-count number +.OP \-\-crit\-count number +.OP \-\-snet\-alarms +.OP \-\-minsize size +.OP \-\-perfdata +.OP \-\-version +.OP \-\-help +.YS .SH DESCRIPTION The program analyses ISC dhcpd shared network and pool usage and outputs the results in a format selected by user. @@ -64,14 +83,14 @@ field is default sort key. \fB\-r\fR, \fB\-\-reverse\fR Sort results in reverse order. .TP -\fB\-f\fR, \fB\-\-format\fR=\fI[thHcxXjJ]\fR +\fB\-f\fR, \fB\-\-format\fR=\fI[tHcxXjJ]\fR Output format. Text .RI ( t ). Full-html .RI ( H ) -page output. -The +page output. In html page critical and warning thresholds can be visualized +with \-\-color=always option. The .RI ( c ) stands for comma-separated values. Output format xml .RI ( x ) @@ -86,6 +105,18 @@ to include ethernet address. The default format is .IR @OUTPUT_FORMAT@ . .TP +\fB\-\-mustach\fR=\fITEMPLATE\fR +Output using mustach +.I template +file. This is useful when the native output formats controlled with +.B \-\-format +option do not provide what you need. See below example mustach template +that is using all available {{tags}} to demonstrate what can be displayed +and how. +.IP +@bindir@/dhcpd-pools --config @docdir@/dhcpd.conf --leases +@docdir@/dhcpd.leases --mustach @docdir@/mustach.template +.TP \fB\-o\fR, \fB\-\-output\fR=\fIFILE\fR .I File where output is written. Default is stdout. @@ -104,29 +135,17 @@ determines which numeric analysis tables to include in the output. The following values are "OR'd" together to create the desired output. The default is .IR @OUTPUT_LIMIT@ . -.PP -.RS -.PD 0 -.TP -.B 01 -Print ranges -.TP -.B 02 -Print shared networks -.TP -.B 04 -Print total summary -.TP -.B 10 -Print range header -.TP -.B 20 -Print shared network header -.TP -.B 40 -Print total summary header -.PD -.RE +.IP +.TS +tab(:); +ll. +0\fI1\fR:Print ranges +0\fI2\fR:Print shared networks +0\fI4\fR:Print total summary +\fI1\fR0:Print range header +\fI2\fR0:Print shared network header +\fI4\fR0:Print total summary header +.TE .IP The output limit for total summary has special meaning in .B \-\-warning @@ -136,6 +155,37 @@ alarming context. When the alarming is in use, and total is not wanted to be seen then in the case of alarming returning success nothing is printed. .TP +\fB\-\-color\fR=\fIwhen\fR +Use yellow for warning, red for critical, green for suppressed by \-\-minsize +and blue when \-\-snet\-alarms is the cause of suppression or shared network +does not have any ranges. The +.I when +string can be +.BR always , +.BR never , +or +.BR auto . +Default is auto, that uses colors when command is running in interactive +terminal. With use of +.B \-\-warning +or +.B \-\-critical +coloring thresholds can be changed, but one must also use +.B \-\-format=text +to avoid turning on alarting mode. +.TP +\fB\-\-skip\fR=\fIwhen\fR +The +.I when +can be one of the following: +.IR ok , +.IR warning , +.IR critical , +.IR minsize , +or +.IR suppressed . +The skipping criteria is exact match with colors in \-\-color option. +.TP \fB\-\-warning\fR=\fIpercent\fR Turn on alarm output format, and specify percentage number which will cause an alarm. If either a range or shared network will exceed @@ -168,7 +218,7 @@ and count .I number are required to be exceeded in order to alarm criteria being fulfilled. .IP -This option is intented to be used in setup where very large and small +This option is intended to be used in setup where very large and small shared-networks and ranges co-exists. In such environments percent based alarming can lead to either flood of alarms about small ranges, or way too great overhead of free addresses in large shared-networks. Suggested usage @@ -189,9 +239,25 @@ configurations that has lots of small ranges in big shared-networks. \fB\-\-minsize\fR=\fIsize\fR Ignore ranges and shared networks that are smaller or equal to the defined size. This option is meaningful only in context of alarming, and -will intented to supress for example single host ranges. By default this +will intended to suppress for example single host ranges. By default this option is not in use. .TP +\fB\-p\fR, \fB\-\-perfdata\fR +Print additional performance data, like lease count, touched leases and +backup leases. This option is meaningful only in context of alarming and +will print lots of data, if there are many networks. By default this option +is not in use. +.TP +\fB\-A\fR, \fB\-\-all\-as\-shared\fR +Treat all stand-alone subnets as shared-network with named formed from it's +CIDR. By default this option is not in use for backwards compatibility. +.TP +\fB\-\-ip\-version\fR=\fI4|6\fR +Force command to read configuration and leases files in IPv4 or IPv6 mode. +Notice that when inputs do not match with what is forced analysis output is +garbage. This option should not be necessary to use, and exists only to +allow debugging. +.TP \fB\-v\fR, \fB\-\-version\fR Print version information to standard output and exit successfully. .TP @@ -220,7 +286,7 @@ $ dhcpd-pools \-c dhcpd.conf \-l dhcpd.leases \-L 22 \-\-critical 70 \-\-warning .br [no-output] .br -Supress printing OK, and make alarm only to go off if shared networks +Suppress printing OK, and make alarm only to go off if shared networks exceed critial or warning levels. .SH FILES .TP @@ -229,14 +295,23 @@ ISC dhcpd configuration file. .TP @DHCPDLEASE_FILE@ ISC dhcpd lease file. +.TP +@docdir@/prometheus.template +Prometheus text file collector mustach template. .SH AUTHORS Original design by Sami Kerola. .br +uthash by Troy D. Hanson. +.br XML support by Dominic Germain, Sogetel inc. .br IPv6 support by Cheer Xiao. -.PP -The software has FreeBSD License. +.br +Mustache templating support by José Bollo. +.SH LICENSE +The dhcpd-pools uses FreeBSD License, uthash uses BSD license, the mustache +uses Apache License, and the gnulib modules are mostly, but not entirely, +GPL. .SH "REPORTING BUGS" Report bugs to .MT @PACKAGE_BUGREPORT@ @@ -249,4 +324,6 @@ Home page .SH "SEE ALSO" .BR dhcpd.leases (5), .BR dhcpd.conf (5), -.BR chmod (1) +.BR chmod (1), +.UR https://mustache.github.io/ +.UE diff --git a/project.doap b/project.doap new file mode 100644 index 0000000..b9f0a8a --- /dev/null +++ b/project.doap @@ -0,0 +1,38 @@ + + + + dhcpd-pools + + This is dhcpd-pools ISC dhcp shared network and pool + range usage analysis tool. + Purpose of command is to count usage ratio of each + IP range and shared network pool which ISC dhcpd is in control + of. Program is written C. Design goal is to get analysis done + quickly where there is lots of data. On cheap laptop the speed + of analysis is more than 100k leases per second. Number of + ranges, or shared networks, does not make any significant + difference in getting analysis done. + + + C + + + + + + + + BSD 2-Clause license + + + + + Sami Kerola + + + + + diff --git a/samples/Makemodule.am b/samples/Makemodule.am new file mode 100644 index 0000000..054f001 --- /dev/null +++ b/samples/Makemodule.am @@ -0,0 +1,5 @@ +dist_doc_DATA = \ + samples/dhcpd.conf \ + samples/dhcpd.leases \ + samples/mustach.template \ + samples/prometheus.template diff --git a/samples/dhcpd.conf b/samples/dhcpd.conf new file mode 100644 index 0000000..0ef9eb0 --- /dev/null +++ b/samples/dhcpd.conf @@ -0,0 +1,31 @@ +shared-network example1 { + subnet 10.0.0.0 netmask 255.255.255.0 { + pool { + range 10.0.0.1 10.0.0.20; + } + } + subnet 10.1.0.0 netmask 255.255.255.0 { + pool { + range 10.1.0.1 10.1.0.20; + } + } +} + +shared-network example2 { + subnet 10.2.0.0 netmask 255.255.255.0 { + pool { + range 10.2.0.1 10.2.0.20; + } + } + subnet 10.3.0.0 netmask 255.255.255.0 { + pool { + range 10.3.0.1 10.3.0.20; + } + } +} + +subnet 10.4.0.0 netmask 255.255.255.0 { + pool { + range 10.4.0.1 10.4.0.20; + } +} diff --git a/samples/dhcpd.leases b/samples/dhcpd.leases new file mode 100644 index 0000000..0d7e317 --- /dev/null +++ b/samples/dhcpd.leases @@ -0,0 +1,210 @@ +lease 10.0.0.0 { + binding state active; + hardware ethernet 00:00:00:00:00:00; +} +lease 10.0.0.1 { + binding state active; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.0.0.2 { + binding state active; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.0.0.3 { + binding state active; + hardware ethernet 00:00:00:00:00:03; +} +lease 10.0.0.4 { + binding state active; + hardware ethernet 00:00:00:00:00:04; +} +lease 10.0.0.5 { + binding state active; + hardware ethernet 00:00:00:00:00:05; +} +lease 10.0.0.6 { + binding state active; + hardware ethernet 00:00:00:00:00:06; +} +lease 10.0.0.7 { + binding state active; + hardware ethernet 00:00:00:00:00:07; +} +lease 10.0.0.8 { + binding state active; + hardware ethernet 00:00:00:00:00:08; +} +lease 10.0.0.9 { + binding state active; + hardware ethernet 00:00:00:00:00:09; +} +lease 10.0.0.10 { + binding state active; + hardware ethernet 00:00:00:00:00:10; +} +lease 10.0.0.11 { + binding state active; + hardware ethernet 00:00:00:00:00:11; +} +lease 10.0.0.12 { + binding state backup; + hardware ethernet 00:00:00:00:00:12; +} + + +lease 10.1.0.0 { + binding state active; + hardware ethernet 00:00:00:00:01:00; +} +lease 10.1.0.1 { + binding state active; + hardware ethernet 00:00:00:00:01:01; +} +lease 10.1.0.2 { + binding state active; + hardware ethernet 00:00:00:00:01:02; +} +lease 10.1.0.3 { + binding state active; + hardware ethernet 00:00:00:00:01:03; +} +lease 10.1.0.4 { + binding state active; + hardware ethernet 00:00:00:00:01:04; +} +lease 10.1.0.5 { + binding state active; + hardware ethernet 00:00:00:00:01:05; +} +lease 10.1.0.6 { + binding state active; + hardware ethernet 00:00:00:00:01:06; +} +lease 10.1.0.7 { + binding state active; + hardware ethernet 00:00:00:00:01:07; +} +lease 10.1.0.8 { + binding state active; + hardware ethernet 00:00:00:00:01:08; +} +lease 10.1.0.9 { + binding state active; + hardware ethernet 00:00:00:00:01:09; +} +lease 10.1.0.10 { + binding state active; + hardware ethernet 00:00:00:00:01:10; +} + + +lease 10.2.0.0 { + binding state active; + hardware ethernet 00:00:00:00:02:00; +} +lease 10.2.0.1 { + binding state active; + hardware ethernet 00:00:00:00:02:01; +} +lease 10.2.0.2 { + binding state active; + hardware ethernet 00:00:00:00:02:02; +} +lease 10.2.0.3 { + binding state active; + hardware ethernet 00:00:00:00:02:03; +} +lease 10.2.0.4 { + binding state active; + hardware ethernet 00:00:00:00:02:04; +} +lease 10.2.0.5 { + binding state active; + hardware ethernet 00:00:00:00:02:05; +} +lease 10.2.0.6 { + binding state active; + hardware ethernet 00:00:00:00:02:06; +} +lease 10.2.0.7 { + binding state active; + hardware ethernet 00:00:00:00:02:07; +} +lease 10.2.0.8 { + binding state active; + hardware ethernet 00:00:00:00:02:08; +} + +lease 10.3.0.0 { + binding state active; + hardware ethernet 00:00:00:00:03:00; +} +lease 10.3.0.1 { + binding state active; + hardware ethernet 00:00:00:00:03:01; +} +lease 10.3.0.2 { + binding state active; + hardware ethernet 00:00:00:00:03:02; +} +lease 10.3.0.3 { + binding state active; + hardware ethernet 00:00:00:00:03:03; +} +lease 10.3.0.4 { + binding state active; + hardware ethernet 00:00:00:00:03:04; +} +lease 10.3.0.5 { + binding state active; + hardware ethernet 00:00:00:00:03:05; +} +lease 10.3.0.6 { + binding state active; + hardware ethernet 00:00:00:00:03:06; +} +lease 10.3.0.7 { + binding state active; + hardware ethernet 00:00:00:00:03:07; +} +lease 10.3.0.8 { + binding state active; + hardware ethernet 00:00:00:00:03:08; +} +lease 10.3.0.9 { + binding state active; + hardware ethernet 00:00:00:00:03:09; +} +lease 10.3.0.9 { + binding state active; + hardware ethernet 00:00:00:00:03:09; +} + +lease 10.4.0.0 { + binding state active; + hardware ethernet 00:00:00:00:04:00; +} +lease 10.4.0.1 { + binding state active; + hardware ethernet 00:00:00:00:04:01; +} +lease 10.4.0.2 { + binding state active; + hardware ethernet 00:00:00:00:04:02; +} +lease 10.4.0.3 { + binding state active; + hardware ethernet 00:00:00:00:04:03; +} +lease 10.4.0.4 { + binding state active; + hardware ethernet 00:00:00:00:04:04; +} +lease 10.4.0.5 { + binding state active; + hardware ethernet 00:00:00:00:04:05; +} +lease 10.4.0.6 { + binding state backup; + hardware ethernet 00:00:00:00:04:06; +} diff --git a/samples/mustach.template b/samples/mustach.template new file mode 100644 index 0000000..ed4561e --- /dev/null +++ b/samples/mustach.template @@ -0,0 +1,76 @@ +Ethernets:{{#active_lease}} + macaddress: {{macaddress}} ip: {{ip}}{{/active_lease}} + +Subnets:{{#subnets}} + location: {{location}} + range: {{range}} + first_ip: {{first_ip}} + last_ip: {{last_ip}} + used: {{used}} + touched: {{touched}} + defined: {{defined}} + free: {{free}} + percent: {{percent}} + touch_count: {{touch_count}} + touch_percent: {{touch_percent}} + backup_count: {{backup_count}} + backup_percent: {{backup_percent}} + status: {{status}} + gettimeofday: {{gettimeofday}} + lease_file_epoch_mtime: {{lease_file_epoch_mtime}} +{{/subnets}} + +Shared-networks:{{#shared-networks}} + location: {{location}} + defined: {{defined}} + used: {{used}} + touched: {{touched}} + free: {{free}} + percent: {{percent}} + touch_count: {{touch_count}} + touch_percent: {{touch_percent}} + backup_count: {{backup_count}} + backup_percent: {{backup_percent}} + status: {{status}} + gettimeofday: {{gettimeofday}} + lease_file_epoch_mtime: {{lease_file_epoch_mtime}} +{{/shared-networks}} + +Summary:{{#summary}} + location: {{location}} + defined: {{defined}} + used: {{used}} + touched: {{touched}} + free: {{free}} + percent: {{percent}} + touch_count: {{touch_count}} + touch_percent: {{touch_percent}} + backup_count: {{backup_count}} + backup_percent: {{backup_percent}} + status: {{status}} + gettimeofday: {{gettimeofday}} + lease_file_epoch_mtime: {{lease_file_epoch_mtime}} +{{/summary}} + +localtime: {{localtime}} +number_of_ranges: {{number_of_ranges}} +number_of_shared_networks: {{number_of_shared_networks}} +version: {{version}} + +conf_file_path: {{conf_file_path}} +conf_file_local_mtime: {{conf_file_local_mtime}} +conf_file_epoch_mtime: {{conf_file_epoch_mtime}} + +lease_file_path: {{lease_file_path}} +lease_file_local_mtime: {{lease_file_local_mtime}} +lease_file_epoch_mtime: {{lease_file_epoch_mtime}} + +template_file_path: {{template_file_path}} +template_file_local_mtime: {{template_file_local_mtime}} +template_file_epoch_mtime: {{template_file_epoch_mtime}} + +number_of_ranges_warning: {{number_of_ranges_warning}} +number_of_ranges_critical: {{number_of_ranges_critical}} +number_of_shared_networks: {{number_of_shared_networks}} +number_of_shared_networks_warning: {{number_of_shared_networks_warning}} +number_of_shared_networks_critical: {{number_of_shared_networks_critical}} diff --git a/samples/prometheus.template b/samples/prometheus.template new file mode 100644 index 0000000..d48f959 --- /dev/null +++ b/samples/prometheus.template @@ -0,0 +1,11 @@ +# This mustach template can be used as Prometheus text file. +# https://prometheus.io/ +# HELP dhcpd_pools ISC dhcpd statistics +# TYPE dhcpd_pools gauge +{{#subnets}}dhcpd_pools{location="{{location}}",range="{{first_ip}}",used="1"} {{used}} {{gettimeofday}}000 +dhcpd_pools{location="{{location}}",range="{{first_ip}}",touched="1"} {{touched}} {{gettimeofday}}000 +dhcpd_pools{location="{{location}}",range="{{first_ip}}",defined="1"} {{defined}} {{gettimeofday}}000 +dhcpd_pools{location="{{location}}",range="{{first_ip}}",free="1"} {{free}} {{gettimeofday}}000 +dhcpd_pools{location="{{location}}",range="{{first_ip}}",touch_count="1"} {{touch_count}} {{gettimeofday}}000 +dhcpd_pools{location="{{location}}",range="{{first_ip}}",status="1"} {{status}} {{gettimeofday}}000 +{{/subnets}} diff --git a/src/.indent.pro b/src/.indent.pro index 3d15003..64735c3 100644 --- a/src/.indent.pro +++ b/src/.indent.pro @@ -1,5 +1,6 @@ -linux -TFILE +-Tconf_t -Tipaddr_t -Tleases_t -Toff_t @@ -7,7 +8,9 @@ -Tsize_t -Ttime_t -Tuintmax_t --l100 --ppi1 --bad --sob +--blank-lines-after-declarations +--format-all-comments +--line-length100 +--preprocessor-indentation1 +--start-left-side-of-comments +--swallow-optional-blank-lines diff --git a/src/Makemodule.am b/src/Makemodule.am index 45e62b5..e1c75f5 100644 --- a/src/Makemodule.am +++ b/src/Makemodule.am @@ -4,11 +4,10 @@ bin_PROGRAMS = dhcpd-pools AC_PROG_RANLIB = resolv AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_builddir)/lib -dhcpd_pools_LDADD = $(top_builddir)/lib/libdhcpd_pools.la +dhcpd_pools_LDADD = $(top_builddir)/lib/libdhcpd_pools.la $(MATH_LIBS) dhcpd_pools_SOURCES = \ src/analyze.c \ - src/defaults.h \ src/dhcpd-pools.c \ src/dhcpd-pools.h \ src/getdata.c \ @@ -16,3 +15,10 @@ dhcpd_pools_SOURCES = \ src/other.c \ src/output.c \ src/sort.c + +if ENABLE_MUSTACH +dhcpd_pools_SOURCES += \ + src/mustach-dhcpd-pools.c \ + src/mustach.c \ + src/mustach.h +endif diff --git a/src/analyze.c b/src/analyze.c index 072c07e..6721761 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -47,30 +47,31 @@ /*! \brief Prepare data for analysis. The function will sort leases and * ranges. */ -void prepare_data(void) +void prepare_data(struct conf_t *state) { /* Sort leases */ - HASH_SORT(leases, leasecomp); + HASH_SORT(state->leases, leasecomp); /* Sort ranges */ - qsort(ranges, (size_t)num_ranges, sizeof(struct range_t), &rangecomp); + qsort(state->ranges, state->num_ranges, sizeof(struct range_t), &rangecomp); } -/*! \brief Perform counting. Join leases with ranges, and update counters. */ -void do_counting(void) +/*!\brief Perform counting. Join leases with ranges, and update range and + * shared network counters. */ +void do_counting(struct conf_t *state) { - struct range_t *restrict range_p; - const struct leases_t *restrict l = leases; - unsigned long i, k, block_size; + struct range_t *restrict range_p = state->ranges; + const struct leases_t *restrict l = state->leases; + unsigned long i; + double block_size; /* Walk through ranges */ - range_p = ranges; - for (i = 0; i < num_ranges; i++) { - for (; l != NULL && ipcomp(&range_p->first_ip, &l->ip) < 0; l = l->hh.prev) - /* rewind */ ; + for (i = 0; i < state->num_ranges; i++, range_p++) { + while (l != NULL && ipcomp(&range_p->first_ip, &l->ip) < 0) + l = l->hh.prev; /* rewind */ if (l == NULL) - l = leases; + l = state->leases; for (; l != NULL && ipcomp(&l->ip, &range_p->last_ip) <= 0; l = l->hh.next) { - if (ipcomp(&l->ip, &range_p->first_ip) < 0) + if (unlikely(ipcomp(&l->ip, &range_p->first_ip) < 0)) continue; /* cannot happen? */ /* IP in range */ switch (l->type) { @@ -83,39 +84,23 @@ void do_counting(void) case BACKUP: range_p->backups++; break; - } - if (range_p->shared_net) { - switch (l->type) { - case FREE: - range_p->shared_net->touched++; - break; - case ACTIVE: - range_p->shared_net->used++; - break; - case BACKUP: - range_p->shared_net->backups++; - break; - } + default: + abort(); } } - /* Size of range, shared net & all networks */ + /* Size of range size. */ block_size = get_range_size(range_p); - if (range_p->shared_net) - range_p->shared_net->available += block_size; - range_p++; - } - /* FIXME: During count of other shared networks default network - * and all networks got mixed together semantically. The below - * fixes the problem, but is not elegant. */ - shared_networks->available = 0; - shared_networks->used = 0; - shared_networks->touched = 0; - range_p = ranges; - for (k = 0; k < num_ranges; k++) { - shared_networks->available += get_range_size(range_p); - shared_networks->used += range_p->count; - shared_networks->touched += range_p->touched; - shared_networks->backups += range_p->backups; - range_p++; + /* Count together ranges within shared network block. */ + range_p->shared_net->available += block_size; + range_p->shared_net->used += range_p->count; + range_p->shared_net->touched += range_p->touched; + range_p->shared_net->backups += range_p->backups; + /* When shared network is not 'all networks' add it as well. */ + if (range_p->shared_net != state->shared_net_root) { + state->shared_net_root->available += block_size; + state->shared_net_root->used += range_p->count; + state->shared_net_root->touched += range_p->touched; + state->shared_net_root->backups += range_p->backups; + } } } diff --git a/src/defaults.h b/src/defaults.h deleted file mode 100644 index c39149a..0000000 --- a/src/defaults.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * The dhcpd-pools has BSD 2-clause license which also known as "Simplified - * BSD License" or "FreeBSD License". - * - * Copyright 2006- Sami Kerola. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are - * those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of Sami Kerola. - */ - -/*! \file defaults.h - * \brief Default settings which cannot be changed without recompiling - * the software. - */ - -#ifndef DEFAULTS_H -# define DEFAULTS_H 1 - -# include "dhcpd-pools.h" - -/*! \var MAXLEN - * \brief Maximum expected line length in dhcpd.conf and dhcpd.leases - * files. */ -static const size_t MAXLEN = 1024; - -/*! \var SHARED_NETWORKS - * \brief Maximum number of different shared networks in dhcpd.conf file. */ -static const unsigned int SHARED_NETWORKS = 8192; - -#endif /* DEFAULTS_H */ diff --git a/src/dhcpd-pools.c b/src/dhcpd-pools.c index d7f57f0..1373053 100644 --- a/src/dhcpd-pools.c +++ b/src/dhcpd-pools.c @@ -39,6 +39,7 @@ #include +#include #include #include #include @@ -53,74 +54,111 @@ #include "xalloc.h" #include "dhcpd-pools.h" -#include "defaults.h" - -/* Global variables */ -int prefix_length[2][NUM_OF_PREFIX]; -struct configuration_t config; -struct shared_network_t *shared_networks; -unsigned int num_shared_networks; -struct range_t *ranges; -unsigned int num_ranges; -struct leases_t *leases; -unsigned int RANGES; /* Function pointers */ -int (*parse_ipaddr) (const char *restrict src, union ipaddr_t *restrict dst); +int (*parse_ipaddr) (struct conf_t *state, const char *restrict src, union ipaddr_t *restrict dst); void (*copy_ipaddr) (union ipaddr_t *restrict dst, const union ipaddr_t *restrict src); const char *(*ntop_ipaddr) (const union ipaddr_t *ip); double (*get_range_size) (const struct range_t *r); -int (*xstrstr) (const char *__restrict str); +int (*xstrstr) (struct conf_t *state, const char *restrict str); int (*ipcomp) (const union ipaddr_t *restrict a, const union ipaddr_t *restrict b); int (*leasecomp) (const struct leases_t *restrict a, const struct leases_t *restrict b); -int (*output_analysis) (void); -void (*add_lease) (union ipaddr_t *ip, enum ltype type); -struct leases_t *(*find_lease) (union ipaddr_t *ip); +void (*add_lease) (struct conf_t *state, union ipaddr_t *ip, enum ltype type); +struct leases_t *(*find_lease) (struct conf_t *state, union ipaddr_t *ip); +/*! \brief An option argument parser to populate state header_limit and + * number_limit values. + */ static int return_limit(const char c) { if ('0' <= c && c < '8') return c - '0'; - clean_up(); error(EXIT_FAILURE, 0, "return_limit: output mask %s is illegal", quote(optarg)); return 0; } -/*! \brief Start of execution. Parse options, and call other other - * functions one after another. At the moment adding threading support - * would be difficult, but there does not seem to be valid reason to - * consider that. Overall the analysis already quick enough even without - * making it parallel. - * - * \return Return value indicates success or fail or analysis, unless - * either --warning or --critical options are in use, which makes the - * return value in some cases to match with Nagios expectations about - * alarming. */ -int main(int argc, char **argv) +/*! \brief Run time initialization. Global allocations, counter + * initializations, etc are here. */ +static void prepare_memory(struct conf_t *state) +{ + state->ranges = xmalloc(sizeof(struct range_t) * state->ranges_size); + /* First shared network entry is all networks */ + state->shared_net_root = xcalloc(sizeof(struct shared_network_t), 1); + state->shared_net_root->name = xstrdup("All networks"); + state->shared_net_head = state->shared_net_root; +} + +/*! \brief The --skip option argument parser. */ +static void skip_arg_parse(struct conf_t *state, char *arg) +{ + enum { + OPT_ARG_OK = 0, + OPT_ARG_WARNING, + OPT_ARG_CRITICAL, + OPT_ARG_MINSIZE, + OPT_ARG_SUPPRESSED + }; + + char *const tokens[] = { + [OPT_ARG_OK] = "ok", + [OPT_ARG_WARNING] = "warning", + [OPT_ARG_CRITICAL] = "critical", + [OPT_ARG_MINSIZE] = "minsize", + [OPT_ARG_SUPPRESSED] = "suppressed", + NULL + }; + char *value; + + while (*arg != '\0') { + switch (getsubopt(&arg, tokens, &value)) { + case OPT_ARG_OK: + state->skip_ok = 1; + break; + case OPT_ARG_WARNING: + state->skip_warning = 1; + break; + case OPT_ARG_CRITICAL: + state->skip_critical = 1; + break; + case OPT_ARG_MINSIZE: + state->skip_minsize = 1; + break; + case OPT_ARG_SUPPRESSED: + state->skip_suppressed = 1; + break; + default: + error(EXIT_FAILURE, 0, "unknown --skip specifier: %s", value); + } + } +} + +/*! \brief Command line options parser. */ +static void parse_command_line_opts(struct conf_t *state, int argc, char **argv) { - int i; - int option_index = 0; - char const *tmp; - char *print_mac_addreses_tmp; - struct range_t *tmp_ranges; enum { OPT_SNET_ALARMS = CHAR_MAX + 1, OPT_WARN, OPT_CRIT, OPT_MINSIZE, OPT_WARN_COUNT, - OPT_CRIT_COUNT + OPT_CRIT_COUNT, + OPT_COLOR, + OPT_SKIP, + OPT_SET_IPV, + OPT_MUSTACH }; - int ret_val; static struct option const long_options[] = { {"config", required_argument, NULL, 'c'}, {"leases", required_argument, NULL, 'l'}, + {"color", required_argument, NULL, OPT_COLOR}, + {"skip", required_argument, NULL, OPT_SKIP}, {"format", required_argument, NULL, 'f'}, {"sort", required_argument, NULL, 's'}, {"reverse", no_argument, NULL, 'r'}, {"output", required_argument, NULL, 'o'}, {"limit", required_argument, NULL, 'L'}, + {"mustach", required_argument, NULL, OPT_MUSTACH}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {"snet-alarms", no_argument, NULL, OPT_SNET_ALARMS}, @@ -129,111 +167,124 @@ int main(int argc, char **argv) {"warn-count", required_argument, NULL, OPT_WARN_COUNT}, {"crit-count", required_argument, NULL, OPT_CRIT_COUNT}, {"minsize", required_argument, NULL, OPT_MINSIZE}, + {"perfdata", no_argument, NULL, 'p'}, + {"all-as-shared", no_argument, NULL, 'A'}, + {"ip-version", required_argument, NULL, OPT_SET_IPV}, {NULL, 0, NULL, 0} }; + int alarming = 0; - atexit(close_stdout); - set_program_name(argv[0]); - /* FIXME: These allocations should be fully dynamic, e.g., grow - * if needed. */ - config.dhcpdconf_file = xmalloc(sizeof(char) * MAXLEN); - config.dhcpdlease_file = xmalloc(sizeof(char) * MAXLEN); - config.output_file = xmalloc(sizeof(char) * MAXLEN); - /* Make sure string has zero length if there is no - * command line option */ - config.output_file[0] = '\0'; - /* Alarming defaults. */ - config.snet_alarms = false; - config.warning = ALARM_WARN; - config.critical = ALARM_CRIT; - config.warn_count = 0x100000000; /* == 2^32 that is the entire IPv4 space */ - config.crit_count = 0x100000000; /* basically turns off the count criteria */ - /* File location defaults */ - strncpy(config.dhcpdconf_file, DHCPDCONF_FILE, MAXLEN - 1); - strncpy(config.dhcpdlease_file, DHCPDLEASE_FILE, MAXLEN - 1); - tmp = OUTPUT_LIMIT; - config.header_limit = (*tmp - '0'); - tmp++; - config.number_limit = (*tmp - '0'); - /* Make sure some output format is selected by default */ - print_mac_addreses_tmp = OUTPUT_FORMAT; - /* Default sort order is by IPs small to big */ - config.reverse_order = false; - config.backups_found = false; - prepare_memory(); - /* Parse command line options */ while (1) { int c; - c = getopt_long(argc, argv, "c:l:f:o:s:rL:vh", long_options, &option_index); + c = getopt_long(argc, argv, "c:l:f:o:s:rL:pAvh", long_options, NULL); if (c == EOF) break; switch (c) { case 'c': /* config file */ - strncpy(config.dhcpdconf_file, optarg, MAXLEN - 1); + state->dhcpdconf_file = optarg; break; case 'l': /* lease file */ - strncpy(config.dhcpdlease_file, optarg, MAXLEN - 1); + state->dhcpdlease_file = optarg; break; case 'f': /* Output format */ - print_mac_addreses_tmp = optarg; + state->output_format = optarg[0]; break; case 's': - { - /* Output sorting option */ - struct output_sort *p = config.sorts; + { + /* Output sorting option */ + struct output_sort *p = state->sorts; + char *ptr = optarg; - while (p && p->next) - p = p->next; - for (i = 0; i < strlen(optarg); i++) { - if (config.sorts == NULL) { - config.sorts = xcalloc(1, sizeof(struct output_sort)); - p = config.sorts; - } else { - p->next = xcalloc(1, sizeof(struct output_sort)); + while (p && p->next) p = p->next; + while (*ptr) { + if (state->sorts == NULL) { + state->sorts = + xcalloc(1, sizeof(struct output_sort)); + p = state->sorts; + } else { + p->next = xcalloc(1, sizeof(struct output_sort)); + p = p->next; + } + p->func = field_selector(*ptr++); } - p->func = field_selector(optarg[i]); } - } break; case 'r': /* What ever sort in reverse order */ - config.reverse_order = true; + state->reverse_order = 1; break; case 'o': /* Output file */ - strncpy(config.output_file, optarg, MAXLEN - 1); + state->output_file = optarg; break; case 'L': /* Specification what will be printed */ - config.header_limit = return_limit(optarg[0]); - config.number_limit = return_limit(optarg[1]); + state->header_limit = return_limit(optarg[0]); + state->number_limit = return_limit(optarg[1]); + break; + case OPT_MUSTACH: +#ifdef BUILD_MUSTACH + state->mustach_template = optarg; + state->output_format = 'm'; + state->print_mac_addreses = 1; +#else + error(EXIT_FAILURE, 0, "compiled without mustach support"); +#endif + break; + case OPT_COLOR: + state->color_mode = parse_color_mode(optarg); + if (state->color_mode == color_unknown) + error(EXIT_FAILURE, errno, "unknown color mode: %s", quote(optarg)); + break; + case OPT_SKIP: + skip_arg_parse(state, optarg); break; case OPT_SNET_ALARMS: - config.snet_alarms = true; + state->snet_alarms = 1; break; case OPT_WARN: - print_mac_addreses_tmp = "a"; - config.warning = strtod_or_err(optarg, "illegal argument"); + alarming = 1; + state->warning = strtod_or_err(optarg, "illegal argument"); break; case OPT_CRIT: - print_mac_addreses_tmp = "a"; - config.critical = strtod_or_err(optarg, "illegal argument"); + alarming = 1; + state->critical = strtod_or_err(optarg, "illegal argument"); break; case OPT_WARN_COUNT: - print_mac_addreses_tmp = "a"; - config.warn_count = strtod_or_err(optarg, "illegal argument"); + alarming = 1; + state->warn_count = strtod_or_err(optarg, "illegal argument"); break; case OPT_CRIT_COUNT: - print_mac_addreses_tmp = "a"; - config.crit_count = strtod_or_err(optarg, "illegal argument"); + alarming = 1; + state->crit_count = strtod_or_err(optarg, "illegal argument"); break; case OPT_MINSIZE: - config.minsize = strtod_or_err(optarg, "illegal argument"); + state->minsize = strtod_or_err(optarg, "illegal argument"); + break; + case OPT_SET_IPV: + switch (optarg[0]) { + case '4': + set_ipv_functions(state, IPv4); + break; + case '6': + set_ipv_functions(state, IPv6); + break; + default: + error(EXIT_FAILURE, 0, "unknown --ip-version argument: %s", optarg); + } + break; + case 'p': + /* Print additional performance data in alarming mode */ + state->perfdata = 1; + break; + case 'A': + /* Treat single networks as shared with network CIDR as name */ + state->all_as_shared = 1; break; case 'v': /* Print version */ @@ -242,81 +293,76 @@ int main(int argc, char **argv) /* Print help */ usage(EXIT_SUCCESS); default: - error(EXIT_FAILURE, 0, "Try %s --help for more information.", - program_name); + error(EXIT_FAILURE, 0, "Try %s --help for more information.", program_name); } } - /* Output function selection */ - switch (print_mac_addreses_tmp[0]) { - case 't': - output_analysis = output_txt; - config.print_mac_addreses = 0; - break; - case 'a': - output_analysis = output_alarming; - config.print_mac_addreses = 0; - break; - case 'h': - error(EXIT_FAILURE, 0, "html table only output format is deprecated"); - break; - case 'H': - output_analysis = output_html; - config.print_mac_addreses = 0; - break; - case 'x': - output_analysis = output_xml; - config.print_mac_addreses = 0; - break; - case 'X': - output_analysis = output_xml; - config.print_mac_addreses = 1; - break; - case 'j': - output_analysis = output_json; - config.print_mac_addreses = 0; - break; - case 'J': - output_analysis = output_json; - config.print_mac_addreses = 1; - break; - case 'c': - output_analysis = output_csv; - config.print_mac_addreses = 0; - break; - default: - clean_up(); - error(EXIT_FAILURE, 0, "unknown output format: %s", quote(print_mac_addreses_tmp)); + + /* Use default dhcpd.conf when user did not define anything. */ + if (state->dhcpdconf_file == NULL) + state->dhcpdconf_file = DHCPDCONF_FILE; + /* Use default dhcpd.leases when user did not define anything. */ + if (state->dhcpdlease_file == NULL) + state->dhcpdlease_file = DHCPDLEASE_FILE; + /* Use default limits when user did not define anything. */ + if (state->header_limit == 8) { + char const *default_limit = OUTPUT_LIMIT; + + state->header_limit = return_limit(default_limit[0]); + state->number_limit = return_limit(default_limit[1]); + } + /* Output format is not defined, if alarm thresholds are then it's alarming, else use the + * default. */ + if (state->output_format == '\0') { + if (alarming == 1) + state->output_format = 'a'; + else { + const char *const default_format = OUTPUT_FORMAT; + + state->output_format = default_format[0]; + } + } + if (state->output_format == 'X' || state->output_format == 'J') { + state->print_mac_addreses = 1; } - /* Do the job */ - set_ipv_functions(IPvUNKNOWN); - parse_config(true, config.dhcpdconf_file, shared_networks); - parse_leases(); - prepare_data(); - do_counting(); - tmp_ranges = xmalloc(sizeof(struct range_t) * num_ranges); - if (config.sorts != NULL) - mergesort_ranges(ranges, num_ranges, tmp_ranges); - if (config.reverse_order == true) - flip_ranges(ranges, tmp_ranges); - free(tmp_ranges); - ret_val = output_analysis(); - clean_up(); - return (ret_val); } -/*! \brief Run time initialization. Global allocations, counter - * initializations, etc are here. */ -void prepare_memory(void) +/*!\brief Start of execution. This will mostly call other functions one + * after another. + * + * \return Return value indicates success or fail or analysis, unless + * either --warning or --critical options are in use, which makes the + * return value in some cases to match with Nagios expectations about + * alarming. */ +int main(int argc, char **argv) { - config.ip_version = IPvUNKNOWN; - RANGES = 64; - num_ranges = num_shared_networks = 0; - shared_networks = xmalloc(sizeof(struct shared_network_t) * SHARED_NETWORKS); - ranges = xmalloc(sizeof(struct range_t) * RANGES); - /* First shared network entry is all networks */ - shared_networks->name = xstrdup("All networks"); - shared_networks->used = 0; - shared_networks->touched = 0; - shared_networks->backups = 0; - config.sorts = NULL; + struct conf_t state = { + .warning = ALARM_WARN, + .critical = ALARM_CRIT, + .warn_count = 0x100000000, /* == 2^32 that is the entire IPv4 space */ + .crit_count = 0x100000000, /* basically turns off the count criteria */ + .header_limit = 8, + .ranges_size = 64, + .ip_version = IPvUNKNOWN, + .color_mode = color_auto + }; + int ret_val; + + atexit(close_stdout); + set_program_name(argv[0]); + prepare_memory(&state); + set_ipv_functions(&state, IPvUNKNOWN); + parse_command_line_opts(&state, argc, argv); + + /* Do the job */ + parse_config(&state, 1, state.dhcpdconf_file, state.shared_net_root); + parse_leases(&state); + prepare_data(&state); + do_counting(&state); + if (state.sorts != NULL) + mergesort_ranges(&state, state.ranges, state.num_ranges, NULL, 1); + if (state.reverse_order == 1) + flip_ranges(&state); + ret_val = output_analysis(&state); + clean_up(&state); + return (ret_val); } diff --git a/src/dhcpd-pools.h b/src/dhcpd-pools.h index c20b72f..aff6b6a 100644 --- a/src/dhcpd-pools.h +++ b/src/dhcpd-pools.h @@ -35,8 +35,6 @@ /*! \file dhcpd-pools.h * \brief Global definitions of structures, enums, and function prototypes. - * FIXME: The file has too many global variables. Most of them should be - * removed, if not all. */ #ifndef DHCPD_POOLS_H @@ -63,22 +61,11 @@ # define unlikely(x) (x) # endif -/* The __attribute__ feature is available in gcc versions 2.5 and later. - * The attribute __pure__ was added in gcc 2.96. */ -# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) -# define _DP_ATTRIBUTE_PURE __attribute__ ((__pure__)) -# else -# define _DP_ATTRIBUTE_PURE /* empty */ -# endif - -/* The __const__ attribute was added in gcc 2.95. */ -# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) -# define _DP_ATTRIBUTE_CONST __attribute__ ((__const__)) -# else -# define _DP_ATTRIBUTE_CONST /* empty */ -# endif - -/* The attribute __hot__ was added in gcc 4.3. */ +/*! \def _DP_ATTRIBUTE_HOT + * \brief The function attribute __hot__ was added in gcc 4.3. See gnu + * documentation for further information. + * https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-hot-function-attribute + */ # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) # define _DP_ATTRIBUTE_HOT __attribute__ ((__hot__)) # else @@ -91,6 +78,7 @@ union ipaddr_t { uint32_t v4; unsigned char v6[16]; }; + /*! \enum dhcp_version * \brief The IP version, IPv4 or IPv6, served by the dhcpd. */ @@ -99,9 +87,11 @@ enum dhcp_version { IPv4, IPv6 }; + /*! \enum prefix_t - * \brief Enumeration of interesting data in dhcpd.leases file, that has - * to be further examined, and saved. + * \brief Enumeration of interesting data in dhcpd.leases file, that has to + * be further examined, and saved. Functions xstrstr_v4() and xstrstr_v6() + * return one of these values to parse_leases(). */ enum prefix_t { PREFIX_LEASE, @@ -112,10 +102,25 @@ enum prefix_t { PREFIX_BINDING_STATE_ACTIVE, PREFIX_BINDING_STATE_BACKUP, PREFIX_HARDWARE_ETHERNET, + PREFIX_STARTS, + PREFIX_ENDS, + PREFIX_HOSTNAME, NUM_OF_PREFIX }; + +/*! \enum color_mode + * \brief Enumeration whether to use or not color output. + */ +enum color_mode { + color_unknown, + color_off, + color_on, + color_auto /*!< Default, use colors when output terminal is interactive. */ +}; + /*! \struct shared_network_t - * \brief Counters for an individual shared network. + * \brief Counters for an individual shared network. This data entry is + * also used for 'all networks' counting. */ struct shared_network_t { char *name; @@ -123,7 +128,10 @@ struct shared_network_t { double used; double touched; double backups; + struct shared_network_t *next; + int netmask; }; + /*! \struct range_t * \brief Counters for an individual range. */ @@ -135,44 +143,62 @@ struct range_t { double touched; double backups; }; -/*! \enum isc_conf_parser - * \brief Configuration file parsing state flags. + +/*! \struct output_helper_t + * \brief Various per range and shared net temporary calculation results. */ -enum isc_conf_parser { - ITS_NOTHING_INTERESTING, - ITS_A_RANGE_FIRST_IP, - ITS_A_RANGE_SECOND_IP, - ITS_A_SHAREDNET, - ITS_AN_INCLUCE +struct output_helper_t { + double range_size; + double percent; + double tc; + double tcp; + double bup; + int status; }; + +/*! \struct status_counts_t + * \brief Range and shared network alarming status counts. + */ +struct status_counts_t { + unsigned int warning; + unsigned int critical; + unsigned int ok; + unsigned int ignored; +}; + /*! \enum ltype - * \brief Lease state types. + * \brief Lease state types. These are the possible values in struct leases_t. */ enum ltype { ACTIVE, FREE, BACKUP }; + /*! \struct leases_t - * \brief An individual lease. The leaases are hashed. + * \brief An individual lease. These leaases are hashed. */ struct leases_t { union ipaddr_t ip; /* ip as key */ - enum ltype type; char *ethernet; UT_hash_handle hh; + enum ltype type; + char *ends; + char *starts; + char *hostname; }; + /*! \enum limbits - * \brief Output limit bits: R_BIT ranges, S_BIT shared networks, A_BIT all. + * \brief Output limit bits. */ enum limbits { - R_BIT = (1 << 0), - S_BIT = (1 << 1), - A_BIT = (1 << 2) + R_BIT = (1 << 0), /*!< Range limit. */ + S_BIT = (1 << 1), /*!< Shared networks limit. */ + A_BIT = (1 << 2) /*!< All networks summary limit. */ }; /*! \def STATE_OK - * \brief Nagios alarm exit values. + * \brief Nagios alarm exit value. */ # define STATE_OK 0 # define STATE_WARNING 1 @@ -190,99 +216,129 @@ struct output_sort { comparer_t func; struct output_sort *next; }; -/*! \struct configuration_t - * \brief Runtime configuration. + +/*! \struct conf_t + * \brief Runtime configuration state. */ -struct configuration_t { - char dhcpv6; - enum dhcp_version ip_version; - char *dhcpdconf_file; - char *dhcpdlease_file; - struct output_sort *sorts; - char *output_file; - double warning; - double critical; - double warn_count; - double crit_count; - double minsize; - unsigned int - reverse_order:1, - backups_found:1, - snet_alarms:1, - print_mac_addreses:1, - header_limit:3, - number_limit:3; +struct conf_t { + struct shared_network_t *shared_net_root; /*!< First entry in shared network linked list, that is the 'all networks', */ + struct shared_network_t *shared_net_head; /*!< Last entry in shared network linked list. */ + struct range_t *ranges; /*!< Array of ranges. */ + unsigned int num_ranges; /*!< Number of ranges in the ranges array. */ + enum dhcp_version ip_version; /*!< Designator if the dhcpd is running in IPv4 or IPv6 mode. */ + size_t ranges_size; /*!< Size of the ranges array. */ + struct leases_t *leases; /*!< An array of individual leases from dhcpd.leases file. */ + const char *dhcpdconf_file; /*!< Path to dhcpd.conf file. */ + const char *dhcpdlease_file; /*!< Path to dhcpd.leases file. */ + struct output_sort *sorts; /*!< Linked list how to sort ranges. */ + const char *output_file; /*!< Output file path. */ + const char *mustach_template; /*!< Mustach template file path. */ + double warning; /*!< Warning percent threshold. */ + double critical; /*!< Critical percent threshold. */ + double warn_count; /*!< Maximum number of free IP's before warning. */ + double crit_count; /*!< Maximum number of free IP's before critical. */ + double minsize; /*!< Minimum size of range or shared network to be considered exceeding threshold. */ + int color_format; /*!< Column to use in color_tags array. */ + char output_format; /*!< Output format, such as text, json, xml, .... */ + uint32_t + print_mac_addreses:1, /*!< Print mac address in xml or json. */ + reverse_order:1, /*!< Reverse sort order. */ + backups_found:1, /*!< Indicator if dhcpd.leases file has leases in backup state. */ + snet_alarms:1, /*!< Suppress alarming thresholds for ranges that are part of a shared network. */ + perfdata:1, /*!< Include performance statistics when using Nagios alarm output format. */ + all_as_shared:1, /*!< Treat stand-alone subnets as a shared network. */ + header_limit:4, /*!< Bits to suppress header output. */ + number_limit:3, /*!< Bits to suppress value output. */ + skip_ok:1, /*!< Skip none-alarming values from output. */ + skip_warning:1, /*!< Skip warning values from output. */ + skip_critical:1, /*!< Skip critical values from output. */ + skip_minsize:1, /*!< Skip alarming values that are below minsize from output. */ + skip_suppressed:1, /*!< Skip alarming values that are suppressed with --snet-alarms option, or they are shared networks without IP availability. */ + color_mode:2; /*!< Indicator if colors should be used in output. */ }; -/* Global variables */ -/* \var prefix_length Length of each prefix. */ -extern int prefix_length[2][NUM_OF_PREFIX]; -/* \var config Runtime configuration. */ -extern struct configuration_t config; -/* \var shared_networks Pointer holding shared network count results. */ -extern struct shared_network_t *shared_networks; -/* \var num_shared_networks Number of shared networks found. */ -extern unsigned int num_shared_networks; -/* \var ranges Pointer holding range count results. */ -extern struct range_t *ranges; -/* \var num_ranges Number of ranges found. */ -extern unsigned int num_ranges; -/* \var leases Pointer holding all leases. */ -extern struct leases_t *leases; -/*! \var RANGES Maximum number of ranges. */ -extern unsigned int RANGES; /* Function prototypes */ -extern void prepare_memory(void); -extern void set_ipv_functions(int version); -extern int parse_leases(void); -extern void parse_config(int, const char *__restrict, struct shared_network_t *__restrict) - __attribute__ ((nonnull(2, 3))); -extern void prepare_data(void); -extern void do_counting(void); -extern void flip_ranges(struct range_t *__restrict ranges, struct range_t *__restrict tmp_ranges) - __attribute__ ((nonnull(1, 2))); -/* support functions */ -extern int (*parse_ipaddr) (const char *restrict src, union ipaddr_t *restrict dst); -extern int parse_ipaddr_init(const char *restrict src, - union ipaddr_t *restrict dst) _DP_ATTRIBUTE_CONST; -extern int parse_ipaddr_v4(const char *restrict src, union ipaddr_t *restrict dst); -extern int parse_ipaddr_v6(const char *restrict src, union ipaddr_t *restrict dst); + +/* analyze.c */ +extern void prepare_data(struct conf_t *state); +extern void do_counting(struct conf_t *state); + +/* getdata.c */ +extern int parse_leases(struct conf_t *state); +extern void parse_config(struct conf_t *state, const int is_include, + const char *restrict config_file, + struct shared_network_t *restrict shared_p); + +/* hash.c */ +extern void (*add_lease) (struct conf_t *state, union ipaddr_t *addr, enum ltype type); +extern void add_lease_init(struct conf_t *state, union ipaddr_t *addr, enum ltype type); +extern void add_lease_v4(struct conf_t *state, union ipaddr_t *addr, enum ltype type); +extern void add_lease_v6(struct conf_t *state, union ipaddr_t *addr, enum ltype type); + +extern struct leases_t *(*find_lease) (struct conf_t *state, union ipaddr_t *addr); +extern struct leases_t *find_lease_init(struct conf_t *state, union ipaddr_t *addr); +extern struct leases_t *find_lease_v4(struct conf_t *state, union ipaddr_t *addr); +extern struct leases_t *find_lease_v6(struct conf_t *state, union ipaddr_t *addr); + +extern void delete_lease(struct conf_t *state, struct leases_t *lease); +extern void delete_all_leases(struct conf_t *state); + +/* mustach-dhcpd-pools.c */ +extern int mustach_dhcpd_pools(struct conf_t *state); + +/* other.c */ +extern void set_ipv_functions(struct conf_t *state, int version); +extern void flip_ranges(struct conf_t *state); +extern void clean_up(struct conf_t *state); +extern void parse_cidr(struct conf_t *state, struct range_t *range_p, const char *word); +extern int parse_color_mode(const char *restrict arg); +extern double strtod_or_err(const char *restrict str, const char *restrict errmesg); +extern void __attribute__ ((noreturn)) print_version(void); +extern void __attribute__ ((noreturn)) usage(int status); +extern void dp_time_tool(FILE *file, const char *path, int epoch); + +extern int (*parse_ipaddr) (struct conf_t *state, const char *restrict src, + union ipaddr_t *restrict dst); +extern int parse_ipaddr_init(struct conf_t *state, const char *restrict src, + union ipaddr_t *restrict dst); +extern int parse_ipaddr_v4(struct conf_t *state, const char *restrict src, + union ipaddr_t *restrict dst); +extern int parse_ipaddr_v6(struct conf_t *state, const char *restrict src, + union ipaddr_t *restrict dst); + +extern int (*xstrstr) (struct conf_t *state, const char *restrict str); +extern int xstrstr_init(struct conf_t *state, const char *restrict str); +extern int xstrstr_v4(struct conf_t *state, const char *restrict str); +extern int xstrstr_v6(struct conf_t *state, const char *restrict str); extern void (*copy_ipaddr) (union ipaddr_t *restrict dst, const union ipaddr_t *restrict src); -extern void copy_ipaddr_init(union ipaddr_t *restrict dst, - const union ipaddr_t *restrict src) _DP_ATTRIBUTE_CONST; +extern void copy_ipaddr_init(union ipaddr_t *restrict dst, const union ipaddr_t *restrict src); extern void copy_ipaddr_v4(union ipaddr_t *restrict dst, const union ipaddr_t *restrict src); extern void copy_ipaddr_v6(union ipaddr_t *restrict dst, const union ipaddr_t *restrict src); extern const char *(*ntop_ipaddr) (const union ipaddr_t *ip); -extern const char *ntop_ipaddr_init(const union ipaddr_t *ip) _DP_ATTRIBUTE_CONST; +extern const char *ntop_ipaddr_init(const union ipaddr_t *ip); extern const char *ntop_ipaddr_v4(const union ipaddr_t *ip); extern const char *ntop_ipaddr_v6(const union ipaddr_t *ip); extern double (*get_range_size) (const struct range_t *r); -extern double get_range_size_init(const struct range_t *r) _DP_ATTRIBUTE_CONST; -extern double get_range_size_v4(const struct range_t *r) _DP_ATTRIBUTE_PURE; -extern double get_range_size_v6(const struct range_t *r) _DP_ATTRIBUTE_PURE; +extern double get_range_size_init(const struct range_t *r); +extern double get_range_size_v4(const struct range_t *r); +extern double get_range_size_v6(const struct range_t *r); -extern int (*xstrstr) (const char *__restrict str); -extern int xstrstr_init(const char *__restrict str) _DP_ATTRIBUTE_CONST; -extern int xstrstr_v4(const char *__restrict str) -_DP_ATTRIBUTE_HOT _DP_ATTRIBUTE_PURE; -extern int xstrstr_v6(const char *__restrict str) -_DP_ATTRIBUTE_HOT _DP_ATTRIBUTE_PURE; +/* output.c */ +extern int range_output_helper(struct conf_t *state, struct output_helper_t *oh, + struct range_t *range_p); +extern int shnet_output_helper(struct conf_t *state, struct output_helper_t *oh, + struct shared_network_t *shared_p); +extern int output_analysis(struct conf_t *state); +extern void range_alarms(struct conf_t *state, struct status_counts_t *rangstat); +extern void shared_net_alarms(struct conf_t *state, struct status_counts_t *sharstat); -extern double strtod_or_err(const char *__restrict str, const char *__restrict errmesg); -extern void print_version(void) __attribute__ ((noreturn)); -extern void usage(int status) __attribute__ ((noreturn)); -/* qsort required functions... */ -/* ...for ranges and... */ -extern int (*ipcomp) (const union ipaddr_t *restrict a, const union ipaddr_t *restrict b); -extern int ipcomp_init(const union ipaddr_t *restrict a, - const union ipaddr_t *restrict b) _DP_ATTRIBUTE_CONST; -extern int ipcomp_v4(const union ipaddr_t *restrict a, - const union ipaddr_t *restrict b) _DP_ATTRIBUTE_PURE; -extern int ipcomp_v6(const union ipaddr_t *restrict a, - const union ipaddr_t *restrict b) _DP_ATTRIBUTE_PURE; +/* sort.c */ +extern void mergesort_ranges(struct conf_t *state, + struct range_t *restrict orig, unsigned int size, + struct range_t *restrict temp, const int root_call); extern int (*leasecomp) (const struct leases_t *restrict a, const struct leases_t *restrict b); extern int leasecomp_init(const struct leases_t *restrict a @@ -291,46 +347,26 @@ extern int leasecomp_init(const struct leases_t *restrict a extern int leasecomp_v4(const struct leases_t *restrict a, const struct leases_t *restrict b); extern int leasecomp_v6(const struct leases_t *restrict a, const struct leases_t *restrict b); -extern int comp_cur(struct range_t *r1, struct range_t *r2) _DP_ATTRIBUTE_PURE; -extern int comp_double(double f1, double f2) _DP_ATTRIBUTE_CONST; +extern int (*ipcomp) (const union ipaddr_t *restrict a, const union ipaddr_t *restrict b); +extern int ipcomp_init(const union ipaddr_t *restrict a, const union ipaddr_t *restrict b); +extern int ipcomp_v4(const union ipaddr_t *restrict a, const union ipaddr_t *restrict b); +extern int ipcomp_v6(const union ipaddr_t *restrict a, const union ipaddr_t *restrict b); + +extern int rangecomp(const void *restrict r1, const void *restrict r2) + __attribute__ ((nonnull(1, 2))); + +extern int comp_cur(struct range_t *r1, struct range_t *r2); +extern int comp_double(double f1, double f2); extern int comp_ip(struct range_t *r1, struct range_t *r2); extern int comp_max(struct range_t *r1, struct range_t *r2); extern int comp_percent(struct range_t *r1, struct range_t *r2); -extern int comp_tc(struct range_t *r1, struct range_t *r2) _DP_ATTRIBUTE_PURE; +extern int comp_tc(struct range_t *r1, struct range_t *r2); extern int comp_tcperc(struct range_t *r1, struct range_t *r2); -extern int comp_touched(struct range_t *r1, struct range_t *r2) _DP_ATTRIBUTE_PURE; -extern int rangecomp(const void *__restrict r1, const void *__restrict r2) - __attribute__ ((nonnull(1, 2))); -/* sort function pointer and functions */ +extern int comp_touched(struct range_t *r1, struct range_t *r2); + extern comparer_t field_selector(char c); extern double ret_percent(struct range_t r); -extern double ret_tc(struct range_t r) _DP_ATTRIBUTE_CONST; +extern double ret_tc(struct range_t r); extern double ret_tcperc(struct range_t r); -extern void mergesort_ranges(struct range_t *__restrict orig, int size, - struct range_t *__restrict temp) - __attribute__ ((nonnull(1, 3))); -/* output function pointer and functions */ -extern int (*output_analysis) (void); -extern int output_txt(void); -extern int output_html(void); -extern int output_xml(void); -extern int output_json(void); -extern int output_csv(void); -extern int output_alarming(void); -/* Memory release, file closing etc */ -extern void clean_up(void); -/* Hash functions */ -extern void (*add_lease) (union ipaddr_t *ip, enum ltype type); -extern void add_lease_init(union ipaddr_t *ip, enum ltype type) _DP_ATTRIBUTE_CONST; -extern void add_lease_v4(union ipaddr_t *ip, enum ltype type); -extern void add_lease_v6(union ipaddr_t *ip, enum ltype type); -extern struct leases_t *(*find_lease) (union ipaddr_t *ip); -extern struct leases_t *find_lease_init(union ipaddr_t *ip) _DP_ATTRIBUTE_CONST; -extern struct leases_t *find_lease_v4(union ipaddr_t *ip) _DP_ATTRIBUTE_PURE; -extern struct leases_t *find_lease_v6(union ipaddr_t *ip) _DP_ATTRIBUTE_PURE; - -extern void delete_lease(struct leases_t *lease); -extern void delete_all_leases(void); - -#endif /* DHCPD_POOLS_H */ +#endif /* DHCPD_POOLS_H */ diff --git a/src/getdata.c b/src/getdata.c index 03a0b13..31734de 100644 --- a/src/getdata.c +++ b/src/getdata.c @@ -43,8 +43,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -55,88 +55,125 @@ #include "xalloc.h" #include "dhcpd-pools.h" -#include "defaults.h" + +/*! \enum dhcpd_magic_numbers + * \brief MAXLEN is maximum expected line length in dhcpd.conf and + * dhcpd.leases. + */ +enum dhcpd_magic_numbers { + MAXLEN = 1024 +}; + +/*! \enum isc_conf_parser + * \brief Configuration file parsing state flags. The + * is_interesting_config_clause() will return one of these to parse_config(). + */ +enum isc_conf_parser { + ITS_NOTHING_INTERESTING, + ITS_A_RANGE_FIRST_IP, + ITS_A_RANGE_SECOND_IP, + ITS_A_SHAREDNET, + ITS_AN_INCLUDE, + ITS_A_SUBNET, + ITS_A_NETMASK +}; /*! \brief Lease file parser. The parser can only read ISC DHCPD * dhcpd.leases file format. */ -int parse_leases(void) +int parse_leases(struct conf_t *state) { FILE *dhcpd_leases; - char *line, *ipstring, macstring[20], *stop; + char *line, *ipstring, macstring[20], *stop, endsstr[30], startsstr[30], hostnamestr[MAXLEN]; union ipaddr_t addr; - struct stat lease_file_stats; - bool ethernets = false; struct leases_t *lease; - dhcpd_leases = fopen(config.dhcpdlease_file, "r"); + dhcpd_leases = fopen(state->dhcpdlease_file, "r"); if (dhcpd_leases == NULL) - error(EXIT_FAILURE, errno, "parse_leases: %s", config.dhcpdlease_file); + error(EXIT_FAILURE, errno, "parse_leases: %s", state->dhcpdlease_file); #ifdef HAVE_POSIX_FADVISE -# ifdef POSIX_FADV_NOREUSE - if (posix_fadvise(fileno(dhcpd_leases), 0, 0, POSIX_FADV_NOREUSE) != 0) - error(EXIT_FAILURE, errno, "parse_leases: fadvise %s", config.dhcpdlease_file); -# endif /* POSIX_FADV_NOREUSE */ # ifdef POSIX_FADV_SEQUENTIAL if (posix_fadvise(fileno(dhcpd_leases), 0, 0, POSIX_FADV_SEQUENTIAL) != 0) - error(EXIT_FAILURE, errno, "parse_leases: fadvise %s", config.dhcpdlease_file); + error(EXIT_FAILURE, errno, "parse_leases: fadvise %s", state->dhcpdlease_file); # endif /* POSIX_FADV_SEQUENTIAL */ #endif /* HAVE_POSIX_FADVISE */ - /* I found out that there's one lease address per 300 bytes in - * dhcpd.leases file. Malloc is little bit pessimistic and uses 250. - * If someone has higher density in lease file I'm interested to - * hear about that. */ - if (stat(config.dhcpdlease_file, &lease_file_stats)) - error(EXIT_FAILURE, errno, "parse_leases: %s", config.dhcpdlease_file); line = xmalloc(sizeof(char) * MAXLEN); line[0] = '\0'; ipstring = xmalloc(sizeof(char) * MAXLEN); ipstring[0] = '\0'; - if (config.print_mac_addreses == 1) - ethernets = true; + endsstr[0] = '\0'; + startsstr[0] = '\0'; + hostnamestr[0] = '\0'; while (!feof(dhcpd_leases)) { if (!fgets(line, MAXLEN, dhcpd_leases) && ferror(dhcpd_leases)) - error(EXIT_FAILURE, errno, "parse_leases: %s", config.dhcpdlease_file); - switch (xstrstr(line)) { + error(EXIT_FAILURE, errno, "parse_leases: %s", state->dhcpdlease_file); + switch (xstrstr(state, line)) { /* It's a lease, save IP */ case PREFIX_LEASE: stop = memccpy(ipstring, - line + (config.ip_version == + line + (state->ip_version == IPv4 ? 6 : 9), ' ', strlen(line)); if (stop != NULL) { --stop; *stop = '\0'; } - parse_ipaddr(ipstring, &addr); + parse_ipaddr(state, ipstring, &addr); break; case PREFIX_BINDING_STATE_FREE: case PREFIX_BINDING_STATE_ABANDONED: case PREFIX_BINDING_STATE_EXPIRED: case PREFIX_BINDING_STATE_RELEASED: - if ((lease = find_lease(&addr)) != NULL) - delete_lease(lease); - add_lease(&addr, FREE); + if ((lease = find_lease(state, &addr)) != NULL) + delete_lease(state, lease); + add_lease(state, &addr, FREE); break; case PREFIX_BINDING_STATE_ACTIVE: /* remove old entry, if exists */ - if ((lease = find_lease(&addr)) != NULL) - delete_lease(lease); - add_lease(&addr, ACTIVE); + if ((lease = find_lease(state, &addr)) != NULL) + delete_lease(state, lease); + add_lease(state, &addr, ACTIVE); break; case PREFIX_BINDING_STATE_BACKUP: /* remove old entry, if exists */ - if ((lease = find_lease(&addr)) != NULL) - delete_lease(lease); - add_lease(&addr, BACKUP); - config.backups_found = true; + if ((lease = find_lease(state, &addr)) != NULL) + delete_lease(state, lease); + add_lease(state, &addr, BACKUP); + state->backups_found = 1; break; case PREFIX_HARDWARE_ETHERNET: - if (ethernets == false) + if (state->print_mac_addreses == 0) break; memcpy(macstring, line + 20, 17); macstring[17] = '\0'; - if ((lease = find_lease(&addr)) != NULL) + if ((lease = find_lease(state, &addr)) != NULL) { lease->ethernet = xstrdup(macstring); + lease->starts = xstrdup(startsstr); + startsstr[0] = '\0'; + lease->ends = xstrdup(endsstr); + endsstr[0] = '\0'; + } + break; + case PREFIX_ENDS: + if (state->print_mac_addreses == 0) + break; + strncpy(endsstr, line + 7, sizeof(endsstr)-1); + endsstr[strlen(endsstr)-2] = '\0'; + break; + case PREFIX_STARTS: + if (state->print_mac_addreses == 0) + break; + strncpy(startsstr, line + 9, sizeof(startsstr)-1); + startsstr[strlen(startsstr)-2] = '\0'; + break; + case PREFIX_HOSTNAME: + if (state->print_mac_addreses == 0) + break; + strncpy(hostnamestr, line + 19, sizeof(hostnamestr)-1); + hostnamestr[strlen(hostnamestr)-3] = '\0'; + if ((lease = find_lease(state, &addr)) != NULL) { + lease->hostname = xstrdup(hostnamestr); + } + hostnamestr[0] = '\0'; break; default: /* do nothing */ ; @@ -152,26 +189,45 @@ int parse_leases(void) /*! \brief Keyword search in dhcpd.conf file. * \param s A line from the dhcpd.conf file. * \return Indicator what configuration was found. */ -static int is_interesting_config_clause(char const *restrict s) +static int is_interesting_config_clause(struct conf_t *state, char const *restrict s) { if (strstr(s, "range")) return ITS_A_RANGE_FIRST_IP; if (strstr(s, "shared-network")) return ITS_A_SHAREDNET; + if (state->all_as_shared) { + if (strstr(s, "subnet")) + return ITS_A_SUBNET; + if (strstr(s, "netmask")) + return ITS_A_NETMASK; + } if (strstr(s, "include")) - return ITS_AN_INCLUCE; + return ITS_AN_INCLUDE; return ITS_NOTHING_INTERESTING; } -/*! \brief The dhcpd.conf file parser. - * FIXME: This spaghetti monster function need to be rewrote at least - * ones. +/*! \brief Flip first and last IP in range if they are in unusual order. */ -void parse_config(int is_include, const char *restrict config_file, +static void reorder_last_first(struct range_t *range_p) +{ + if (ipcomp(&range_p->first_ip, &range_p->last_ip) > 0) { + union ipaddr_t tmp; + + tmp = range_p->first_ip; + range_p->first_ip = range_p->last_ip; + range_p->last_ip = tmp; + } +} + +/*! \brief The dhcpd.conf file parser. + * FIXME: This spaghetti monster function needs to be rewrote at least + * ones more. + */ +void parse_config(struct conf_t *state, const int is_include, const char *restrict config_file, struct shared_network_t *restrict shared_p) { FILE *dhcpd_config; - bool newclause = true, comment = false, one_ip_range = false; + int newclause = 1, comment = 0, one_ip_range = 0; /* booleans */ int quote = 0, braces = 0, argument = ITS_NOTHING_INTERESTING; size_t i = 0; char *word; @@ -182,16 +238,18 @@ void parse_config(int is_include, const char *restrict config_file, word = xmalloc(sizeof(char) * MAXLEN); if (is_include) /* Default place holder for ranges "All networks". */ - shared_p->name = shared_networks->name; + shared_p->name = state->shared_net_root->name; /* Open configuration file */ dhcpd_config = fopen(config_file, "r"); - if (dhcpd_config == NULL) + if (dhcpd_config == NULL) { + if (is_include) { + error(0, errno, "cannot open inlude: %s", config_file); + return; + } + /* config if from command line, just exit with error */ error(EXIT_FAILURE, errno, "parse_config: %s", config_file); + } #ifdef HAVE_POSIX_FADVISE -# ifdef POSIX_FADV_NOREUSE - if (posix_fadvise(fileno(dhcpd_config), 0, 0, POSIX_FADV_NOREUSE) != 0) - error(EXIT_FAILURE, errno, "parse_config: fadvise %s", config_file); -# endif /* POSIX_FADV_NOREUSE */ # ifdef POSIX_FADV_SEQUENTIAL if (posix_fadvise(fileno(dhcpd_config), 0, 0, POSIX_FADV_SEQUENTIAL) != 0) error(EXIT_FAILURE, errno, "parse_config: fadvise %s", config_file); @@ -199,18 +257,20 @@ void parse_config(int is_include, const char *restrict config_file, #endif /* HAVE_POSIX_FADVISE */ /* Very hairy stuff begins. */ while (unlikely(!feof(dhcpd_config))) { - char c; + int c; c = fgetc(dhcpd_config); + if (CHAR_MAX < c) + continue; /* Certain characters are magical */ switch (c) { /* Handle comments if they are not quoted */ case '#': if (quote == 0) - comment = true; + comment = 1; continue; case '"': - if (comment == false) { + if (comment == 0) { quote++; /* Either one or zero */ quote = quote % 2; @@ -220,18 +280,18 @@ void parse_config(int is_include, const char *restrict config_file, /* New line resets comment section, but * not if quoted */ if (quote == 0) - comment = false; + comment = 0; break; case ';': /* Quoted colon does not mean new clause */ if (0 < quote) break; - if (comment == false + if (comment == 0 && argument != ITS_A_RANGE_FIRST_IP - && argument != ITS_A_RANGE_SECOND_IP && argument != ITS_AN_INCLUCE) { - newclause = true; + && argument != ITS_A_RANGE_SECOND_IP && argument != ITS_AN_INCLUDE) { + newclause = 1; i = 0; - } else if (argument == ITS_A_RANGE_FIRST_IP && one_ip_range == true) { + } else if (argument == ITS_A_RANGE_FIRST_IP && one_ip_range == 1) { argument = ITS_A_RANGE_SECOND_IP; c = ' '; } else if (argument == ITS_A_RANGE_SECOND_IP && 0 < i) { @@ -245,8 +305,9 @@ void parse_config(int is_include, const char *restrict config_file, break; } else if (argument == ITS_A_RANGE_SECOND_IP && i == 0) { if (!range_p) { - puts("parse_config: range_p uninitialized: report a bug"); - abort(); + long int pos; + pos = ftell(dhcpd_config); + error(EXIT_FAILURE, 0, "parse_config: parsing failed at position: %ld", pos); } range_p->last_ip = range_p->first_ip; goto newrange; @@ -261,123 +322,150 @@ void parse_config(int is_include, const char *restrict config_file, * * shared-network DSL{ ... */ if (i == 0) { - newclause = true; + newclause = 1; continue; - } else - break; + } + break; case '}': if (0 < quote) break; - if (comment == false) { + if (comment == 0) { braces--; /* End of shared-network */ if (braces_shared == braces) { /* FIXME: Using 1000 is lame, but * works. */ braces_shared = 1000; - shared_p = shared_networks; + shared_p = state->shared_net_root; } - /* Not literally true, but works for this + /* Not literally 1, but works for this * program */ - newclause = true; + newclause = 1; } continue; default: break; } /* Either inside comment or Nth word of clause. */ - if (comment == true || (newclause == false && argument == ITS_NOTHING_INTERESTING)) + if (comment == 1 || (newclause == 0 && argument == ITS_NOTHING_INTERESTING)) continue; /* Strip white spaces before new clause word. */ - if ((newclause == true || argument != ITS_NOTHING_INTERESTING) - && isspace(c) && i == 0 && one_ip_range == false) + if ((newclause == 1 || argument != ITS_NOTHING_INTERESTING) + && isspace(c) && i == 0 && one_ip_range == 0) continue; /* Save to word which clause this is. */ - if ((newclause == true || argument != ITS_NOTHING_INTERESTING) + if ((newclause == 1 || argument != ITS_NOTHING_INTERESTING) && (!isspace(c) || 0 < quote)) { - word[i] = c; + word[i] = (char) c; i++; /* Long word which is almost causing overflow. None * of words are this long which the program is * searching. */ if (MAXLEN == i) { - newclause = false; + newclause = 0; i = 0; continue; } } /* See if clause is something that parser is looking for. */ - else if (newclause == true) { + else if (newclause == 1) { /* Insert string end & set state */ word[i] = '\0'; if (word[i - 1] != '{') - newclause = false; + newclause = 0; i = 0; - argument = is_interesting_config_clause(word); + argument = is_interesting_config_clause(state, word); if (argument == ITS_A_RANGE_FIRST_IP) - one_ip_range = true; + one_ip_range = 1; } /* words after range, shared-network or include */ else if (argument != ITS_NOTHING_INTERESTING) { word[i] = '\0'; - newclause = false; + newclause = 0; i = 0; switch (argument) { case ITS_A_RANGE_SECOND_IP: /* printf ("range 2nd ip: %s\n", word); */ - range_p = ranges + num_ranges; + range_p = state->ranges + state->num_ranges; argument = ITS_NOTHING_INTERESTING; - parse_ipaddr(word, &addr); - if (one_ip_range == true) { - one_ip_range = false; - copy_ipaddr(&range_p->first_ip, &addr); + if (strchr(word, '/')) { + parse_cidr(state, range_p, word); + one_ip_range = 0; + } else { + /* not cidr */ + parse_ipaddr(state, word, &addr); + if (one_ip_range == 1) { + one_ip_range = 0; + copy_ipaddr(&range_p->first_ip, &addr); + } + copy_ipaddr(&range_p->last_ip, &addr); + reorder_last_first(range_p); } - copy_ipaddr(&range_p->last_ip, &addr); newrange: range_p->count = 0; range_p->touched = 0; range_p->backups = 0; range_p->shared_net = shared_p; - num_ranges++; - if (RANGES < num_ranges + 1) { - RANGES *= 2; - ranges = xrealloc(ranges, sizeof(struct range_t) * RANGES); - range_p = ranges + num_ranges; + state->num_ranges++; + if (state->ranges_size <= state->num_ranges) { + state->ranges_size *= 2; + state->ranges = xrealloc(state->ranges, sizeof(struct range_t) * state->ranges_size); + range_p = state->ranges + state->num_ranges; } - newclause = true; + newclause = 1; break; case ITS_A_RANGE_FIRST_IP: - /* printf ("range 1nd ip: %s\n", word); */ - range_p = ranges + num_ranges; - if (!(parse_ipaddr(word, &addr))) + /* printf ("range 1st ip: %s\n", word); */ + range_p = state->ranges + state->num_ranges; + if (!(parse_ipaddr(state, word, &addr))) /* word was not ip, try again */ break; copy_ipaddr(&range_p->first_ip, &addr); - one_ip_range = false; + one_ip_range = 0; argument = ITS_A_RANGE_SECOND_IP; break; case ITS_A_SHAREDNET: - /* printf ("shared-network named: %s\n", word); */ - num_shared_networks++; - shared_p = shared_networks + num_shared_networks; + case ITS_A_SUBNET: + /* ignore subnets inside a shared-network */ + if (argument == ITS_A_SUBNET && shared_p != state->shared_net_root) { + argument = ITS_NOTHING_INTERESTING; + break; + } + state->shared_net_head->next = xcalloc(sizeof(struct shared_network_t), 1); + state->shared_net_head = state->shared_net_head->next; + shared_p = state->shared_net_head; shared_p->name = xstrdup(word); - shared_p->available = 0; - shared_p->used = 0; - shared_p->touched = 0; - shared_p->backups = 0; - if (SHARED_NETWORKS < num_shared_networks + 2) - /* FIXME: make this to go away by reallocating more space. */ - error(EXIT_FAILURE, 0, - "parse_config: increase default.h SHARED_NETWORKS and recompile"); + shared_p->netmask = (argument == ITS_A_SUBNET ? -1 : 0); /* do not fill in netmask */ + /* record network's mask too */ + if (argument == ITS_A_SUBNET) + newclause = 1; argument = ITS_NOTHING_INTERESTING; braces_shared = braces; break; - case ITS_AN_INCLUCE: + case ITS_A_NETMASK: + /* fill in only when requested to do so */ + if (shared_p->netmask) { + if (!(parse_ipaddr(state, word, &addr))) + break; + shared_p->netmask = 32; + while ((addr.v4 & 0x01) == 0) { + addr.v4 >>= 1; + shared_p->netmask--; + } + snprintf(word, MAXLEN-1, "%s/%d", shared_p->name, shared_p->netmask); + if (shared_p->name) + free(shared_p->name); + shared_p->name = xstrdup(word); + } + argument = ITS_NOTHING_INTERESTING; + braces_shared = braces; + break; + case ITS_AN_INCLUDE: /* printf ("include file: %s\n", word); */ argument = ITS_NOTHING_INTERESTING; - parse_config(false, word, shared_p); - newclause = true; + parse_config(state, 0, word, shared_p); + newclause = 1; break; case ITS_NOTHING_INTERESTING: /* printf ("nothing interesting: %s\n", word); */ diff --git a/src/hash.c b/src/hash.c index 08b95b4..eb434df 100644 --- a/src/hash.c +++ b/src/hash.c @@ -40,6 +40,8 @@ #include +#include + #include "xalloc.h" #include "dhcpd-pools.h" @@ -50,88 +52,104 @@ /*! \brief Add a lease to hash array. * \param addr Binary IP to be added in leases hash. * \param type Lease state of the IP. */ -void add_lease_init(union ipaddr_t *addr +void add_lease_init(struct conf_t *state __attribute__ ((unused)), union ipaddr_t *addr __attribute__ ((unused)), enum ltype type __attribute__ ((unused))) { } -void add_lease_v4(union ipaddr_t *addr, enum ltype type) +void add_lease_v4(struct conf_t *state, union ipaddr_t *addr, enum ltype type) { struct leases_t *l; l = xmalloc(sizeof(struct leases_t)); copy_ipaddr(&l->ip, addr); l->type = type; - HASH_ADD_INT(leases, ip.v4, l); + HASH_ADD_INT(state->leases, ip.v4, l); l->ethernet = NULL; + l->ends = NULL; + l->starts = NULL; + l->hostname = NULL; } -void add_lease_v6(union ipaddr_t *addr, enum ltype type) +void add_lease_v6(struct conf_t *state, union ipaddr_t *addr, enum ltype type) { struct leases_t *l; l = xmalloc(sizeof(struct leases_t)); copy_ipaddr(&l->ip, addr); l->type = type; - HASH_ADD_V6(leases, ip.v6, l); + HASH_ADD_V6(state->leases, ip.v6, l); l->ethernet = NULL; + l->ends = NULL; + l->starts = NULL; + l->hostname = NULL; } /*! \brief Find pointer to lease from hash array. * \param addr Binary IP searched from leases hash. * \return A lease structure about requested IP, or NULL. */ -struct leases_t *find_lease_init(union ipaddr_t *addr __attribute__ ((unused))) +struct leases_t *find_lease_init(struct conf_t *state __attribute__ ((unused)), union ipaddr_t *addr + __attribute__ ((unused))) { return NULL; } -struct leases_t *find_lease_v4(union ipaddr_t *addr) +struct leases_t *find_lease_v4(struct conf_t *state, union ipaddr_t *addr) { struct leases_t *l; - HASH_FIND_INT(leases, &addr->v4, l); + HASH_FIND_INT(state->leases, &addr->v4, l); return l; } -struct leases_t *find_lease_v6(union ipaddr_t *addr) +struct leases_t *find_lease_v6(struct conf_t *state, union ipaddr_t *addr) { struct leases_t *l; - HASH_FIND_V6(leases, &addr->v4, l); + HASH_FIND_V6(state->leases, &addr->v4, l); return l; } /*! \brief Delete a lease from hash array. * \param lease Pointer to lease hash. */ -void delete_lease(struct leases_t *lease) +void delete_lease(struct conf_t *state, struct leases_t *lease) { free(lease->ethernet); - HASH_DEL(leases, lease); + free(lease->ends); + free(lease->starts); + free(lease->hostname); + HASH_DEL(state->leases, lease); free(lease); } /*! \brief Delete all leases from hash array. */ #ifdef HASH_ITER -void delete_all_leases(void) +void delete_all_leases(struct conf_t *state) { struct leases_t *l, *tmp; - HASH_ITER(hh, leases, l, tmp) { + HASH_ITER(hh, state->leases, l, tmp) { free(l->ethernet); - HASH_DEL(leases, l); + free(l->ends); + free(l->starts); + free(l->hostname); + HASH_DEL(state->leases, l); free(l); } } #else -void delete_all_leases(void) +void delete_all_leases(struct conf_t *state) { - while (leases) { + while (state->leases) { struct leases_t *l; - l = leases; + l = state->leases; free(l->ethernet); - HASH_DEL(leases, l); /* leases advances to next on delete */ + free(l->ends); + free(l->starts); + free(l->hostname); + HASH_DEL(state->leases, l); /* leases advances to next on delete */ free(l); } } diff --git a/src/mustach-dhcpd-pools.c b/src/mustach-dhcpd-pools.c new file mode 100644 index 0000000..625588e --- /dev/null +++ b/src/mustach-dhcpd-pools.c @@ -0,0 +1,519 @@ +/* + * The dhcpd-pools has BSD 2-clause license which also known as "Simplified + * BSD License" or "FreeBSD License". + * + * Copyright 2006- Sami Kerola. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of Sami Kerola. + */ + +/*! \file mustach-dhcpd-pools.c + * \brief Mustache templating specific functions. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "close-stream.h" +#include "dhcpd-pools.h" +#include "error.h" +#include "mustach.h" +#include "xalloc.h" + +/*! \struct expl + * \brief A structure that travels through mustach via closure void pointer. + */ +struct expl { + struct conf_t *state; + struct leases_t *lease_p; + struct range_t *range_p; + struct shared_network_t *shnet_p; + struct output_helper_t oh; + int current; + uint32_t pad; +}; + +static int must_enter(void *closure, const char *name); +static int must_leave(void *closure); + +/*! \brief Template base level tag parser and printer. */ +static int must_put_base(void *closure, const char *name, int escape + __attribute__ ((unused)), FILE *file) +{ + struct expl *e = closure; + + if (!strcmp(name, "localtime")) { + dp_time_tool(file, NULL, 0); + return 0; + } + if (!strcmp(name, "number_of_ranges")) { + fprintf(file, "%u", e->state->num_ranges); + return 0; + } + if (!strcmp(name, "number_of_ranges_warning")) { + struct status_counts_t stat = { 0 }; + + range_alarms(e->state, &stat); + fprintf(file, "%u", stat.warning); + return 0; + } + if (!strcmp(name, "number_of_ranges_critical")) { + struct status_counts_t stat = { 0 }; + + range_alarms(e->state, &stat); + fprintf(file, "%u", stat.critical); + return 0; + } + if (!strcmp(name, "number_of_shared_networks")) { + uint32_t num = 0; + struct shared_network_t *shared_p; + + for (shared_p = e->state->shared_net_root->next; shared_p; + shared_p = shared_p->next) { + num++; + } + fprintf(file, "%u", num); + return 0; + } + if (!strcmp(name, "number_of_shared_networks_warning")) { + struct status_counts_t stat = { 0 }; + + shared_net_alarms(e->state, &stat); + fprintf(file, "%u", stat.warning); + return 0; + } + if (!strcmp(name, "number_of_shared_networks_critical")) { + struct status_counts_t stat = { 0 }; + + shared_net_alarms(e->state, &stat); + fprintf(file, "%u", stat.critical); + return 0; + } + if (!strcmp(name, "version")) { + fprintf(file, "%s", PACKAGE_VERSION); + return 0; + } + /* lease file */ + if (!strcmp(name, "lease_file_path")) { + fprintf(file, "%s", e->state->dhcpdlease_file); + return 0; + } + if (!strcmp(name, "lease_file_local_mtime")) { + dp_time_tool(file, e->state->dhcpdlease_file, 0); + return 0; + } + if (!strcmp(name, "lease_file_epoch_mtime")) { + dp_time_tool(file, e->state->dhcpdlease_file, 1); + return 0; + } + /* conf file */ + if (!strcmp(name, "conf_file_path")) { + fprintf(file, "%s", e->state->dhcpdconf_file); + return 0; + } + if (!strcmp(name, "conf_file_local_mtime")) { + dp_time_tool(file, e->state->dhcpdconf_file, 0); + return 0; + } + if (!strcmp(name, "conf_file_epoch_mtime")) { + dp_time_tool(file, e->state->dhcpdconf_file, 1); + return 0; + } + /* template file */ + if (!strcmp(name, "template_file_path")) { + fprintf(file, "%s", e->state->mustach_template); + return 0; + } + if (!strcmp(name, "template_file_local_mtime")) { + dp_time_tool(file, e->state->mustach_template, 0); + return 0; + } + if (!strcmp(name, "template_file_epoch_mtime")) { + dp_time_tool(file, e->state->mustach_template, 1); + return 0; + } + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unexpected tag: %s", name); + return 1; +} + +/*! \struct mustach_itf + * \brief Mustach function pointers. */ +static struct mustach_itf itf = { + .start = NULL, + .enter = must_enter, + .put = must_put_base, + .next = NULL, + .leave = must_leave +}; + +/*! \brief Mustach active lease aka {{#active_lease}} tag parser and printer. */ +static int must_put_active_lease(void *closure, const char *name, int escape + __attribute__((unused)), FILE *file) +{ + struct expl *e = closure; + + if (!strcmp(name, "ip")) { + fprintf(file, "%s", ntop_ipaddr(&e->lease_p->ip)); + return 0; + } + if (!strcmp(name, "macaddress")) { + fprintf(file, "%s", e->lease_p->ethernet); + return 0; + } + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unexpected tag: %s", name); + return 1; +} + +/*! \brief A function to move to next lease when {{/active_lease}} is encountered. */ +static int must_next_active_lease(void *closure) +{ + struct expl *e = closure; + + e->lease_p = e->lease_p->hh.next; + if (e->lease_p == NULL) + return 0; + return 1; +} + + +/*! \brief Mustach range aka {{#subnets}} tag parser and printer. */ +static int must_put_range(void *closure, const char *name, int escape + __attribute__ ((unused)), FILE *file) +{ + struct expl *e = closure; + + if (!strcmp(name, "location")) { + fprintf(file, "%s", e->range_p->shared_net->name); + return 0; + } + if (!strcmp(name, "range")) { + fprintf(file, "%s - ", ntop_ipaddr(&e->range_p->first_ip)); + fprintf(file, "%s", ntop_ipaddr(&e->range_p->last_ip)); + return 0; + } + if (!strcmp(name, "first_ip")) { + fprintf(file, "%s", ntop_ipaddr(&e->range_p->first_ip)); + return 0; + } + if (!strcmp(name, "last_ip")) { + fprintf(file, "%s", ntop_ipaddr(&e->range_p->last_ip)); + return 0; + } + if (!strcmp(name, "used")) { + fprintf(file, "%g", e->range_p->count); + return 0; + } + if (!strcmp(name, "touched")) { + fprintf(file, "%g", e->range_p->touched); + return 0; + } + if (!strcmp(name, "defined")) { + fprintf(file, "%g", e->oh.range_size); + return 0; + } + if (!strcmp(name, "free")) { + fprintf(file, "%g", e->oh.range_size - e->range_p->count); + return 0; + } + if (!strcmp(name, "percent")) { + fprintf(file, "%g", e->oh.percent); + return 0; + } + if (!strcmp(name, "touch_count")) { + fprintf(file, "%g", e->oh.tc); + return 0; + } + if (!strcmp(name, "touch_percent")) { + fprintf(file, "%g", e->oh.tcp); + return 0; + } + if (e->state->backups_found == 1) { + if (!strcmp(name, "backup_count")) { + fprintf(file, "%g", e->range_p->backups); + return 0; + } + if (!strcmp(name, "backup_percent")) { + fprintf(file, "%g", e->oh.bup); + return 0; + } + } + if (!strcmp(name, "status")) { + fprintf(file, "%d", e->oh.status); + return 0; + } + if (!strcmp(name, "gettimeofday")) { + dp_time_tool(file, NULL, 1); + return 0; + } + if (!strcmp(name, "lease_file_epoch_mtime")) { + dp_time_tool(file, e->state->dhcpdlease_file, 1); + return 0; + } + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unexpected tag: %s", name); + return 1; +} + +/*! \brief Mustach shared networks aka {{#shared-networks}} tag parser and printer. */ +static int must_put_shnet(void *closure, const char *name, int escape + __attribute__ ((unused)), FILE *file) +{ + struct expl *e = closure; + + if (!strcmp(name, "location")) { + fprintf(file, "%s", e->shnet_p->name); + return 0; + } + if (!strcmp(name, "defined")) { + fprintf(file, "%g", e->shnet_p->available); + return 0; + } + if (!strcmp(name, "used")) { + fprintf(file, "%g", e->shnet_p->used); + return 0; + } + if (!strcmp(name, "touched")) { + fprintf(file, "%g", e->shnet_p->touched); + return 0; + } + if (!strcmp(name, "free")) { + fprintf(file, "%g", e->shnet_p->available - e->shnet_p->used); + return 0; + } + if (!strcmp(name, "percent")) { + fprintf(file, "%g", e->oh.percent); + return 0; + } + if (!strcmp(name, "touch_count")) { + fprintf(file, "%g", e->oh.tc); + return 0; + } + if (!strcmp(name, "touch_percent")) { + fprintf(file, "%g", e->oh.tcp); + return 0; + } + if (e->state->backups_found == 1) { + if (!strcmp(name, "backup_count")) { + fprintf(file, "%g", e->shnet_p->backups); + return 0; + } + if (!strcmp(name, "backup_percent")) { + fprintf(file, "%g", e->oh.bup); + return 0; + } + } + if (!strcmp(name, "status")) { + fprintf(file, "%d", e->oh.status); + return 0; + } + if (!strcmp(name, "gettimeofday")) { + dp_time_tool(file, NULL, 1); + return 0; + } + if (!strcmp(name, "lease_file_epoch_mtime")) { + dp_time_tool(file, e->state->dhcpdlease_file, 1); + return 0; + } + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unexpected tag: %s", name); + return 1; +} + +/*! \brief A function to move to next range when {{/subnets}} is encountered. */ +static int must_next_range(void *closure) +{ + struct expl *e = closure; + + do { + e->range_p++; + e->current--; + if (e->current <= 0) + return 0; + } while (range_output_helper(e->state, &e->oh, e->range_p)); + return 1; +} + +/*! \brief A function to move to next shared network when {{/shared-networks}} + * is encountered. */ +static int must_next_shnet(void *closure) +{ + struct expl *e = closure; + + if (e->current == 1 || e->shnet_p == NULL) + return 0; + while (1) { + e->shnet_p = e->shnet_p->next; + if (e->shnet_p == NULL) + break; + if (shnet_output_helper(e->state, &e->oh, e->shnet_p)) + continue; + else + return 1; + } + return 0; +} + +/*! \brief Function that is called when mustach is searching output loops from + * template file. */ +static int must_enter(void *closure, const char *name) +{ + struct expl *e = closure; + + if (!strcmp(name, "active_lease")) { + itf.put = must_put_active_lease; + itf.next = must_next_active_lease; + e->current = 0; + e->lease_p = e->state->leases; + return must_next_active_lease(closure); + } + if (!strcmp(name, "subnets")) { + itf.put = must_put_range; + itf.next = must_next_range; + e->current = e->state->num_ranges + 1; + e->range_p = e->state->ranges; + /* must_next_range() will skip_ok when needed */ + e->range_p--; + return must_next_range(closure); + } + if (!strcmp(name, "shared-networks")) { + itf.put = must_put_shnet; + itf.next = must_next_shnet; + e->shnet_p = e->state->shared_net_root; + e->current = 0; + return must_next_shnet(closure); + } + if (!strcmp(name, "summary")) { + itf.put = must_put_shnet; + itf.next = must_next_shnet; + e->shnet_p = e->state->shared_net_root; + e->current = 1; + shnet_output_helper(e->state, &e->oh, e->shnet_p); + return 1; + } + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unexpected tag: %s", name); + return 1; +} + +/*! \brief Function that is called when all elements within a print loop are outputted. */ +static int must_leave(void *closure) +{ + struct expl *e = closure; + + e->shnet_p = e->state->shared_net_root; + e->range_p = e->state->ranges; + itf.put = must_put_base; + return 0; +} + +/*! \brief Read mustach template to memory. */ +static char *must_read_template(const char *filename) +{ + int f; + struct stat s; + char *result; + + if (filename == NULL) + error(EXIT_FAILURE, 0, "must_read_template: --mustach argument missing"); + if ((f = open(filename, O_RDONLY)) < 0) { + error(EXIT_FAILURE, errno, "must_read_template: open: %s", filename); + } + fstat(f, &s); + result = xmalloc(s.st_size + 1); + if (read(f, result, s.st_size) != s.st_size) { + error(EXIT_FAILURE, errno, "must_read_template: read: %s", filename); + } + close(f); + result[s.st_size] = 0; + return result; +} + +/*! \brief Start mustach processing. */ +int mustach_dhcpd_pools(struct conf_t *state) +{ + struct expl e = { .state = state }; + char *template; + FILE *outfile; + int ret; + + template = must_read_template(state->mustach_template); + if (state->output_file) { + outfile = fopen(state->output_file, "w+"); + if (outfile == NULL) { + error(EXIT_FAILURE, errno, "mustach_dhcpd_pools: fopen: %s", + state->output_file); + } + } else { + outfile = stdout; + } + ret = fmustach(template, &itf, &e, outfile); + free(template); + if (outfile == stdout) { + if (fflush(stdout)) + error(EXIT_FAILURE, errno, "mustach_dhcpd_pools: fflush"); + } else { + if (close_stream(outfile)) + error(EXIT_FAILURE, errno, "mustach_dhcpd_pools: fclose"); + } + switch (ret) { + case MUSTACH_OK: + return 0; + case MUSTACH_ERROR_SYSTEM: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: system error"); + break; + case MUSTACH_ERROR_UNEXPECTED_END: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unexpected end"); + break; + case MUSTACH_ERROR_EMPTY_TAG: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: empty tag"); + break; + case MUSTACH_ERROR_TAG_TOO_LONG: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: too long tag"); + break; + case MUSTACH_ERROR_BAD_SEPARATORS: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: bad separator"); + break; + case MUSTACH_ERROR_TOO_DEEP: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: too deep"); + break; + case MUSTACH_ERROR_CLOSING: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: closing"); + break; + case MUSTACH_ERROR_BAD_UNESCAPE_TAG: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: bad escape tag"); + break; + default: + error(EXIT_FAILURE, 0, "mustach_dhcpd_pools: fmustach: unknown error"); + } + return 1; +} diff --git a/src/mustach.c b/src/mustach.c new file mode 100644 index 0000000..b197a83 --- /dev/null +++ b/src/mustach.c @@ -0,0 +1,325 @@ +/* + Author: José Bollo + Author: José Bollo + + https://gitlab.com/jobol/mustach + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#define _GNU_SOURCE + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "mustach.h" + +#define NAME_LENGTH_MAX 1024 +#define DEPTH_MAX 256 + +#if !defined(NO_OPEN_MEMSTREAM) +static FILE *memfile_open(char **buffer, size_t *size) +{ + return open_memstream(buffer, size); +} +static void memfile_abort(FILE *file, char **buffer, size_t *size) +{ + fclose(file); + free(*buffer); + *buffer = NULL; + *size = 0; +} +static int memfile_close(FILE *file, char **buffer, size_t *size) +{ + int rc; + + /* adds terminating null */ + rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0; + fclose(file); + if (rc == 0) + /* removes terminating null of the length */ + (*size)--; + else { + free(*buffer); + *buffer = NULL; + *size = 0; + } + return rc; +} +#else +static FILE *memfile_open(char **buffer, size_t *size) +{ + return tmpfile(); +} +static void memfile_abort(FILE *file, char **buffer, size_t *size) +{ + fclose(file); + *buffer = NULL; + *size = 0; +} +static int memfile_close(FILE *file, char **buffer, size_t *size) +{ + int rc; + size_t s; + char *b; + + s = (size_t)ftell(file); + b = malloc(s + 1); + if (b == NULL) { + rc = MUSTACH_ERROR_SYSTEM; + errno = ENOMEM; + s = 0; + } else { + rewind(file); + if (1 == fread(b, s, 1, file)) { + rc = 0; + b[s] = 0; + } else { + rc = MUSTACH_ERROR_SYSTEM; + free(b); + b = NULL; + s = 0; + } + } + *buffer = b; + *size = s; + return rc; +} +#endif + +static int getpartial(struct mustach_itf *itf, void *closure, const char *name, char **result) +{ + int rc; + FILE *file; + size_t size; + + *result = NULL; + file = memfile_open(result, &size); + if (file == NULL) + rc = MUSTACH_ERROR_SYSTEM; + else { + rc = itf->put(closure, name, 0, file); + if (rc < 0) + memfile_abort(file, result, &size); + else + rc = memfile_close(file, result, &size); + } + return rc; +} + +static int process(const char *template, struct mustach_itf *itf, void *closure, FILE *file, const char *opstr, const char *clstr) +{ + char name[NAME_LENGTH_MAX + 1], *partial, c, *tmp; + const char *beg, *term; + struct { const char *name, *again; size_t length; int emit, entered; } stack[DEPTH_MAX]; + size_t oplen, cllen, len, l; + int depth, rc, emit; + + emit = 1; + oplen = strlen(opstr); + cllen = strlen(clstr); + depth = 0; + for(;;) { + beg = strstr(template, opstr); + if (beg == NULL) { + /* no more mustach */ + if (emit) + fwrite(template, strlen(template), 1, file); + return depth ? MUSTACH_ERROR_UNEXPECTED_END : 0; + } + if (emit) + fwrite(template, (size_t)(beg - template), 1, file); + beg += oplen; + term = strstr(beg, clstr); + if (term == NULL) + return MUSTACH_ERROR_UNEXPECTED_END; + template = term + cllen; + len = (size_t)(term - beg); + c = *beg; + switch(c) { + case '!': + case '=': + break; + case '{': + for (l = 0 ; clstr[l] == '}' ; l++); + if (clstr[l]) { + if (!len || beg[len-1] != '}') + return MUSTACH_ERROR_BAD_UNESCAPE_TAG; + len--; + } else { + if (term[l] != '}') + return MUSTACH_ERROR_BAD_UNESCAPE_TAG; + template++; + } + c = '&'; + /*@fallthrough@*/ + case '^': + case '#': + case '/': + case '&': + case '>': +#if !defined(NO_EXTENSION_FOR_MUSTACH) && !defined(NO_COLON_EXTENSION_FOR_MUSTACH) + case ':': +#endif + beg++; len--; + default: + while (len && isspace(beg[0])) { beg++; len--; } + while (len && isspace(beg[len-1])) len--; +#if defined(NO_EXTENSION_FOR_MUSTACH) || defined(NO_ALLOW_EMPTY_TAG) + if (len == 0) + return MUSTACH_ERROR_EMPTY_TAG; +#endif + if (len > NAME_LENGTH_MAX) + return MUSTACH_ERROR_TAG_TOO_LONG; + memcpy(name, beg, len); + name[len] = 0; + break; + } + switch(c) { + case '!': + /* comment */ + /* nothing to do */ + break; + case '=': + /* defines separators */ + if (len < 5 || beg[len - 1] != '=') + return MUSTACH_ERROR_BAD_SEPARATORS; + beg++; + len -= 2; + for (l = 0; l < len && !isspace(beg[l]) ; l++); + if (l == len) + return MUSTACH_ERROR_BAD_SEPARATORS; + oplen = l; + tmp = alloca(oplen + 1); + memcpy(tmp, beg, oplen); + tmp[oplen] = 0; + opstr = tmp; + while (l < len && isspace(beg[l])) l++; + if (l == len) + return MUSTACH_ERROR_BAD_SEPARATORS; + cllen = len - l; + tmp = alloca(cllen + 1); + memcpy(tmp, beg + l, cllen); + tmp[cllen] = 0; + clstr = tmp; + break; + case '^': + case '#': + /* begin section */ + if (depth == DEPTH_MAX) + return MUSTACH_ERROR_TOO_DEEP; + rc = emit; + if (rc) { + rc = itf->enter(closure, name); + if (rc < 0) + return rc; + } + stack[depth].name = beg; + stack[depth].again = template; + stack[depth].length = len; + stack[depth].emit = emit; + stack[depth].entered = rc; + if ((c == '#') == (rc == 0)) + emit = 0; + depth++; + break; + case '/': + /* end section */ + if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len)) + return MUSTACH_ERROR_CLOSING; + rc = emit && stack[depth].entered ? itf->next(closure) : 0; + if (rc < 0) + return rc; + if (rc) { + template = stack[depth++].again; + } else { + emit = stack[depth].emit; + if (emit && stack[depth].entered) + itf->leave(closure); + } + break; + case '>': + /* partials */ + if (emit) { + rc = getpartial(itf, closure, name, &partial); + if (rc == 0) { + rc = process(partial, itf, closure, file, opstr, clstr); + free(partial); + } + if (rc < 0) + return rc; + } + break; + default: + /* replacement */ + if (emit) { + rc = itf->put(closure, name, c != '&', file); + if (rc < 0) + return rc; + } + break; + } + } +} + +int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file) +{ + int rc = itf->start ? itf->start(closure) : 0; + if (rc == 0) + rc = process(template, itf, closure, file, "{{", "}}"); + return rc; +} + +int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd) +{ + int rc; + FILE *file; + + file = fdopen(fd, "w"); + if (file == NULL) { + rc = MUSTACH_ERROR_SYSTEM; + errno = ENOMEM; + } else { + rc = fmustach(template, itf, closure, file); + fclose(file); + } + return rc; +} + +int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size) +{ + int rc; + FILE *file; + size_t s; + + *result = NULL; + if (size == NULL) + size = &s; + file = memfile_open(result, size); + if (file == NULL) + rc = MUSTACH_ERROR_SYSTEM; + else { + rc = fmustach(template, itf, closure, file); + if (rc < 0) + memfile_abort(file, result, size); + else + rc = memfile_close(file, result, size); + } + return rc; +} + diff --git a/src/mustach.h b/src/mustach.h new file mode 100644 index 0000000..0ceba16 --- /dev/null +++ b/src/mustach.h @@ -0,0 +1,120 @@ +/* + Author: José Bollo + Author: José Bollo + + https://gitlab.com/jobol/mustach + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef _mustach_h_included_ +#define _mustach_h_included_ + +/** + * mustach_itf - interface for callbacks + * + * All of this function should return a negative value to stop + * the mustache processing. The returned negative value will be + * then returned to the caller of mustach as is. + * + * The functions enter and next should return 0 or 1. + * + * All other functions should normally return 0. If it returns + * a negative value, it means an error that stop the process + * and that is reported to the caller. + * + * @start: Starts the mustach processing of the closure + * 'start' is optional (can be NULL) + * + * @put: Writes the value of 'name' to 'file' with 'escape' or not + * As an extension (see NO_ALLOW_EMPTY_TAG), the 'name' can be + * the empty string. In that later case an implemntation can + * return MUSTACH_ERROR_EMPTY_TAG to refuse empty names. + * + * @enter: Enters the section of 'name' if possible. + * Musts return 1 if entered or 0 if not entered. + * When 1 is returned, the function 'leave' will always be called. + * Conversely 'leave' is never called when enter returns 0 or + * a negative value. + * When 1 is returned, the function must activate the first + * item of the section. + * + * @next: Activates the next item of the section if it exists. + * Musts return 1 when the next item is activated. + * Musts return 0 when there is no item to activate. + * + * @leave: Leaves the last entered section + */ +struct mustach_itf { + int (*start)(void *closure); + int (*put)(void *closure, const char *name, int escape, FILE *file); + int (*enter)(void *closure, const char *name); + int (*next)(void *closure); + int (*leave)(void *closure); +}; + +#define MUSTACH_OK 0 +#define MUSTACH_ERROR_SYSTEM -1 +#define MUSTACH_ERROR_UNEXPECTED_END -2 +#define MUSTACH_ERROR_EMPTY_TAG -3 +#define MUSTACH_ERROR_TAG_TOO_LONG -4 +#define MUSTACH_ERROR_BAD_SEPARATORS -5 +#define MUSTACH_ERROR_TOO_DEEP -6 +#define MUSTACH_ERROR_CLOSING -7 +#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8 + +/* compatibility with older bad name */ +#define MUSTACH_ERROR_TOO_DEPTH MUSTACH_ERROR_TOO_DEEP + +/** + * fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @file: the file where to write the result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file); + +/** + * fmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @fd: the file descriptor number where to write the result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd); + +/** + * fmustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @result: the pointer receiving the result when 0 is returned + * @size: the size of the returned result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size); + +#endif + diff --git a/src/other.c b/src/other.c index cd2cb3a..b319a2d 100644 --- a/src/other.c +++ b/src/other.c @@ -47,24 +47,30 @@ #include #include #include +#include +#include #include #include "error.h" #include "progname.h" #include "quote.h" +#include "xalloc.h" #include "dhcpd-pools.h" -#include "defaults.h" + +static char *(*cidr_last) (union ipaddr_t *restrict addr, const int mask); +static char *cidr_last_v4(union ipaddr_t *restrict addr, const int mask); +static char *cidr_last_v6(union ipaddr_t *restrict addr, const int mask); /*! \brief Set function pointers depending on IP version. * \param ip IP version. */ -void set_ipv_functions(int version) +void set_ipv_functions(struct conf_t *state, int version) { switch (version) { case IPv4: - config.ip_version = version; + state->ip_version = version; add_lease = add_lease_v4; copy_ipaddr = copy_ipaddr_v4; find_lease = find_lease_v4; @@ -73,11 +79,12 @@ void set_ipv_functions(int version) leasecomp = leasecomp_v4; ntop_ipaddr = ntop_ipaddr_v4; parse_ipaddr = parse_ipaddr_v4; + cidr_last = cidr_last_v4; xstrstr = xstrstr_v4; break; case IPv6: - config.ip_version = version; + state->ip_version = version; add_lease = add_lease_v6; copy_ipaddr = copy_ipaddr_v6; find_lease = find_lease_v6; @@ -86,11 +93,12 @@ void set_ipv_functions(int version) leasecomp = leasecomp_v6; ntop_ipaddr = ntop_ipaddr_v6; parse_ipaddr = parse_ipaddr_v6; + cidr_last = cidr_last_v6; xstrstr = xstrstr_v6; break; case IPvUNKNOWN: - config.ip_version = version; + state->ip_version = version; add_lease = add_lease_init; copy_ipaddr = copy_ipaddr_init; find_lease = find_lease_init; @@ -99,6 +107,7 @@ void set_ipv_functions(int version) leasecomp = leasecomp_init; ntop_ipaddr = ntop_ipaddr_init; parse_ipaddr = parse_ipaddr_init; + cidr_last = NULL; xstrstr = xstrstr_init; break; @@ -114,21 +123,23 @@ void set_ipv_functions(int version) * \param dst An union which will hold conversion result. * \return Was parsing successful. */ -int parse_ipaddr_init(const char *restrict src, union ipaddr_t *restrict dst) +int parse_ipaddr_init(struct conf_t *state, const char *restrict src, union ipaddr_t *restrict dst) { struct in_addr addr; struct in6_addr addr6; if (inet_aton(src, &addr) == 1) - set_ipv_functions(IPv4); + set_ipv_functions(state, IPv4); else if (inet_pton(AF_INET6, src, &addr6) == 1) - set_ipv_functions(IPv6); + set_ipv_functions(state, IPv6); else return 0; - return parse_ipaddr(src, dst); + return parse_ipaddr(state, src, dst); } -int parse_ipaddr_v4(const char *restrict src, union ipaddr_t *restrict dst) +int parse_ipaddr_v4(struct conf_t *state + __attribute__ ((unused)), const char *restrict src, + union ipaddr_t *restrict dst) { int rv; struct in_addr addr; @@ -138,7 +149,9 @@ int parse_ipaddr_v4(const char *restrict src, union ipaddr_t *restrict dst) return rv == 1; } -int parse_ipaddr_v6(const char *restrict src, union ipaddr_t *restrict dst) +int parse_ipaddr_v6(struct conf_t *state + __attribute__ ((unused)), const char *restrict src, + union ipaddr_t *restrict dst) { int rv; struct in6_addr addr; @@ -148,6 +161,107 @@ int parse_ipaddr_v6(const char *restrict src, union ipaddr_t *restrict dst) return rv == 1; } +/*! \brief Convert string to a desimal format network marks. + * \param src Digit that should be a network mask. + * \return Network mask, or -1 when failing. + */ +static int strtol_mask(const char *str) +{ + long num; + char *end = NULL; + + errno = 0; + if (str == NULL || *str == '\0') + goto err; + num = strtol(str, &end, 10); + + if (errno || str == end || (end && *end)) + goto err; + if (num < 0 || 128 < num) + goto err; + return (int)num; + err: + return -1; +} + +/*! \brief Find last address in IPv4 range by using cidr format. + * \param addr Pointer to memory where address needs to be stored. + * \return Allocated string format of the address. + */ +static char *cidr_last_v4(union ipaddr_t *restrict addr, const int mask) +{ + union ipaddr_t last_ip; + uint32_t netmask; + const char *ip; + + if (mask) + netmask = (1U << (32 - mask)) - 1; + else + netmask = 0; + last_ip.v4 = addr->v4 | netmask; + + ip = ntop_ipaddr(&last_ip); + return xstrdup(ip); +} + +/*! \brief Find last address in IPv6 range by using cidr format. + * \param addr Pointer to memory where address needs to be stored. + * \return Allocated string format of the address. + */ +static char *cidr_last_v6(union ipaddr_t *restrict addr, const int mask) +{ + union ipaddr_t bitmask; + int i, j; + char ip[128]; + + memset(&bitmask, 0x0, sizeof(bitmask)); + for (i = mask, j = 0; i > 0; i -= 8, j++) { + if (i >= 8) + bitmask.v6[j] = 0xff; + else + bitmask.v6[j] = (unsigned char)(0xffU << (8 - i)); + } + for (i = 0; i < (int)sizeof(bitmask); i++) + addr->v6[i] |= ~bitmask.v6[i]; + inet_ntop(AF_INET6, addr, ip, sizeof(ip)); + return xstrdup(ip); +} + +/*! \brief Convert a cidr notated address to a range. + * \param range_p Pointer to memory where addresses need to be stored. + * \param word A range as a cidr string. + */ +void parse_cidr(struct conf_t *state, struct range_t *range_p, const char *word) +{ + char *divider; + int mask; + union ipaddr_t addr; + char *last; + + /* determine cidr */ + divider = strchr(word, '/'); + *divider++ = '\0'; + mask = strtol_mask(divider); + if (mask < 0) + error(EXIT_FAILURE, 0, "cidr %s invalid mask %s", word, divider); + if (state->ip_version == IPvUNKNOWN) { + if (!strchr(word, ':')) + set_ipv_functions(state, IPv4); + else + set_ipv_functions(state, IPv6); + } + + /* start of the range is easy */ + parse_ipaddr(state, word, &addr); + copy_ipaddr(&range_p->first_ip, &addr); + + /* end of the range depends cidr size */ + last = cidr_last(&addr, mask); + parse_ipaddr(state, last, &addr); + copy_ipaddr(&range_p->last_ip, &addr); + free(last); +} + /*! \brief Copy IP address to union. * * \param dst Destination for a binary IP address. @@ -184,7 +298,7 @@ const char *ntop_ipaddr_init(const union ipaddr_t *ip __attribute__ ((unused))) const char *ntop_ipaddr_v4(const union ipaddr_t *ip) { - static char buffer[sizeof("255.255.255.255")]; + static char buffer[INET_ADDRSTRLEN]; struct in_addr addr; addr.s_addr = htonl(ip->v4); @@ -193,7 +307,7 @@ const char *ntop_ipaddr_v4(const union ipaddr_t *ip) const char *ntop_ipaddr_v6(const union ipaddr_t *ip) { - static char buffer[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + static char buffer[INET6_ADDRSTRLEN]; struct in6_addr addr; memcpy(addr.s6_addr, ip->v6, sizeof(addr.s6_addr)); @@ -238,17 +352,14 @@ double get_range_size_v6(const struct range_t *r) * \param str A line from dhcpd.conf * \return prefix_t enum value */ -int -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) - __attribute__ ((hot)) -#endif - xstrstr_init(const char *restrict str) +int xstrstr_init(struct conf_t *state, const char *restrict str) { - if (memcmp("lease ", str, 6)) { - set_ipv_functions(IPv4); + if (!memcmp("lease ", str, 6)) { + set_ipv_functions(state, IPv4); return PREFIX_LEASE; - } else if (memcmp(" iaaddr ", str, 9)) { - set_ipv_functions(IPv6); + } + if (!memcmp(" iaaddr ", str, 9)) { + set_ipv_functions(state, IPv6); return PREFIX_LEASE; } return NUM_OF_PREFIX; @@ -264,7 +375,7 @@ int #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) __attribute__ ((hot)) #endif - xstrstr_v4(const char *restrict str) + xstrstr_v4(struct conf_t *state __attribute__ ((unused)), const char *restrict str) { size_t len; @@ -300,10 +411,18 @@ int if (!memcmp(" hardware ethernet", str, 19)) return PREFIX_HARDWARE_ETHERNET; break; + default: + break; } } if (!memcmp("lease ", str, 6)) return PREFIX_LEASE; + else if (!memcmp(" starts ", str, 9)) + return PREFIX_STARTS; + else if (!memcmp(" ends ", str, 7)) + return PREFIX_ENDS; + else if (!memcmp(" client-hostname ", str, 18)) + return PREFIX_HOSTNAME; return NUM_OF_PREFIX; } @@ -317,7 +436,7 @@ int #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) __attribute__ ((hot)) #endif - xstrstr_v6(const char *restrict str) + xstrstr_v6(struct conf_t *state __attribute__ ((unused)), const char *restrict str) { size_t len; @@ -353,13 +472,37 @@ int if (!memcmp(" hardware ethernet", str, 19)) return PREFIX_HARDWARE_ETHERNET; break; + default: + break; } } if (!memcmp(" iaaddr ", str, 9)) return PREFIX_LEASE; + else if (!memcmp(" starts ", str, 9)) + return PREFIX_STARTS; + else if (!memcmp(" ends ", str, 7)) + return PREFIX_ENDS; + else if (!memcmp(" client-hostname ", str, 18)) + return PREFIX_HOSTNAME; return NUM_OF_PREFIX; } +/*! \brief Parse option argument color mode. + * + * \param Color mode string. + * \return color mode enum. + */ +int parse_color_mode(const char *restrict arg) +{ + if (!strcmp(arg, "always")) + return color_on; + if (!strcmp(arg, "auto")) + return color_auto; + if (!strcmp(arg, "never")) + return color_off; + return color_unknown; +} + /*! \brief Return a double floating point value. * * \param str String to be converted to a double. @@ -384,56 +527,79 @@ double strtod_or_err(const char *restrict str, const char *restrict errmesg) } /*! \brief Reverse range. - * Used before output, if a caller has requested reverse sorting. - * FIXME: The temporary memory area handling should be internal to this - * function, not a parameter. - * - * \param flip_me The range that needs to be inverted. - * \param tmp_ranges Temporary memory area for the flip. */ -void flip_ranges(struct range_t *restrict flip_me, struct range_t *restrict tmp_ranges) + * Used before output, if a caller has requested reverse sorting. */ +void flip_ranges(struct conf_t *state) { - unsigned int i = num_ranges - 1, j; + unsigned int i = state->num_ranges - 1, j; + struct range_t *tmp_ranges; - for (j = 0; j < num_ranges; j++, i--) - *(tmp_ranges + j) = *(flip_me + i); - memcpy(flip_me, tmp_ranges, num_ranges * sizeof(struct range_t)); + tmp_ranges = xmalloc(sizeof(struct range_t) * state->num_ranges); + for (j = 0; j < state->num_ranges; j++, i--) + *(tmp_ranges + j) = *(state->ranges + i); + memcpy(state->ranges, tmp_ranges, state->num_ranges * sizeof(struct range_t)); + free(tmp_ranges); } /*! \brief Free memory, flush buffers etc. */ -void clean_up(void) +void clean_up(struct conf_t *state) { struct output_sort *cur, *next; + struct shared_network_t *c, *n; /* Just in case there something in buffers */ if (fflush(NULL)) error(EXIT_FAILURE, errno, "clean_up: fflush"); - free(config.dhcpdconf_file); - free(config.dhcpdlease_file); - free(config.output_file); - free(ranges); - delete_all_leases(); - if (shared_networks) { - unsigned int i; - - num_shared_networks++; - for (i = 0; i < num_shared_networks; i++) - free((shared_networks + i)->name); - free(shared_networks); - } - for (cur = config.sorts; cur; cur = next) { + free(state->ranges); + delete_all_leases(state); + for (cur = state->sorts; cur; cur = next) { next = cur->next; free(cur); } + for (c = state->shared_net_root; c; c = n) { + n = c->next; + free(c->name); + free(c); + } +} + +/*! \brief Print a time stamp of a path or now to output file. */ +void dp_time_tool(FILE *file, const char *path, const int epoch) +{ + struct stat st; + + if (path) + stat(path, &st); + else + clock_gettime(CLOCK_REALTIME, &st.st_mtim); + if (epoch) + fprintf(file, "%ld", st.st_mtim.tv_sec); + else { + char time_stamp[64]; + const time_t mtime = st.st_mtim.tv_sec; + struct tm tm; + + localtime_r(&mtime, &tm); + strftime(time_stamp, sizeof(time_stamp), "%FT%T%z", &tm); + fprintf(file, "%s", time_stamp); + } } /*! \brief A version printing. */ void __attribute__ ((__noreturn__)) print_version(void) { +#define stringify(s) #s +#define stringify_value(s) stringify(s) fprintf(stdout, "%s\n" - "Original design by Sami Kerola.\n" + "Original design and maintainer Sami Kerola.\n" + "uthash %s by Troy D. Hanson.\n" "XML support by Dominic Germain, Sogetel inc.\n" - "IPv6 support by Cheer Xiao.\n\n" - "The software has FreeBSD License.\n", PACKAGE_STRING); + "IPv6 support by Cheer Xiao.\n" + "Mustach templating support by Jose Bollo.\n" + " The dhcpd-pools is FreeBSD Licensed,\n" + " uthash uses BSD license,\n" + " gnulib parts are mostly GPL,\n" + " and mustache uses Apache License.\n", PACKAGE_STRING, + stringify_value(UTHASH_VERSION)); exit(EXIT_SUCCESS); } @@ -456,6 +622,9 @@ void __attribute__ ((__noreturn__)) usage(int status) fputs( " j for json\n", out); fputs( " J for json with active lease details\n", out); fputs( " c for comma separated values\n", out); +#ifdef BUILD_MUSTACH + fputs( " --mustach=FILE output using mustach template file\n", out); +#endif fputs( " -s, --sort=[nimcptTe] sort ranges by\n", out); fputs( " n name\n", out); fputs( " i IP\n", out); @@ -468,12 +637,18 @@ void __attribute__ ((__noreturn__)) usage(int status) fputs( " -r, --reverse reverse order sort\n", out); fputs( " -o, --output=FILE output into a file\n", out); fputs( " -L, --limit=NR output limit mask 77 - 00\n", out); - fputs( " --warning=PERC set warning alarming limit\n", out); - fputs( " --critical=PERC set critical alarming limit\n", out); + fputs( " --color=WHEN use colors 'always', 'never', or 'auto'\n", out); + fputs( " --warning=PERC set warning alarming threshold\n", out); + fputs( " --critical=PERC set critical alarming threshold\n", out); + fputs( " --skip=WHAT do not print threshold 'ok', 'warning', 'critical',\n", out); + fputs( " 'minsize', or 'suppressed'\n", out); fputs( " --warn-count=NR a number of free leases before warning raised\n", out); fputs( " --crit-count=NR a number of free leases before critical raised\n", out); fputs( " --minsize=size disable alarms for small ranges and shared-nets\n", out); fputs( " --snet-alarms suppress range alarms that are part of a shared-net\n", out); + fputs( " -p, --perfdata print additional perfdata in alarming mode\n", out); + fputs( " -A, --all-as-shared treat single subnets as shared-network with CIDR as their name\n", out); + fputs( " --ip-version=4|6 force analysis to use either IPv4 or IPv6 functions\n", out); fputs( " -v, --version output version information and exit\n", out); fputs( " -h, --help display this help and exit\n", out); fputs( "\n", out); diff --git a/src/output.c b/src/output.c index e220243..4ff0a90 100644 --- a/src/output.c +++ b/src/output.c @@ -43,12 +43,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include "close-stream.h" #include "error.h" @@ -57,31 +59,180 @@ #include "dhcpd-pools.h" -/*! \brief Text output format, which is the default. */ -int output_txt(void) -{ - unsigned int i; - struct range_t *range_p; - double range_size; - struct shared_network_t *shared_p; - int ret; - FILE *outfile; - int max_ipaddr_length = config.ip_version == IPv6 ? 39 : 16; +/*! \enum colored_formats + * \brief Enumeration of output formats. Keep the text and html first, they + * are used color array selector. + */ +enum colored_formats { + OUT_FORM_TEXT, + OUT_FORM_HTML, + NUM_OF_OUT_FORMS +}; - if (config.output_file[0]) { - outfile = fopen(config.output_file, "w+"); +/*! \enum count_status_t + * \brief Enumeration of possible range and shared net statuses. + */ +enum count_status_t { + STATUS_OK, + STATUS_WARN, + STATUS_CRIT, + STATUS_IGNORED, + STATUS_SUPPRESSED, + COLOR_RESET +}; + +/*! \var color_tags + * \brief Array of strings that make colors to start and end in different + * schemas per array column. */ +static const char *color_tags[][NUM_OF_OUT_FORMS] = { + [STATUS_OK] = { "", "" }, + [STATUS_WARN] = { "\033[1;33m", " style=\"color:magenta;font-style:italic\"" }, + [STATUS_CRIT] = { "\033[1;31m", " style=\"color:red;font-weight:bold\"" }, + [STATUS_IGNORED] = { "\033[1;32m", " style=\"color:green\"" }, + [STATUS_SUPPRESSED] = { "\033[1;34m", " style=\"color:blue\"" }, + [COLOR_RESET] = { "\033[0m", "" } +}; + +/*! \brief Calculate range percentages and such. + * \return Indicator if the entry should be skipped from output. */ +int range_output_helper(struct conf_t *state, struct output_helper_t *oh, + struct range_t *range_p) +{ + /* counts and calculations */ + oh->range_size = get_range_size(range_p); + oh->percent = (double)(100 * range_p->count) / oh->range_size; + oh->tc = range_p->touched + range_p->count; + oh->tcp = (double)(100 * oh->tc) / oh->range_size; + if (state->backups_found == 1) { + oh->bup = (double)(100 * range_p->backups) / oh->range_size; + } + /* set status */ + oh->status = STATUS_OK; + if (state->critical < oh->percent && (oh->range_size - range_p->count) < state->crit_count) + oh->status = STATUS_CRIT; + else if (state->warning < oh->percent + && (oh->range_size - range_p->count) < state->warn_count) + oh->status = STATUS_WARN; + if (oh->status != STATUS_OK) { + if (oh->range_size <= state->minsize) { + oh->status = STATUS_IGNORED; + if (state->skip_minsize) + return 1; + } else if (state->snet_alarms && range_p->shared_net != state->shared_net_root) { + oh->status = STATUS_SUPPRESSED; + if (state->skip_suppressed) + return 1; + } + } + if ((state->skip_ok && oh->status == STATUS_OK) || + (state->skip_warning && oh->status == STATUS_WARN) || + (state->skip_critical && oh->status == STATUS_CRIT)) + return 1; + return 0; +} + +/*! \brief Calculate shared network percentages and such. + * \return Indicator if the entry should be skipped from output. */ +int shnet_output_helper(struct conf_t *state, struct output_helper_t *oh, + struct shared_network_t *shared_p) +{ + /* counts and calculations */ + oh->tc = shared_p->touched + shared_p->used; + if (fpclassify(shared_p->available) == FP_ZERO) { + oh->percent = NAN; + oh->tcp = NAN; + oh->bup = NAN; + oh->status = STATUS_SUPPRESSED; + if (state->skip_suppressed) + return 1; + return 0; + } + + oh->percent = (double)(100 * shared_p->used) / shared_p->available; + oh->tcp = (double)((100 * (shared_p->touched + shared_p->used)) / shared_p->available); + if (state->backups_found == 1) + oh->bup = (double)(100 * shared_p->backups) / shared_p->available; + + /* set status */ + if (shared_p->available <= state->minsize) { + oh->status = STATUS_IGNORED; + if (state->skip_minsize) + return 1; + } else if (state->critical < oh->percent && (shared_p->available - shared_p->used) < state->crit_count) { + oh->status = STATUS_CRIT; + if (state->skip_critical) + return 1; + } else if (state->warning < oh->percent && (shared_p->available - shared_p->used) < state->warn_count) { + oh->status = STATUS_WARN; + if (state->skip_warning) + return 1; + } else { + oh->status = STATUS_OK; + if (state->skip_ok) + return 1; + } + return 0; + +} + +/*! \brief Output a color based on output_helper_t status. + * \return Indicator whether coloring was started or not. */ +static int start_color(struct conf_t *state, struct output_helper_t *oh, FILE *outfile) +{ + if (oh->status == STATUS_OK) { + return 0; + } + fputs(color_tags[oh->status][state->color_format], outfile); + return 1; +} + +/*! \brief Helper function to open a output file. + * \return The outfile in all of the output functions. */ +static FILE *open_outfile(struct conf_t *state) +{ + FILE *outfile; + + if (state->output_file) { + outfile = fopen(state->output_file, "w+"); if (outfile == NULL) { - error(EXIT_FAILURE, errno, "output_txt: %s", config.output_file); + error(EXIT_FAILURE, errno, "open_outfile: %s", state->output_file); } } else { outfile = stdout; } + return outfile; +} - range_p = ranges; - range_size = get_range_size(range_p); - shared_p = shared_networks; - if (config.header_limit & R_BIT) { +/*! \brief Helper function to close outfile. */ +static void close_outfile(FILE *outfile) +{ + if (outfile == stdout) { + if (fflush(stdout)) + error(EXIT_FAILURE, errno, "close_outfile: fflush"); + } else { + if (close_stream(outfile)) + error(EXIT_FAILURE, errno, "close_outfile: fclose"); + } +} + +/*! \brief Text output format, which is the default. */ +static int output_txt(struct conf_t *state) +{ + struct range_t *range_p; + struct shared_network_t *shared_p; + struct output_helper_t oh; + FILE *outfile; + int max_ipaddr_length = state->ip_version == IPv6 ? 39 : 16; + + if (state->color_mode == color_auto && isatty(STDIN_FILENO)) { + state->color_mode = color_on; + } + + outfile = open_outfile(state); + range_p = state->ranges; + + if (state->header_limit & R_BIT) { fprintf(outfile, "Ranges:\n"); fprintf (outfile, @@ -91,13 +242,22 @@ int output_txt(void) "first ip", max_ipaddr_length, "last ip", "max", "cur", "percent", "touch", "t+c", "t+c perc"); - if (config.backups_found == 1) { + if (state->backups_found == 1) { fprintf(outfile, " bu bu perc"); } fprintf(outfile, "\n"); } - if (config.number_limit & R_BIT) { - for (i = 0; i < num_ranges; i++) { + if (state->number_limit & R_BIT) { + unsigned int i; + for (i = 0; i < state->num_ranges; i++) { + int color_set = 0; + + if (range_output_helper(state, &oh, range_p)) { + range_p++; + continue; + } + if (state->color_mode == color_on) + color_set = start_color(state, &oh, outfile); if (range_p->shared_net) { fprintf(outfile, "%-20s", range_p->shared_net->name); } else { @@ -112,135 +272,114 @@ int output_txt(void) " - %-*s %5g %5g %10.3f %5g %5g %9.3f", max_ipaddr_length, ntop_ipaddr(&range_p->last_ip), - range_size, + oh.range_size, range_p->count, - (float)(100 * range_p->count) / range_size, + oh.percent, range_p->touched, - range_p->touched + range_p->count, - (float)(100 * (range_p->touched + range_p->count)) / range_size); - if (config.backups_found == 1) { - fprintf(outfile, "%7g %8.3f", - range_p->backups, - (float)(100 * range_p->backups) / range_size); + oh.tc, + oh.tcp); + if (state->backups_found == 1) { + fprintf(outfile, "%7g %8.3f", range_p->backups, oh.bup); } + if (color_set) + fputs(color_tags[COLOR_RESET][state->color_format], outfile); fprintf(outfile, "\n"); range_p++; - range_size = get_range_size(range_p); } } - if (config.number_limit & R_BIT && config.header_limit & S_BIT) { + if (state->number_limit & R_BIT && state->header_limit & S_BIT) { fprintf(outfile, "\n"); } - if (config.header_limit & S_BIT) { + if (state->header_limit & S_BIT) { fprintf(outfile, "Shared networks:\n"); fprintf(outfile, "name max cur percent touch t+c t+c perc"); - if (config.backups_found == 1) { + if (state->backups_found == 1) { fprintf(outfile, " bu bu perc"); } fprintf(outfile, "\n"); } - if (config.number_limit & S_BIT) { - for (i = 0; i < num_shared_networks; i++) { - shared_p++; + if (state->number_limit & S_BIT) { + for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) { + int color_set = 0; + + if (shnet_output_helper(state, &oh, shared_p)) + continue; + if (state->color_mode == color_on) + color_set = start_color(state, &oh, outfile); fprintf(outfile, "%-20s %5g %5g %10.3f %7g %6g %9.3f", - shared_p->name, shared_p->available, + shared_p->name, + shared_p->available, shared_p->used, - shared_p->available == - 0 ? NAN : (float)(100 * shared_p->used) / shared_p->available, - shared_p->touched, shared_p->touched + shared_p->used, - shared_p->available == - 0 ? NAN : ((float)(100 * (shared_p->touched + shared_p->used)) / - shared_p->available)); - if (config.backups_found == 1) { - fprintf(outfile, "%7g %8.3f", - shared_p->backups, - (float)(100 * shared_p->backups) / shared_p->available); + oh.percent, + shared_p->touched, + oh.tc, + oh.tcp); + if (state->backups_found == 1) { + fprintf(outfile, "%7g %8.3f", shared_p->backups, oh.bup); } - + if (color_set) + fputs(color_tags[COLOR_RESET][state->color_format], outfile); fprintf(outfile, "\n"); } } - if (config.number_limit & S_BIT && config.header_limit & A_BIT) { + if (state->number_limit & S_BIT && state->header_limit & A_BIT) { fprintf(outfile, "\n"); } - if (config.header_limit & A_BIT) { + if (state->header_limit & A_BIT) { fprintf(outfile, "Sum of all ranges:\n"); fprintf(outfile, "name max cur percent touch t+c t+c perc"); - if (config.backups_found == 1) { + if (state->backups_found == 1) { fprintf(outfile, " bu bu perc"); } fprintf(outfile, "\n"); } - if (config.number_limit & A_BIT) { - fprintf(outfile, "%-20s %5g %5g %10.3f %7g %6g %9.3f", - shared_networks->name, - shared_networks->available, - shared_networks->used, - shared_networks->available == - 0 ? NAN : (float)(100 * shared_networks->used) / - shared_networks->available, shared_networks->touched, - shared_networks->touched + shared_networks->used, - shared_networks->available == - 0 ? NAN : (float)(100 * - (shared_networks->touched + - shared_networks->used)) / shared_networks->available); + if (state->number_limit & A_BIT) { + int color_set = 0; - if (config.backups_found == 1) { - fprintf(outfile, "%7g %8.3f", - shared_networks->available == 0 ? NAN : shared_networks->backups, - (float)(100 * shared_networks->backups) / - shared_networks->available); + shnet_output_helper(state, &oh, state->shared_net_root); + if (state->color_mode == color_on) + color_set = start_color(state, &oh, outfile); + fprintf(outfile, "%-20s %5g %5g %10.3f %7g %6g %9.3f", + state->shared_net_root->name, + state->shared_net_root->available, + state->shared_net_root->used, + oh.percent, + state->shared_net_root->touched, + oh.tc, + oh.tcp); + + if (state->backups_found == 1) { + fprintf(outfile, "%7g %8.3f", state->shared_net_root->backups, oh.bup); } + if (color_set) + fputs(color_tags[COLOR_RESET][state->color_format], outfile); fprintf(outfile, "\n"); } - if (outfile == stdout) { - ret = fflush(stdout); - if (ret) { - error(EXIT_FAILURE, errno, "output_txt: fflush"); - } - } else { - ret = close_stream(outfile); - if (ret) { - error(EXIT_FAILURE, errno, "output_txt: fclose"); - } - } - + close_outfile(outfile); return 0; } /*! \brief The xml output formats. */ -int output_xml(void) +static int output_xml(struct conf_t *state) { - unsigned int i; struct range_t *range_p; - double range_size; struct shared_network_t *shared_p; - int ret; + struct output_helper_t oh; FILE *outfile; - if (config.output_file[0]) { - outfile = fopen(config.output_file, "w+"); - if (outfile == NULL) { - error(EXIT_FAILURE, errno, "output_xml: %s", config.output_file); - } - } else { - outfile = stdout; - } - - range_p = ranges; - range_size = get_range_size(range_p); - shared_p = shared_networks; + outfile = open_outfile(state); + range_p = state->ranges; fprintf(outfile, "\n"); - if (config.print_mac_addreses == 1) { + if (state->print_mac_addreses) { struct leases_t *l; - for (l = leases; l != NULL; l = l->hh.next) { + for (l = state->leases; l != NULL; l = l->hh.next) { if (l->type == ACTIVE) { fputs("\n\t", outfile); fputs(ntop_ipaddr(&l->ip), outfile); @@ -248,13 +387,30 @@ int output_xml(void) if (l->ethernet != NULL) { fputs(l->ethernet, outfile); } - fputs("\n\n", outfile); + fputs("\n\t", outfile); + if (l->starts != NULL) { + fputs(l->starts, outfile); + } + fputs("\n\t", outfile); + if (l->ends != NULL) { + fputs(l->ends, outfile); + } + fputs("\n\t", outfile); + if (l->hostname != NULL) { + fputs(l->hostname, outfile); + } + fputs("\n\n", outfile); } } } - if (config.number_limit & R_BIT) { - for (i = 0; i < num_ranges; i++) { + if (state->number_limit & R_BIT) { + unsigned int i; + for (i = 0; i < state->num_ranges; i++) { + if (range_output_helper(state, &oh, range_p)) { + range_p++; + continue; + } fprintf(outfile, "\n"); if (range_p->shared_net) { fprintf(outfile, @@ -264,19 +420,19 @@ int output_xml(void) } fprintf(outfile, "\t%s ", ntop_ipaddr(&range_p->first_ip)); fprintf(outfile, "- %s\n", ntop_ipaddr(&range_p->last_ip)); - fprintf(outfile, "\t%g\n", range_size); + fprintf(outfile, "\t%g\n", oh.range_size); fprintf(outfile, "\t%g\n", range_p->count); fprintf(outfile, "\t%g\n", range_p->touched); - fprintf(outfile, "\t%g\n", range_size - range_p->count); + fprintf(outfile, "\t%g\n", oh.range_size - range_p->count); range_p++; - range_size = get_range_size(range_p); fprintf(outfile, "\n"); } } - if (config.number_limit & S_BIT) { - for (i = 0; i < num_shared_networks; i++) { - shared_p++; + if (state->number_limit & S_BIT) { + for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) { + if (shnet_output_helper(state, &oh, shared_p)) + continue; fprintf(outfile, "\n"); fprintf(outfile, "\t%s\n", shared_p->name); fprintf(outfile, "\t%g\n", shared_p->available); @@ -288,65 +444,43 @@ int output_xml(void) } } - if (config.header_limit & A_BIT) { + if (state->header_limit & A_BIT) { fprintf(outfile, "\n"); - fprintf(outfile, "\t%s\n", shared_networks->name); - fprintf(outfile, "\t%g\n", shared_networks->available); - fprintf(outfile, "\t%g\n", shared_networks->used); - fprintf(outfile, "\t%g\n", shared_networks->touched); + fprintf(outfile, "\t%s\n", state->shared_net_root->name); + fprintf(outfile, "\t%g\n", state->shared_net_root->available); + fprintf(outfile, "\t%g\n", state->shared_net_root->used); + fprintf(outfile, "\t%g\n", state->shared_net_root->touched); fprintf(outfile, "\t%g\n", - shared_networks->available - shared_networks->used); + state->shared_net_root->available - state->shared_net_root->used); fprintf(outfile, "\n"); } fprintf(outfile, "\n"); - if (outfile == stdout) { - ret = fflush(stdout); - if (ret) { - error(EXIT_FAILURE, errno, "output_xml: fflush"); - } - } else { - ret = close_stream(outfile); - if (ret) { - error(EXIT_FAILURE, errno, "output_xml: fclose"); - } - } - + close_outfile(outfile); return 0; } /*! \brief The json output formats. */ -int output_json(void) +static int output_json(struct conf_t *state) { unsigned int i = 0; struct range_t *range_p; - double range_size; struct shared_network_t *shared_p; - int ret; + struct output_helper_t oh; FILE *outfile; unsigned int sep; - if (config.output_file[0]) { - outfile = fopen(config.output_file, "w+"); - if (outfile == NULL) { - error(EXIT_FAILURE, errno, "output_json: %s", config.output_file); - } - } else { - outfile = stdout; - } - - range_p = ranges; - range_size = get_range_size(range_p); - shared_p = shared_networks; + outfile = open_outfile(state); + range_p = state->ranges; sep = 0; fprintf(outfile, "{\n"); - if (config.print_mac_addreses == 1) { + if (state->print_mac_addreses) { struct leases_t *l; fprintf(outfile, " \"active_leases\": ["); - for (l = leases; l != NULL; l = l->hh.next) { + for (l = state->leases; l != NULL; l = l->hh.next) { if (l->type == ACTIVE) { if (i == 0) { i = 1; @@ -359,6 +493,18 @@ int output_json(void) if (l->ethernet != NULL) { fputs(l->ethernet, outfile); } + fputs("\", \"starts\":\"", outfile); + if (l->starts != NULL) { + fputs(l->starts, outfile); + } + fputs("\", \"ends\":\"", outfile); + if (l->ends != NULL) { + fputs(l->ends, outfile); + } + fputs("\", \"hostname\":\"", outfile); + if (l->hostname != NULL) { + fputs(l->hostname, outfile); + } fputs("\" }", outfile); } } @@ -366,12 +512,16 @@ int output_json(void) sep++; } - if (config.number_limit & R_BIT) { + if (state->number_limit & R_BIT) { if (sep) { fprintf(outfile, ",\n"); } fprintf(outfile, " \"subnets\": [\n"); - for (i = 0; i < num_ranges; i++) { + for (i = 0; i < state->num_ranges; i++) { + if (range_output_helper(state, &oh, range_p)) { + range_p++; + continue; + } fprintf(outfile, " "); fprintf(outfile, "{ "); if (range_p->shared_net) { @@ -383,13 +533,23 @@ int output_json(void) fprintf(outfile, "\"range\":\"%s", ntop_ipaddr(&range_p->first_ip)); fprintf(outfile, " - %s\", ", ntop_ipaddr(&range_p->last_ip)); - fprintf(outfile, "\"defined\":%g, ", range_size); + fprintf(outfile, "\"first_ip\":\"%s\", ", ntop_ipaddr(&range_p->first_ip)); + fprintf(outfile, "\"last_ip\":\"%s\", ", ntop_ipaddr(&range_p->last_ip)); + fprintf(outfile, "\"defined\":%g, ", oh.range_size); fprintf(outfile, "\"used\":%g, ", range_p->count); fprintf(outfile, "\"touched\":%g, ", range_p->touched); - fprintf(outfile, "\"free\":%g ", range_size - range_p->count); + fprintf(outfile, "\"free\":%g, ", oh.range_size - range_p->count); + fprintf(outfile, "\"percent\":%g, ", oh.percent); + fprintf(outfile, "\"touch_count\":%g, ", oh.tc); + fprintf(outfile, "\"touch_percent\":%g, ", oh.tcp); + if (state->backups_found == 1) { + fprintf(outfile, "\"backup_count\":%g, ", range_p->backups); + fprintf(outfile, "\"backup_percent\":%g, ", oh.bup); + } + fprintf(outfile, "\"status\":%d ", oh.status); + range_p++; - range_size = get_range_size(range_p); - if (i + 1 < num_ranges) + if (i + 1 < state->num_ranges) fprintf(outfile, "},\n"); else fprintf(outfile, "}\n"); @@ -398,21 +558,39 @@ int output_json(void) sep++; } - if (config.number_limit & S_BIT) { + if (state->number_limit & S_BIT) { if (sep) { fprintf(outfile, ",\n"); } fprintf(outfile, " \"shared-networks\": [\n"); - for (i = 0; i < num_shared_networks; i++) { + for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) { + if (shnet_output_helper(state, &oh, shared_p)) + continue; fprintf(outfile, " "); - shared_p++; fprintf(outfile, "{ "); fprintf(outfile, "\"location\":\"%s\", ", shared_p->name); fprintf(outfile, "\"defined\":%g, ", shared_p->available); fprintf(outfile, "\"used\":%g, ", shared_p->used); fprintf(outfile, "\"touched\":%g, ", shared_p->touched); - fprintf(outfile, "\"free\":%g ", shared_p->available - shared_p->used); - if (i + 1 < num_shared_networks) + fprintf(outfile, "\"free\":%g, ", shared_p->available - shared_p->used); + if (fpclassify(shared_p->available) == FP_ZERO) + fprintf(outfile, "\"percent\":\"%g\", ", oh.percent); + else + fprintf(outfile, "\"percent\":%g, ", oh.percent); + fprintf(outfile, "\"touch_count\":%g, ", oh.tc); + if (fpclassify(shared_p->available) == FP_ZERO) + fprintf(outfile, "\"touch_percent\":\"%g\", ", oh.tcp); + else + fprintf(outfile, "\"touch_percent\":%g, ", oh.tcp); + if (state->backups_found == 1) { + fprintf(outfile, "\"backup_count\":%g, ", shared_p->backups); + if (fpclassify(shared_p->available) == FP_ZERO) + fprintf(outfile, "\"backup_percent\":\"%g\", ", oh.bup); + else + fprintf(outfile, "\"backup_percent\":%g, ", oh.bup); + } + fprintf(outfile, "\"status\":%d ", oh.status); + if (shared_p->next) fprintf(outfile, "},\n"); else fprintf(outfile, "}\n"); @@ -421,34 +599,49 @@ int output_json(void) sep++; } - if (config.header_limit & A_BIT) { + if (state->header_limit & A_BIT) { + shnet_output_helper(state, &oh, state->shared_net_root); if (sep) { fprintf(outfile, ",\n"); } fprintf(outfile, " \"summary\": {\n"); - fprintf(outfile, " \"location\":\"%s\",\n", shared_networks->name); - fprintf(outfile, " \"defined\":%g,\n", shared_networks->available); - fprintf(outfile, " \"used\":%g,\n", shared_networks->used); - fprintf(outfile, " \"touched\":%g,\n", shared_networks->touched); - fprintf(outfile, " \"free\":%g\n", - shared_networks->available - shared_networks->used); - fprintf(outfile, " }"); /* end of summary */ - sep++; - } + fprintf(outfile, " \"location\":\"%s\",\n", state->shared_net_root->name); + fprintf(outfile, " \"defined\":%g,\n", state->shared_net_root->available); + fprintf(outfile, " \"used\":%g,\n", state->shared_net_root->used); + fprintf(outfile, " \"touched\":%g,\n", state->shared_net_root->touched); + fprintf(outfile, " \"free\":%g,\n", + state->shared_net_root->available - state->shared_net_root->used); + if (fpclassify(state->shared_net_root->available) == FP_ZERO) + fprintf(outfile, " \"percent\":\"%g\",\n", oh.percent); + else + fprintf(outfile, " \"percent\":%g,\n", oh.percent); + fprintf(outfile, " \"touch_count\":%g,\n", oh.tc); + if (fpclassify(state->shared_net_root->available) == FP_ZERO) + fprintf(outfile, " \"touch_percent\":\"%g\",\n", oh.tcp); + else + fprintf(outfile, " \"touch_percent\":%g,\n", oh.tcp); + if (state->backups_found == 1) { + fprintf(outfile, " \"backup_count\":%g,\n", + state->shared_net_root->backups); + fprintf(outfile, " \"backup_percent\":%g,\n", oh.bup); + } + fprintf(outfile, " \"status\":%d\n", oh.status); + fprintf(outfile, " },\n"); /* end of summary */ + fprintf(outfile, " \"trivia\": {\n"); + fprintf(outfile, " \"version\":\"%s\",\n", PACKAGE_VERSION); + fprintf(outfile, " \"conf_file_path\":\"%s\",\n", state->dhcpdconf_file); + fprintf(outfile, " \"conf_file_epoch_mtime\":"); + dp_time_tool(outfile, state->dhcpdconf_file, 1); + fprintf(outfile, ",\n"); + fprintf(outfile, " \"lease_file_path\":\"%s\",\n", state->dhcpdlease_file); + fprintf(outfile, " \"lease_file_epoch_mtime\":"); + dp_time_tool(outfile, state->dhcpdlease_file, 1); + fprintf(outfile, "\n"); + fprintf(outfile, " }"); /* end of trivia */ + } fprintf(outfile, "\n}\n"); - if (outfile == stdout) { - ret = fflush(stdout); - if (ret) { - error(EXIT_FAILURE, errno, "output_json: fflush"); - } - } else { - ret = close_stream(outfile); - if (ret) { - error(EXIT_FAILURE, errno, "output_json: fclose"); - } - } - + close_outfile(outfile); return 0; } @@ -456,22 +649,8 @@ int output_json(void) * * \param f Output file descriptor. */ -static void html_header(FILE *restrict f) +static void html_header(struct conf_t *state, FILE *restrict f) { - char outstr[200]; - struct tm *tmp, result; - - struct stat statbuf; - - stat(config.dhcpdlease_file, &statbuf); - - tmp = localtime_r(&statbuf.st_mtime, &result); - if (tmp == NULL) { - error(EXIT_FAILURE, errno, "html_header: localtime"); - } - if (strftime(outstr, sizeof(outstr), nl_langinfo(D_T_FMT), &result) == 0) { - error(EXIT_FAILURE, 0, "html_header: strftime returned 0"); - } fprintf(f, "\n"); fprintf(f, "\n"); fprintf(f, "\n"); @@ -479,8 +658,8 @@ static void html_header(FILE *restrict f) fprintf(f, "\n"); fprintf(f, "\n"); fprintf(f, "\n"); - fprintf(f, "\n"); - fprintf(f, ""); + fprintf(f, "\n"); + fprintf(f, "\n"); fprintf(f, "\n"); @@ -488,7 +667,9 @@ static void html_header(FILE *restrict f) fprintf(f, "\n"); fprintf(f, "
\n"); fprintf(f, "

ISC DHCPD status

\n"); - fprintf(f, "File %s was last modified at %s
\n", config.dhcpdlease_file, outstr); + fprintf(f, "File %s was last modified at ", state->dhcpdlease_file); + dp_time_tool(f, state->dhcpdlease_file, 0); + fprintf(f, "
\n"); } /*! \brief Footer for full html output format. @@ -501,11 +682,10 @@ static void html_footer(FILE *restrict f) fprintf(f, "Generated using %s
\n", PACKAGE_STRING); fprintf(f, "More info at %s\n", PACKAGE_URL, PACKAGE_URL); fprintf(f, "
\n"); - fprintf(f, "\n"); - fprintf(f, "\n"); - fprintf(f, "\n"); - fprintf(f, "\n"); - fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); fprintf(f, "\n"); } @@ -536,8 +716,7 @@ static void end_tag(FILE *restrict f, char const *restrict tag) * \param class How the data is aligned. * \param text Actual payload of the printout. */ -static void output_line(FILE *restrict f, char const *restrict type, - char const *restrict text) +static void output_line(FILE *restrict f, char const *restrict type, char const *restrict text) { fprintf(f, "<%s>%s\n", type, text, type); } @@ -553,13 +732,31 @@ static void output_double(FILE *restrict f, char const *restrict type, double d) fprintf(f, "<%s>%g\n", type, d, type); } +/*! \brief Line with a potentially colored digit in html output format. + * + * \param state Runtime configuration state. + * \param f Output file descriptor. + * \param type HMTL tag name. + * \param d Actual payload of the printout. + */ +static void output_double_color(struct conf_t *state, + struct output_helper_t *oh, FILE *restrict f, + char const *restrict type) +{ + fprintf(f, "<%s", type); + if (state->color_mode == color_on) + start_color(state, oh, f); + fprintf(f, ">%g", oh->percent); + fprintf(f, "\n", type); +} + /*! \brief Line with float in html output format. * * \param f Output file descriptor. * \param type HTML tag name. * \param fl Actual payload of the printout. */ -static void output_float(FILE *restrict f, char const *restrict type, float fl) +static void output_float(FILE *restrict f, char const *restrict type, double fl) { fprintf(f, "<%s>%.3f\n", type, fl, type); } @@ -570,7 +767,7 @@ static void output_float(FILE *restrict f, char const *restrict type, float fl) */ static void table_start(FILE *restrict f, char const *restrict id, char const *restrict summary) { - fprintf(f, "\n", id, summary); + fprintf(f, "
\n", id, summary); } /*! \brief End table in html output format. @@ -593,71 +790,51 @@ static void newsection(FILE *restrict f, char const *restrict title) } /*! \brief Output html format. */ -int output_html(void) +static int output_html(struct conf_t *state) { - unsigned int i; struct range_t *range_p; - double range_size; struct shared_network_t *shared_p; - int ret; + struct output_helper_t oh; FILE *outfile; - if (config.output_file[0]) { - outfile = fopen(config.output_file, "w+"); - if (outfile == NULL) { - error(EXIT_FAILURE, errno, "output_html: %s", config.output_file); - } - } else { - outfile = stdout; - } - - range_p = ranges; - range_size = get_range_size(range_p); - shared_p = shared_networks; - html_header(outfile); + outfile = open_outfile(state); + range_p = state->ranges; + html_header(state, outfile); newsection(outfile, "Sum of all"); table_start(outfile, "a", "all"); - if (config.header_limit & A_BIT) { + if (state->header_limit & A_BIT) { start_tag(outfile, "thead"); start_tag(outfile, "tr"); output_line(outfile, "th", "name"); output_line(outfile, "th", "max"); output_line(outfile, "th", "cur"); + output_line(outfile, "th", "free"); output_line(outfile, "th", "percent"); output_line(outfile, "th", "touch"); output_line(outfile, "th", "t+c"); output_line(outfile, "th", "t+c perc"); - if (config.backups_found == 1) { + if (state->backups_found == 1) { output_line(outfile, "th", "bu"); output_line(outfile, "th", "bu perc"); } end_tag(outfile, "tr"); end_tag(outfile, "thead"); } - if (config.number_limit & A_BIT) { + if (state->number_limit & A_BIT) { start_tag(outfile, "tbody"); start_tag(outfile, "tr"); - output_line(outfile, "td", shared_networks->name); - output_double(outfile, "td", shared_networks->available); - output_double(outfile, "td", shared_networks->used); - output_float(outfile, "td", - shared_networks->available == - 0 ? NAN : (float)(100 * shared_networks->used) / - shared_networks->available); - output_double(outfile, "td", shared_networks->touched); - output_double(outfile, "td", shared_networks->touched + shared_networks->used); - output_float(outfile, "td", - shared_networks->available == 0 ? NAN : (float)(100 * - (shared_networks->touched - + - shared_networks->used)) - / shared_networks->available); - if (config.backups_found == 1) { - output_double(outfile, "td", shared_networks->backups); - output_float(outfile, "td", - shared_networks->available == 0 ? NAN : (float)(100 * - shared_networks->backups) - / shared_networks->available); + shnet_output_helper(state, &oh, state->shared_net_root); + output_line(outfile, "td", state->shared_net_root->name); + output_double(outfile, "td", state->shared_net_root->available); + output_double(outfile, "td", state->shared_net_root->used); + output_double(outfile, "td", state->shared_net_root->available - state->shared_net_root->used); + output_float(outfile, "td", oh.percent); + output_double(outfile, "td", state->shared_net_root->touched); + output_double(outfile, "td", oh.tc); + output_float(outfile, "td", oh.tcp); + if (state->backups_found == 1) { + output_double(outfile, "td", state->shared_net_root->backups); + output_float(outfile, "td", oh.tcp); } end_tag(outfile, "tr"); end_tag(outfile, "tbody"); @@ -665,48 +842,41 @@ int output_html(void) table_end(outfile); newsection(outfile, "Shared networks"); table_start(outfile, "s", "snet"); - if (config.header_limit & S_BIT) { + if (state->header_limit & S_BIT) { start_tag(outfile, "thead"); start_tag(outfile, "tr"); output_line(outfile, "th", "name"); output_line(outfile, "th", "max"); output_line(outfile, "th", "cur"); + output_line(outfile, "th", "free"); output_line(outfile, "th", "percent"); output_line(outfile, "th", "touch"); output_line(outfile, "th", "t+c"); output_line(outfile, "th", "t+c perc"); - if (config.backups_found == 1) { + if (state->backups_found == 1) { output_line(outfile, "th", "bu"); output_line(outfile, "th", "bu perc"); } end_tag(outfile, "tr"); end_tag(outfile, "thead"); } - if (config.number_limit & S_BIT) { + if (state->number_limit & S_BIT) { start_tag(outfile, "tbody"); - for (i = 0; i < num_shared_networks; i++) { - shared_p++; + for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) { + if (shnet_output_helper(state, &oh, shared_p)) + continue; start_tag(outfile, "tr"); output_line(outfile, "td", shared_p->name); output_double(outfile, "td", shared_p->available); output_double(outfile, "td", shared_p->used); - output_float(outfile, "td", - shared_p->available == - 0 ? NAN : (float)(100 * shared_p->used) / - shared_p->available); + output_double(outfile, "td", shared_p->available - shared_p->used); + output_double_color(state, &oh, outfile, "td"); output_double(outfile, "td", shared_p->touched); - output_double(outfile, "td", shared_p->touched + shared_p->used); - output_float(outfile, "td", - shared_p->available == 0 ? NAN : (float)(100 * - (shared_p->touched + - shared_p->used)) / - shared_p->available); - if (config.backups_found == 1) { + output_double(outfile, "td", oh.tc); + output_float(outfile, "td", oh.tcp); + if (state->backups_found == 1) { output_double(outfile, "td", shared_p->backups); - output_float(outfile, "td", - shared_p->available == 0 ? NAN : (float)(100 * - shared_p->backups) - / shared_p->available); + output_float(outfile, "td", oh.bup); } end_tag(outfile, "tr"); } @@ -715,7 +885,7 @@ int output_html(void) table_end(outfile); newsection(outfile, "Ranges"); table_start(outfile, "r", "ranges"); - if (config.header_limit & R_BIT) { + if (state->header_limit & R_BIT) { start_tag(outfile, "thead"); start_tag(outfile, "tr"); output_line(outfile, "th", "shared net name"); @@ -723,20 +893,26 @@ int output_html(void) output_line(outfile, "th", "last ip"); output_line(outfile, "th", "max"); output_line(outfile, "th", "cur"); + output_line(outfile, "th", "free"); output_line(outfile, "th", "percent"); output_line(outfile, "th", "touch"); output_line(outfile, "th", "t+c"); output_line(outfile, "th", "t+c perc"); - if (config.backups_found == 1) { + if (state->backups_found == 1) { output_line(outfile, "th", "bu"); output_line(outfile, "th", "bu perc"); } end_tag(outfile, "tr"); end_tag(outfile, "thead"); } - if (config.number_limit & R_BIT) { + if (state->number_limit & R_BIT) { + unsigned int i; start_tag(outfile, "tbody"); - for (i = 0; i < num_ranges; i++) { + for (i = 0; i < state->num_ranges; i++) { + if (range_output_helper(state, &oh, range_p)) { + range_p++; + continue; + } start_tag(outfile, "tr"); if (range_p->shared_net) { output_line(outfile, "td", range_p->shared_net->name); @@ -745,75 +921,55 @@ int output_html(void) } output_line(outfile, "td", ntop_ipaddr(&range_p->first_ip)); output_line(outfile, "td", ntop_ipaddr(&range_p->last_ip)); - output_double(outfile, "td", range_size); + output_double(outfile, "td", oh.range_size); output_double(outfile, "td", range_p->count); - output_float(outfile, "td", (float)(100 * range_p->count) / range_size); + output_double(outfile, "td", oh.range_size - range_p->count); + output_double_color(state, &oh, outfile, "td"); output_double(outfile, "td", range_p->touched); - output_double(outfile, "td", range_p->touched + range_p->count); - output_float(outfile, "td", - (float)(100 * - (range_p->touched + range_p->count)) / range_size); - if (config.backups_found == 1) { + output_double(outfile, "td", oh.tc); + output_float(outfile, "td", oh.tcp); + if (state->backups_found == 1) { output_double(outfile, "td", range_p->backups); - output_float(outfile, "td", - (float)(100 * range_p->backups) / range_size); + output_float(outfile, "td", oh.bup); } end_tag(outfile, "tr"); range_p++; - range_size = get_range_size(range_p); } end_tag(outfile, "tbody"); } table_end(outfile); html_footer(outfile); - if (outfile == stdout) { - ret = fflush(stdout); - if (ret) { - error(EXIT_FAILURE, errno, "output_html: fflush"); - } - } else { - ret = close_stream(outfile); - if (ret) { - error(EXIT_FAILURE, errno, "output_html: fclose"); - } - } + close_outfile(outfile); return 0; } /*! \brief Output cvs format. */ -int output_csv(void) +static int output_csv(struct conf_t *state) { - unsigned int i; struct range_t *range_p; - double range_size; struct shared_network_t *shared_p; + struct output_helper_t oh; FILE *outfile; - int ret; - if (config.output_file[0]) { - outfile = fopen(config.output_file, "w+"); - if (outfile == NULL) { - error(EXIT_FAILURE, errno, "output_csv: %s", config.output_file); - } - } else { - outfile = stdout; - } - - range_p = ranges; - range_size = get_range_size(range_p); - shared_p = shared_networks; - if (config.header_limit & R_BIT) { + outfile = open_outfile(state); + range_p = state->ranges; + if (state->header_limit & R_BIT) { fprintf(outfile, "\"Ranges:\"\n"); fprintf (outfile, "\"shared net name\",\"first ip\",\"last ip\",\"max\",\"cur\",\"percent\",\"touch\",\"t+c\",\"t+c perc\""); - if (config.backups_found == 1) { + if (state->backups_found == 1) { fprintf(outfile, ",\"bu\",\"bu perc\""); } fprintf(outfile, "\n"); } - if (config.number_limit & R_BIT) { - for (i = 0; i < num_ranges; i++) { + if (state->number_limit & R_BIT) { + unsigned int i; + for (i = 0; i < state->num_ranges; i++) { + if (range_output_helper(state, &oh, range_p)) { + range_p++; + continue; + } if (range_p->shared_net) { fprintf(outfile, "\"%s\",", range_p->shared_net->name); } else { @@ -822,223 +978,301 @@ int output_csv(void) fprintf(outfile, "\"%s\",", ntop_ipaddr(&range_p->first_ip)); fprintf(outfile, "\"%s\",\"%g\",\"%g\",\"%.3f\",\"%g\",\"%g\",\"%.3f\"", - ntop_ipaddr(&range_p->last_ip), range_size, + ntop_ipaddr(&range_p->last_ip), + oh.range_size, range_p->count, - (float)(100 * range_p->count) / range_size, + oh.percent, range_p->touched, - range_p->touched + range_p->count, - (float)(100 * (range_p->touched + range_p->count)) / range_size); - if (config.backups_found == 1) { - fprintf(outfile, ",\"%g\",\"%.3f\"", - range_p->backups, - (float)(100 * range_p->backups) / range_size); + oh.tc, + oh.tcp); + if (state->backups_found == 1) { + fprintf(outfile, ",\"%g\",\"%.3f\"", range_p->backups, oh.bup); } fprintf(outfile, "\n"); range_p++; - range_size = get_range_size(range_p); } fprintf(outfile, "\n"); } - if (config.header_limit & S_BIT) { + if (state->header_limit & S_BIT) { fprintf(outfile, "\"Shared networks:\"\n"); fprintf(outfile, "\"name\",\"max\",\"cur\",\"percent\",\"touch\",\"t+c\",\"t+c perc\""); - if (config.backups_found == 1) { + if (state->backups_found == 1) { fprintf(outfile, ",\"bu\",\"bu perc\""); } fprintf(outfile, "\n"); } - if (config.number_limit & S_BIT) { + if (state->number_limit & S_BIT) { - for (i = 0; i < num_shared_networks; i++) { - shared_p++; + for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) { + if (shnet_output_helper(state, &oh, shared_p)) + continue; fprintf(outfile, "\"%s\",\"%g\",\"%g\",\"%.3f\",\"%g\",\"%g\",\"%.3f\"", - shared_p->name, shared_p->available, + shared_p->name, + shared_p->available, shared_p->used, - shared_p->available == 0 ? NAN : (float)(100 * shared_p->used) / - shared_p->available, shared_p->touched, - shared_p->touched + shared_p->used, - shared_p->available == 0 ? NAN : (float)(100 * - (shared_p->touched + - shared_p->used)) / - shared_p->available); - if (config.backups_found == 1) { - fprintf(outfile, ",\"%g\",\"%.3f\"", - shared_p->backups, - shared_p->available == - 0 ? NAN : (float)(100 * shared_p->backups) / - shared_p->available); + oh.percent, + shared_p->touched, + oh.tc, + oh.tcp); + if (state->backups_found == 1) { + fprintf(outfile, ",\"%g\",\"%.3f\"", shared_p->backups, oh.bup); } fprintf(outfile, "\n"); } fprintf(outfile, "\n"); } - if (config.header_limit & A_BIT) { + if (state->header_limit & A_BIT) { fprintf(outfile, "\"Sum of all ranges:\"\n"); fprintf(outfile, "\"name\",\"max\",\"cur\",\"percent\",\"touch\",\"t+c\",\"t+c perc\""); - if (config.backups_found == 1) { + if (state->backups_found == 1) { fprintf(outfile, ",\"bu\",\"bu perc\""); } fprintf(outfile, "\n"); } - if (config.number_limit & A_BIT) { - + if (state->number_limit & A_BIT) { + shnet_output_helper(state, &oh, state->shared_net_root); fprintf(outfile, "\"%s\",\"%g\",\"%g\",\"%.3f\",\"%g\",\"%g\",\"%.3f\"", - shared_networks->name, shared_networks->available, - shared_networks->used, - shared_networks->available == - 0 ? NAN : (float)(100 * shared_networks->used) / - shared_networks->available, shared_networks->touched, - shared_networks->touched + shared_networks->used, - shared_networks->available == - 0 ? NAN : (float)(100 * - (shared_networks->touched + - shared_networks->used)) / shared_networks->available); - if (config.backups_found == 1) { - fprintf(outfile, "%7g %8.3f", - shared_networks->backups, - shared_networks->available == - 0 ? NAN : (float)(100 * shared_networks->backups) / - shared_networks->available); + state->shared_net_root->name, + state->shared_net_root->available, + state->shared_net_root->used, + oh.percent, + state->shared_net_root->touched, + oh.tc, + oh.tcp); + if (state->backups_found == 1) { + fprintf(outfile, "%7g %8.3f", state->shared_net_root->backups, oh.bup); } fprintf(outfile, "\n"); } - if (outfile == stdout) { - ret = fflush(stdout); - if (ret) { - error(EXIT_FAILURE, errno, "output_cvs: fflush"); - } - } else { - ret = close_stream(outfile); - if (ret) { - error(EXIT_FAILURE, errno, "output_cvs: fclose"); - } - } + close_outfile(outfile); return 0; } -/*! \brief Output alarm text, and return program exit value. */ -int output_alarming(void) +void range_alarms(struct conf_t *state, struct status_counts_t *rangstat) { - FILE *outfile; - struct range_t *range_p; - double range_size; - struct shared_network_t *shared_p; + struct output_helper_t oh; + struct range_t *range_p = state->ranges; unsigned int i; - float perc; - int rw = 0, rc = 0, ro = 0, ri = 0, sw = 0, sc = 0, so = 0, si = 0; - int ret_val, ret; - range_p = ranges; - range_size = get_range_size(range_p); - shared_p = shared_networks; - - if (config.output_file[0]) { - outfile = fopen(config.output_file, "w+"); - if (outfile == NULL) { - error(EXIT_FAILURE, errno, "output_alarming: %s", config.output_file); - } - } else { - outfile = stdout; - } - - if (config.number_limit & R_BIT) { - for (i = 0; i < num_ranges; i++) { - if (config.snet_alarms && range_p->shared_net != shared_networks) { - continue; - } - if (config.minsize < range_size) { - perc = (float)(100 * range_p->count) / range_size; - if (config.critical < perc && (range_size - range_p->count) < config.crit_count) - rc++; - else if (config.warning < perc && (range_size - range_p->count) < config.warn_count) - rw++; - else - ro++; - } else { - ri++; + if (state->number_limit & R_BIT) { + for (i = 0; i < state->num_ranges; i++) { + range_output_helper(state, &oh, range_p); + switch (oh.status) { + case STATUS_SUPPRESSED: + break; + case STATUS_IGNORED: + rangstat->ignored++; + break; + case STATUS_CRIT: + rangstat->critical++; + break; + case STATUS_WARN: + rangstat->warning++; + break; + case STATUS_OK: + rangstat->ok++; + break; + default: + abort(); } range_p++; - range_size = get_range_size(range_p); } } - if (config.number_limit & S_BIT) { - for (i = 0; i < num_shared_networks; i++) { - shared_p++; - if (config.minsize < shared_p->available) { - perc = - shared_p->available == - 0 ? 100 : (float)(100 * shared_p->used) / shared_p->available; - if (config.critical < perc && shared_p->used < config.crit_count) - sc++; - else if (config.warning < perc && shared_p->used < config.warn_count) - sw++; - else - so++; - } else { - si++; +} + +void shared_net_alarms(struct conf_t *state, struct status_counts_t *sharstat) +{ + struct output_helper_t oh; + struct shared_network_t *shared_p; + + if (state->number_limit & S_BIT) { + for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) { + shnet_output_helper(state, &oh, shared_p); + switch (oh.status) { + case STATUS_SUPPRESSED: + break; + case STATUS_IGNORED: + sharstat->ignored++; + break; + case STATUS_CRIT: + sharstat->critical++; + break; + case STATUS_WARN: + sharstat->warning++; + break; + case STATUS_OK: + sharstat->ok++; + break; + default: + abort(); } } } +} - if (sc || rc) + +/*! \brief Output alarm text, and return program exit value. */ +static int output_alarming(struct conf_t *state) +{ + FILE *outfile; + unsigned int i; + int ret_val; + struct status_counts_t rangstat = { 0 }; + struct status_counts_t sharstat = { 0 }; + + outfile = open_outfile(state); + + range_alarms(state, &rangstat); + shared_net_alarms(state, &sharstat); + + if (rangstat.critical || sharstat.critical) ret_val = STATE_CRITICAL; - else if (sw || rw) + else if (rangstat.warning || sharstat.warning) ret_val = STATE_WARNING; else ret_val = STATE_OK; - if ((0 < rc && config.number_limit & R_BIT) - || (0 < sc && config.number_limit & S_BIT)) { + if ((0 < rangstat.critical && state->number_limit & R_BIT) + || (0 < sharstat.critical && state->number_limit & S_BIT)) { fprintf(outfile, "CRITICAL: %s:", program_name); - } else if ((0 < rw && config.number_limit & R_BIT) - || (0 < sw && config.number_limit & S_BIT)) { + } else if ((0 < rangstat.warning && state->number_limit & R_BIT) + || (0 < sharstat.warning && state->number_limit & S_BIT)) { fprintf(outfile, "WARNING: %s:", program_name); } else { - if (config.number_limit & A_BIT) + if (state->number_limit & A_BIT) fprintf(outfile, "OK:"); - else + else { + if (close_stream(outfile)) { + error(EXIT_FAILURE, errno, "output_alarming: fclose"); + } return ret_val; - } - if (config.header_limit & R_BIT) { - fprintf(outfile, " Ranges - crit: %d warn: %d ok: %d", rc, rw, ro); - if (ri != 0) { - fprintf(outfile, " ignored: %d", ri); } - fprintf(outfile, "; | range_crit=%d range_warn=%d range_ok=%d", rc, rw, ro); - if (ri != 0) { - fprintf(outfile, " range_ignored=%d", ri); + } + if (state->header_limit & R_BIT) { + fprintf(outfile, " Ranges - crit: %d warn: %d ok: %d", rangstat.critical, rangstat.warning, rangstat.ok); + if (rangstat.ignored != 0) { + fprintf(outfile, " ignored: %d", rangstat.ignored); + } + fprintf(outfile, "; | range_crit=%d range_warn=%d range_ok=%d", rangstat.critical, rangstat.warning, rangstat.ok); + if (rangstat.ignored != 0) { + fprintf(outfile, " range_ignored=%d", rangstat.ignored); + } + if (state->perfdata == 1 && state->number_limit & R_BIT) { + /* last to first order. too late to fix this after so many years */ + struct range_t *range_p = state->ranges + state->num_ranges - 1; + + for (i = state->num_ranges; i; i--) { + struct output_helper_t oh; + + if (range_output_helper(state, &oh, range_p)) + continue; + if (state->minsize < oh.range_size) { + fprintf(outfile, " %s_r=", ntop_ipaddr(&range_p->first_ip)); + fprintf(outfile, "%g;%g;%g;0;%g", + range_p->count, + (oh.range_size * state->warning / 100), + (oh.range_size * state->critical / 100), oh.range_size); + fprintf(outfile, " %s_rt=%g", + ntop_ipaddr(&range_p->first_ip), range_p->touched); + if (state->backups_found == 1) { + fprintf(outfile, " %s_rbu=%g", + ntop_ipaddr(&range_p->first_ip), + range_p->backups); + } + } + range_p--; + } } fprintf(outfile, "\n"); } else { fprintf(outfile, " "); } - if (config.header_limit & S_BIT) { - fprintf(outfile, "Shared nets - crit: %d warn: %d ok: %d", sc, sw, so); - if (si != 0) { - fprintf(outfile, " ignored: %d", si); + if (state->header_limit & S_BIT) { + fprintf(outfile, "Shared nets - crit: %d warn: %d ok: %d", sharstat.critical, sharstat.warning, sharstat.ok); + if (sharstat.ignored != 0) { + fprintf(outfile, " ignored: %d", sharstat.ignored); } - fprintf(outfile, "; | snet_crit=%d snet_warn=%d snet_ok=%d", sc, sw, so); - if (si != 0) { - fprintf(outfile, " snet_ignored=%d\n", si); + fprintf(outfile, "; | snet_crit=%d snet_warn=%d snet_ok=%d", sharstat.critical, sharstat.warning, sharstat.ok); + if (sharstat.ignored != 0) { + fprintf(outfile, " snet_ignored=%d", sharstat.ignored); + } + if (state->perfdata == 1 && state->header_limit & R_BIT) { + struct shared_network_t *shared_p; + + for (shared_p = state->shared_net_root->next; shared_p; shared_p = shared_p->next) { + struct output_helper_t oh; + + if (shnet_output_helper(state, &oh, shared_p)) + continue; + if (state->minsize < shared_p->available) { + fprintf(outfile, " '%s_s'=%g;%g;%g;0;%g", + shared_p->name, + shared_p->used, + (shared_p->available * state->warning / 100), + (shared_p->available * state->critical / 100), + shared_p->available); + fprintf(outfile, " '%s_st'=%g", + shared_p->name, shared_p->touched); + if (state->backups_found == 1) { + fprintf(outfile, " '%s_sbu'=%g", + shared_p->name, shared_p->backups); + } + } + } + fprintf(outfile, "\n"); } } fprintf(outfile, "\n"); - if (outfile == stdout) { - ret = fflush(stdout); - if (ret) { - error(EXIT_FAILURE, errno, "output_alarming: fflush"); - } - } else { - ret = close_stream(outfile); - if (ret) { - error(EXIT_FAILURE, errno, "output_alarming: fclose"); - } - } + close_outfile(outfile); return ret_val; } + +/*! \brief Return output_format_names enum based on single char input. */ +int output_analysis(struct conf_t *state) +{ + int ret = 1; + + switch (state->output_format) { + case 't': + state->color_format = OUT_FORM_TEXT; + ret = output_txt(state); + break; + case 'a': + ret = output_alarming(state); + break; + case 'h': + error(EXIT_FAILURE, 0, "html table only output format is deprecated"); + break; + case 'H': + state->color_format = OUT_FORM_HTML; + ret = output_html(state); + break; + case 'x': + /* fallthrough */ + case 'X': + ret = output_xml(state); + break; + case 'j': + /* fallthrough */ + case 'J': + ret = output_json(state); + break; + case 'c': + ret = output_csv(state); + break; +#ifdef BUILD_MUSTACH + case 'm': + ret = mustach_dhcpd_pools(state); + break; +#endif + default: + error(EXIT_FAILURE, 0, "unknown output format: '%c'", state->output_format); + } + return ret; +} diff --git a/src/sort.c b/src/sort.c index 0bc9a5b..ab252c0 100644 --- a/src/sort.c +++ b/src/sort.c @@ -40,6 +40,7 @@ #include +#include #include #include #include @@ -47,6 +48,7 @@ #include "error.h" #include "progname.h" #include "quote.h" +#include "xalloc.h" #include "dhcpd-pools.h" @@ -117,9 +119,14 @@ int rangecomp(const void *restrict r1, const void *restrict r2) */ int comp_double(double f1, double f2) { - return f1 < f2 ? -1 : f1 > f2 ? 1 : 0; + if (isless(f1, f2)) + return -1; + else if (isless(f2, f1)) + return 1; + return 0; } + /*! \brief Compare two range_t by their first_ip. * \param r1,r2 Pointers to data to compare. * \return Like strcmp. @@ -239,8 +246,8 @@ comparer_t field_selector(char c) default: { char str[2] = { c, '\0' }; - clean_up(); - error(EXIT_FAILURE, 0, "field_selector: unknown sort order: %s", quote(str)); + error(EXIT_FAILURE, 0, "field_selector: unknown sort order: %s", + quote(str)); } } return NULL; @@ -251,12 +258,12 @@ comparer_t field_selector(char c) * \param right The right side of the merge sort. * \return Relevant for merge sort decision. */ -static int merge(struct range_t *restrict left, struct range_t *restrict right) +static int merge(struct conf_t *state, struct range_t *restrict left, struct range_t *restrict right) { struct output_sort *p; int ret; - for (p = config.sorts; p; p = p->next) { + for (p = state->sorts; p; p = p->next) { if (p->func == NULL) { /* String sorting is special. */ ret = strcmp(left->shared_net->name, right->shared_net->name); @@ -274,43 +281,46 @@ static int merge(struct range_t *restrict left, struct range_t *restrict right) } /*! \brief Mergesort for range table. - * \param orig Pointer to range that is requested to be sorted. - * \param size Number of ranges to be sorted. - * \param temp Temporary memory space, needed when a values has to be - * flipped. */ -void mergesort_ranges(struct range_t *restrict orig, int size, struct range_t *restrict temp) +void mergesort_ranges(struct conf_t *state, struct range_t *restrict orig, unsigned int size, + struct range_t *restrict temp, const int root_call) { - int left, right, i; + unsigned int left, i, u_right; struct range_t hold; - /* Merge sort split size */ - static const int MIN_MERGE_SIZE = 8; + static const unsigned int MIN_MERGE_SIZE = 8; + + if (temp == NULL) + temp = xmalloc(sizeof(struct range_t) * size); if (size < MIN_MERGE_SIZE) { for (left = 0; left < size; left++) { + int s_right; + hold = *(orig + left); - for (right = left - 1; 0 <= right; right--) { - if (merge((orig + right), &hold)) + for (s_right = left - 1; 0 <= s_right; s_right--) { + if (merge(state, (orig + s_right), &hold)) break; - *(orig + right + 1) = *(orig + right); + *(orig + s_right + 1) = *(orig + s_right); } - *(orig + right + 1) = hold; + *(orig + s_right + 1) = hold; } + if (root_call) + free(temp); return; } - mergesort_ranges(orig, size / 2, temp); - mergesort_ranges(orig + size / 2, size - size / 2, temp); + mergesort_ranges(state, orig, size / 2, temp, 0); + mergesort_ranges(state, orig + size / 2, size - size / 2, temp, 0); left = 0; - right = size / 2; + u_right = size / 2; i = 0; - while (left < size / 2 && right < size) { - if (merge((orig + left), (orig + right))) { + while (left < size / 2 && u_right < size) { + if (merge(state, (orig + left), (orig + u_right))) { *(temp + i) = *(orig + left); left++; } else { - *(temp + i) = *(orig + right); - right++; + *(temp + i) = *(orig + u_right); + u_right++; } i++; } @@ -319,10 +329,13 @@ void mergesort_ranges(struct range_t *restrict orig, int size, struct range_t *r left++; i++; } - while (right < size) { - *(temp + i) = *(orig + right); - right++; + while (u_right < size) { + *(temp + i) = *(orig + u_right); + u_right++; i++; } memcpy(orig, temp, size * sizeof(struct range_t)); + + if (root_call) + free(temp); } diff --git a/tests/Makemodule.am b/tests/Makemodule.am index b4c0a94..8e31e8e 100644 --- a/tests/Makemodule.am +++ b/tests/Makemodule.am @@ -10,18 +10,37 @@ TESTS = \ tests/alarm-warning-ranges \ tests/alarm-warning-snets \ tests/shnet-alarm \ + tests/big-small \ + tests/binding-states \ tests/bootp \ + tests/cidr-v4 \ + tests/cidr-v6 \ tests/complete \ + tests/complete-perfdata \ tests/empty \ + tests/errors \ tests/full-json \ tests/full-xml \ + tests/formats \ tests/leading0 \ tests/one-ip \ tests/one-line \ + tests/parser \ + tests/range4 \ + tests/range6 \ tests/same-twice \ + tests/shufled \ tests/simple \ + tests/skip \ tests/sorts \ - tests/v6 + tests/statuses \ + tests/v6 \ + tests/v6-perfdata + +if ENABLE_MUSTACH +TESTS += \ + tests/mustach +endif EXTRA_DIST += \ tests/confs \ diff --git a/tests/alarm-count-option b/tests/alarm-count-option index 15f1dea..37d894d 100755 --- a/tests/alarm-count-option +++ b/tests/alarm-count-option @@ -10,25 +10,32 @@ fi echo '== warn count ==' > tests/outputs/$IAM dhcpd-pools --config $top_srcdir/tests/confs/complete --leases $top_srcdir/tests/leases/complete \ - --warning=40 --warn-count=20 --output=tests/outputs/$IAM-too + --color=never --warning=40 --warn-count=20 --output=tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too cat tests/outputs/$IAM-too >> tests/outputs/$IAM echo '== crit count ==' >> tests/outputs/$IAM dhcpd-pools --config $top_srcdir/tests/confs/complete --leases $top_srcdir/tests/leases/complete \ - --critical=40 --crit-count=20 --output=tests/outputs/$IAM-too + --color=never --critical=40 --crit-count=20 --output=tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too cat tests/outputs/$IAM-too >> tests/outputs/$IAM echo '== minsize ==' >> tests/outputs/$IAM dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ - --warning=40 --warn-count=20 --minsize=40 -o tests/outputs/$IAM-too + --color=never --warning=40 --warn-count=20 --minsize=40 -o tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too cat tests/outputs/$IAM-too >> tests/outputs/$IAM echo '== snet alarms ==' >> tests/outputs/$IAM dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ - --warning=40 --snet-alarms -o tests/outputs/$IAM-too + --color=never --warning=40 --snet-alarms -o tests/outputs/$IAM-too +echo $? >> tests/outputs/$IAM-too +cat tests/outputs/$IAM-too >> tests/outputs/$IAM + +echo '== shared net count alarms ==' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ + --color=always --format t --crit-count=20 --critical=0 --warning=0 --warn-count=24 \ + --output=tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too cat tests/outputs/$IAM-too >> tests/outputs/$IAM diff --git a/tests/big-small b/tests/big-small new file mode 120000 index 0000000..61a58b0 --- /dev/null +++ b/tests/big-small @@ -0,0 +1 @@ +test.sh \ No newline at end of file diff --git a/tests/binding-states b/tests/binding-states new file mode 100755 index 0000000..a5e98d0 --- /dev/null +++ b/tests/binding-states @@ -0,0 +1,14 @@ +#!/bin/sh +# +# Minimal regression test suite. + +IAM=$(basename $0) + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +dhcpd-pools -c $top_srcdir/tests/confs/$IAM --color=never \ + -l $top_srcdir/tests/leases/$IAM -o tests/outputs/$IAM +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/tests/cidr-v4 b/tests/cidr-v4 new file mode 120000 index 0000000..61a58b0 --- /dev/null +++ b/tests/cidr-v4 @@ -0,0 +1 @@ +test.sh \ No newline at end of file diff --git a/tests/cidr-v6 b/tests/cidr-v6 new file mode 120000 index 0000000..61a58b0 --- /dev/null +++ b/tests/cidr-v6 @@ -0,0 +1 @@ +test.sh \ No newline at end of file diff --git a/tests/complete-perfdata b/tests/complete-perfdata new file mode 100755 index 0000000..2765fd4 --- /dev/null +++ b/tests/complete-perfdata @@ -0,0 +1,16 @@ +#!/bin/sh +# +# Minimal regression test suite. + +IAM=$(basename $0) +TESTDATA=${IAM%-*} + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +dhcpd-pools -c $top_srcdir/tests/confs/$TESTDATA \ + -l $top_srcdir/tests/leases/$TESTDATA -o tests/outputs/$IAM \ + --warning 80 --perfdata +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/tests/confs/big-small b/tests/confs/big-small new file mode 100644 index 0000000..7cc8997 --- /dev/null +++ b/tests/confs/big-small @@ -0,0 +1,5 @@ +subnet 10.0.0.0 netmask 255.255.255.0 { + pool { + range 10.0.0.10 10.0.0.1; + } +} diff --git a/tests/confs/binding-states b/tests/confs/binding-states new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/confs/binding-states @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/confs/cidr-v4 b/tests/confs/cidr-v4 new file mode 100644 index 0000000..18ba31c --- /dev/null +++ b/tests/confs/cidr-v4 @@ -0,0 +1,31 @@ +shared-network example1 { + subnet 10.0.0.0 netmask 255.255.255.0 { + pool { + range 10.0.0.1/27; + } + } + subnet 10.1.0.0 netmask 255.255.255.0 { + pool { + range 10.1.0.1/27; + } + } +} + +shared-network example2 { + subnet 10.2.0.0 netmask 255.255.255.0 { + pool { + range 10.2.0.1/27; + } + } + subnet 10.3.0.0 netmask 255.255.255.0 { + pool { + range 10.3.0.1/27; + } + } +} + +subnet 10.4.0.0 netmask 255.255.255.0 { + pool { + range 10.4.0.1/28; + } +} diff --git a/tests/confs/cidr-v6 b/tests/confs/cidr-v6 new file mode 100644 index 0000000..a57036c --- /dev/null +++ b/tests/confs/cidr-v6 @@ -0,0 +1,10 @@ +subnet6 dead:abba:1000::/56 { + range6 dead:abba:1000::2/120; + prefix6 dead:abba:1000:0100:: dead:abba:1000:ff00::/56; +} + +subnet6 dead:abba:40ff::/56 { + range6 dead:abba:4000::2/120; + prefix6 dead:abba:4000:0100:: dead:abba:4000:ff00::/56; +} + diff --git a/tests/confs/formats b/tests/confs/formats new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/confs/formats @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/confs/range4 b/tests/confs/range4 new file mode 100644 index 0000000..6540de7 --- /dev/null +++ b/tests/confs/range4 @@ -0,0 +1,31 @@ +shared-network example1 { + subnet 10.0.0.0 netmask 255.255.255.0 { + pool { + range 10.0.0.0/27; + } + } + subnet 10.1.0.0 netmask 255.255.255.0 { + pool { + range 10.1.0.1/27; + } + } +} + +shared-network example2 { + subnet 10.2.0.0 netmask 255.255.255.0 { + pool { + range 10.2.0.1/28; + } + } + subnet 10.3.0.0 netmask 255.255.255.0 { + pool { + range 10.3.0.1/29; + } + } +} + +subnet 10.4.0.0 netmask 255.255.255.0 { + pool { + range 10.4.0.1/32; + } +} diff --git a/tests/confs/range6 b/tests/confs/range6 new file mode 100644 index 0000000..b15d615 --- /dev/null +++ b/tests/confs/range6 @@ -0,0 +1,4 @@ +subnet6 dead:abba:1000::/56 { + range6 dead:abba:1000::/56; + prefix6 dead:abba:1000:0100:: dead:abba:1000:ff00::/56; +} diff --git a/tests/confs/shnet-alarm b/tests/confs/shnet-alarm deleted file mode 100644 index 0ef9eb0..0000000 --- a/tests/confs/shnet-alarm +++ /dev/null @@ -1,31 +0,0 @@ -shared-network example1 { - subnet 10.0.0.0 netmask 255.255.255.0 { - pool { - range 10.0.0.1 10.0.0.20; - } - } - subnet 10.1.0.0 netmask 255.255.255.0 { - pool { - range 10.1.0.1 10.1.0.20; - } - } -} - -shared-network example2 { - subnet 10.2.0.0 netmask 255.255.255.0 { - pool { - range 10.2.0.1 10.2.0.20; - } - } - subnet 10.3.0.0 netmask 255.255.255.0 { - pool { - range 10.3.0.1 10.3.0.20; - } - } -} - -subnet 10.4.0.0 netmask 255.255.255.0 { - pool { - range 10.4.0.1 10.4.0.20; - } -} diff --git a/tests/confs/shnet-alarm b/tests/confs/shnet-alarm new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/confs/shnet-alarm @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/confs/shufled b/tests/confs/shufled new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/confs/shufled @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/confs/statuses b/tests/confs/statuses new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/confs/statuses @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/errors b/tests/errors new file mode 100755 index 0000000..49a4f11 --- /dev/null +++ b/tests/errors @@ -0,0 +1,48 @@ +#!/bin/sh +# +# Test error inputs. + +IAM=$(basename $0) + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +echo '=== output mask' >| tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ + -L 88 >> tests/outputs/$IAM 2>&1 + +echo '=== unknown specifier' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ + --skip=okish >> tests/outputs/$IAM 2>&1 + +echo '=== color mode' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ + --color=sometimes >> tests/outputs/$IAM 2>&1 + +echo '=== IPv5' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ + --ip-version=5 >> tests/outputs/$IAM 2>&1 + +echo '=== missing conf' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete_NXFILE -l $top_srcdir/tests/leases/complete 2>&1 | \ + sed 's/: ..\/..\/tests/: .\/tests/'>> tests/outputs/$IAM + +echo '=== missing leases' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete_NXFILE 2>&1 | \ + sed 's/: ..\/..\/tests/: .\/tests/' >> tests/outputs/$IAM + +echo '=== html table' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ + --format=html >> tests/outputs/$IAM 2>&1 + +echo '=== none existing format' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ + -fz >> tests/outputs/$IAM 2>&1 + +echo '=== broken percent input' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ + --warning=eighty >> tests/outputs/$IAM 2>&1 + +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/tests/expected/alarm-count-option b/tests/expected/alarm-count-option index 5b00b83..c2f466e 100644 --- a/tests/expected/alarm-count-option +++ b/tests/expected/alarm-count-option @@ -7,11 +7,28 @@ CRITICAL: dhcpd-pools: Ranges - crit: 3 warn: 0 ok: 2; | range_crit=3 range_warn Shared nets - crit: 1 warn: 0 ok: 1; | snet_crit=1 snet_warn=0 snet_ok=1 2 == minsize == -OK: Ranges - crit: 0 warn: 0 ok: 0 ignored: 5; | range_crit=0 range_warn=0 range_ok=0 range_ignored=5 +OK: Ranges - crit: 0 warn: 0 ok: 2 ignored: 3; | range_crit=0 range_warn=0 range_ok=2 range_ignored=3 Shared nets - crit: 0 warn: 0 ok: 0 ignored: 2; | snet_crit=0 snet_warn=0 snet_ok=0 snet_ignored=2 - 0 == snet alarms == -WARNING: dhcpd-pools: Ranges - crit: 0 warn: 0 ok: 0; | range_crit=0 range_warn=0 range_ok=0 +WARNING: dhcpd-pools: Ranges - crit: 0 warn: 0 ok: 2; | range_crit=0 range_warn=0 range_ok=2 Shared nets - crit: 0 warn: 2 ok: 0; | snet_crit=0 snet_warn=2 snet_ok=0 1 +== shared net count alarms == +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +example1 10.0.0.1 - 10.0.0.20 20 11 55.000 0 11 55.000 +example1 10.1.0.1 - 10.1.0.20 20 10 50.000 0 10 50.000 +example2 10.2.0.1 - 10.2.0.20 20 8 40.000 0 8 40.000 +example2 10.3.0.1 - 10.3.0.20 20 9 45.000 0 9 45.000 +All networks 10.4.0.1 - 10.4.0.20 20 5 25.000 0 5 25.000 + +Shared networks: +name max cur percent touch t+c t+c perc +example1 40 21 52.500 0 21 52.500 +example2 40 17 42.500 0 17 42.500 + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 100 43 43.000 0 43 43.000 +0 diff --git a/tests/expected/big-small b/tests/expected/big-small new file mode 120000 index 0000000..8fd3246 --- /dev/null +++ b/tests/expected/big-small @@ -0,0 +1 @@ +simple \ No newline at end of file diff --git a/tests/expected/binding-states b/tests/expected/binding-states new file mode 100644 index 0000000..840db2c --- /dev/null +++ b/tests/expected/binding-states @@ -0,0 +1,16 @@ +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc bu bu perc +example1 10.0.0.1 - 10.0.0.20 20 4 20.000 3 7 35.000 2 10.000 +example1 10.1.0.1 - 10.1.0.20 20 10 50.000 0 10 50.000 0 0.000 +example2 10.2.0.1 - 10.2.0.20 20 8 40.000 0 8 40.000 0 0.000 +example2 10.3.0.1 - 10.3.0.20 20 9 45.000 0 9 45.000 0 0.000 +All networks 10.4.0.1 - 10.4.0.20 20 5 25.000 0 5 25.000 0 0.000 + +Shared networks: +name max cur percent touch t+c t+c perc bu bu perc +example1 40 14 35.000 3 17 42.500 2 5.000 +example2 40 17 42.500 0 17 42.500 0 0.000 + +Sum of all ranges: +name max cur percent touch t+c t+c perc bu bu perc +All networks 100 36 36.000 3 39 39.000 2 2.000 diff --git a/tests/expected/bootp b/tests/expected/bootp deleted file mode 100644 index da14b05..0000000 --- a/tests/expected/bootp +++ /dev/null @@ -1,10 +0,0 @@ -Ranges: -shared net name first ip last ip max cur percent touch t+c t+c perc -All networks 10.0.0.1 - 10.0.0.10 10 10 100.000 0 10 100.000 - -Shared networks: -name max cur percent touch t+c t+c perc - -Sum of all ranges: -name max cur percent touch t+c t+c perc -All networks 10 10 100.000 0 10 100.000 diff --git a/tests/expected/bootp b/tests/expected/bootp new file mode 120000 index 0000000..8fd3246 --- /dev/null +++ b/tests/expected/bootp @@ -0,0 +1 @@ +simple \ No newline at end of file diff --git a/tests/expected/cidr-v4 b/tests/expected/cidr-v4 new file mode 100644 index 0000000..db0e50e --- /dev/null +++ b/tests/expected/cidr-v4 @@ -0,0 +1,16 @@ +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +example1 10.0.0.1 - 10.0.0.31 31 11 35.484 0 11 35.484 +example1 10.1.0.1 - 10.1.0.31 31 10 32.258 0 10 32.258 +example2 10.2.0.1 - 10.2.0.31 31 8 25.806 0 8 25.806 +example2 10.3.0.1 - 10.3.0.31 31 9 29.032 0 9 29.032 +All networks 10.4.0.1 - 10.4.0.15 15 5 33.333 0 5 33.333 + +Shared networks: +name max cur percent touch t+c t+c perc +example1 62 21 33.871 0 21 33.871 +example2 62 17 27.419 0 17 27.419 + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 139 43 30.935 0 43 30.935 diff --git a/tests/expected/cidr-v6 b/tests/expected/cidr-v6 new file mode 100644 index 0000000..04a2462 --- /dev/null +++ b/tests/expected/cidr-v6 @@ -0,0 +1,11 @@ +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +All networks dead:abba:1000::2 - dead:abba:1000::ff 254 0 0.000 0 0 0.000 +All networks dead:abba:4000::2 - dead:abba:4000::ff 254 1 0.394 0 1 0.394 + +Shared networks: +name max cur percent touch t+c t+c perc + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 508 1 0.197 0 1 0.197 diff --git a/tests/expected/complete-perfdata b/tests/expected/complete-perfdata new file mode 100644 index 0000000..dfe0ac6 --- /dev/null +++ b/tests/expected/complete-perfdata @@ -0,0 +1,3 @@ +OK: Ranges - crit: 0 warn: 0 ok: 5; | range_crit=0 range_warn=0 range_ok=5 10.4.0.1_r=5;16;18;0;20 10.4.0.1_rt=0 10.3.0.1_r=9;16;18;0;20 10.3.0.1_rt=0 10.2.0.1_r=8;16;18;0;20 10.2.0.1_rt=0 10.1.0.1_r=10;16;18;0;20 10.1.0.1_rt=0 10.0.0.1_r=11;16;18;0;20 10.0.0.1_rt=0 +Shared nets - crit: 0 warn: 0 ok: 2; | snet_crit=0 snet_warn=0 snet_ok=2 'example1_s'=21;32;36;0;40 'example1_st'=0 'example2_s'=17;32;36;0;40 'example2_st'=0 + diff --git a/tests/expected/errors b/tests/expected/errors new file mode 100644 index 0000000..6903ac6 --- /dev/null +++ b/tests/expected/errors @@ -0,0 +1,27 @@ +=== output mask +dhcpd-pools: return_limit: output mask '88' is illegal +=== unknown specifier +dhcpd-pools: unknown --skip specifier: okish +=== color mode +dhcpd-pools: unknown color mode: 'sometimes' +=== IPv5 +dhcpd-pools: unknown --ip-version argument: 5 +=== missing conf +dhcpd-pools: cannot open inlude: ./tests/confs/complete_NXFILE: No such file or directory +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc + +Shared networks: +name max cur percent touch t+c t+c perc + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 0 0 nan 0 0 nan +=== missing leases +dhcpd-pools: parse_leases: ./tests/leases/complete_NXFILE: No such file or directory +=== html table +dhcpd-pools: html table only output format is deprecated +=== none existing format +dhcpd-pools: unknown output format: 'z' +=== broken percent input +dhcpd-pools: illegal argument: 'eighty' diff --git a/tests/expected/formats b/tests/expected/formats new file mode 100644 index 0000000..4023f89 --- /dev/null +++ b/tests/expected/formats @@ -0,0 +1,284 @@ +=== color text +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +example1 10.0.0.1 - 10.0.0.20 20 11 55.000 0 11 55.000 +example1 10.1.0.1 - 10.1.0.20 20 10 50.000 0 10 50.000 +example2 10.2.0.1 - 10.2.0.20 20 8 40.000 0 8 40.000 +example2 10.3.0.1 - 10.3.0.20 20 9 45.000 0 9 45.000 +All networks 10.4.0.1 - 10.4.0.20 20 5 25.000 0 5 25.000 + +Shared networks: +name max cur percent touch t+c t+c perc +example1 40 21 52.500 0 21 52.500 +example2 40 17 42.500 0 17 42.500 + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 100 43 43.000 0 43 43.000 +=== html + + + +ISC dhcpd dhcpd-pools output + + + + + + + + +
+

ISC DHCPD status

+

Sum of all

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
namemaxcurfreepercenttoucht+ct+c perc
All networks100435743.00004343.000
+

Shared networks

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
namemaxcurfreepercenttoucht+ct+c perc
example140211952.502152.500
example240172342.501742.500
+

Ranges

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
shared net namefirst iplast ipmaxcurfreepercenttoucht+ct+c perc
example110.0.0.110.0.0.20201195501155.000
example110.1.0.110.1.0.202010105001050.000
example210.2.0.110.2.0.2020812400840.000
example210.3.0.110.3.0.2020911450945.000
All networks10.4.0.110.4.0.2020515250525.000
+
+ + + + + +=== xml + + + example1 + 10.0.0.1 - 10.0.0.20 + 20 + 11 + 0 + 9 + + + example1 + 10.1.0.1 - 10.1.0.20 + 20 + 10 + 0 + 10 + + + example2 + 10.2.0.1 - 10.2.0.20 + 20 + 8 + 0 + 12 + + + example2 + 10.3.0.1 - 10.3.0.20 + 20 + 9 + 0 + 11 + + + All networks + 10.4.0.1 - 10.4.0.20 + 20 + 5 + 0 + 15 + + + example1 + 40 + 21 + 0 + 19 + + + example2 + 40 + 17 + 0 + 23 + + + All networks + 100 + 43 + 0 + 57 + + +=== csv +"Ranges:" +"shared net name","first ip","last ip","max","cur","percent","touch","t+c","t+c perc" +"example1","10.0.0.1","10.0.0.20","20","11","55.000","0","11","55.000" +"example1","10.1.0.1","10.1.0.20","20","10","50.000","0","10","50.000" +"example2","10.2.0.1","10.2.0.20","20","8","40.000","0","8","40.000" +"example2","10.3.0.1","10.3.0.20","20","9","45.000","0","9","45.000" +"All networks","10.4.0.1","10.4.0.20","20","5","25.000","0","5","25.000" + +"Shared networks:" +"name","max","cur","percent","touch","t+c","t+c perc" +"example1","40","21","52.500","0","21","52.500" +"example2","40","17","42.500","0","17","42.500" + +"Sum of all ranges:" +"name","max","cur","percent","touch","t+c","t+c perc" +"All networks","100","43","43.000","0","43","43.000" +=== json +{ + "subnets": [ + { "location":"example1", "range":"10.0.0.1 - 10.0.0.20", "first_ip":"10.0.0.1", "last_ip":"10.0.0.20", "defined":20, "used":11, "touched":0, "free":9, "percent":55, "touch_count":11, "touch_percent":55, "status":0 }, + { "location":"example1", "range":"10.1.0.1 - 10.1.0.20", "first_ip":"10.1.0.1", "last_ip":"10.1.0.20", "defined":20, "used":10, "touched":0, "free":10, "percent":50, "touch_count":10, "touch_percent":50, "status":0 }, + { "location":"example2", "range":"10.2.0.1 - 10.2.0.20", "first_ip":"10.2.0.1", "last_ip":"10.2.0.20", "defined":20, "used":8, "touched":0, "free":12, "percent":40, "touch_count":8, "touch_percent":40, "status":0 }, + { "location":"example2", "range":"10.3.0.1 - 10.3.0.20", "first_ip":"10.3.0.1", "last_ip":"10.3.0.20", "defined":20, "used":9, "touched":0, "free":11, "percent":45, "touch_count":9, "touch_percent":45, "status":0 }, + { "location":"All networks", "range":"10.4.0.1 - 10.4.0.20", "first_ip":"10.4.0.1", "last_ip":"10.4.0.20", "defined":20, "used":5, "touched":0, "free":15, "percent":25, "touch_count":5, "touch_percent":25, "status":0 } + ], + "shared-networks": [ + { "location":"example1", "defined":40, "used":21, "touched":0, "free":19, "percent":52.5, "touch_count":21, "touch_percent":52.5, "status":0 }, + { "location":"example2", "defined":40, "used":17, "touched":0, "free":23, "percent":42.5, "touch_count":17, "touch_percent":42.5, "status":0 } + ] +} +=== perfdata +CRITICAL: dhcpd-pools: Ranges - crit: 3 warn: 2 ok: 0; | range_crit=3 range_warn=2 range_ok=0 10.4.0.1_r=5;4;8;0;20 10.4.0.1_rt=0 10.3.0.1_r=9;4;8;0;20 10.3.0.1_rt=0 10.2.0.1_r=8;4;8;0;20 10.2.0.1_rt=0 10.1.0.1_r=10;4;8;0;20 10.1.0.1_rt=0 10.0.0.1_r=11;4;8;0;20 10.0.0.1_rt=0 +Shared nets - crit: 2 warn: 0 ok: 0; | snet_crit=2 snet_warn=0 snet_ok=0 'example1_s'=21;8;16;0;40 'example1_st'=0 'example2_s'=17;8;16;0;40 'example2_st'=0 + diff --git a/tests/expected/mustach b/tests/expected/mustach new file mode 100644 index 0000000..f4df432 --- /dev/null +++ b/tests/expected/mustach @@ -0,0 +1,317 @@ +Ethernets: + macaddress: 00:00:00:00:00:01 ip: 10.0.0.1 + macaddress: 00:00:00:00:00:02 ip: 10.0.0.2 + macaddress: 00:00:00:00:00:03 ip: 10.0.0.3 + macaddress: 00:00:00:00:00:04 ip: 10.0.0.4 + macaddress: 00:00:00:00:00:05 ip: 10.0.0.5 + macaddress: 00:00:00:00:00:06 ip: 10.0.0.6 + macaddress: 00:00:00:00:00:07 ip: 10.0.0.7 + macaddress: 00:00:00:00:00:08 ip: 10.0.0.8 + macaddress: 00:00:00:00:00:09 ip: 10.0.0.9 + macaddress: 00:00:00:00:00:10 ip: 10.0.0.10 + macaddress: 00:00:00:00:00:11 ip: 10.0.0.11 + macaddress: 00:00:00:00:00:12 ip: 10.0.0.12 + macaddress: 00:00:00:00:01:00 ip: 10.1.0.0 + macaddress: 00:00:00:00:01:01 ip: 10.1.0.1 + macaddress: 00:00:00:00:01:02 ip: 10.1.0.2 + macaddress: 00:00:00:00:01:03 ip: 10.1.0.3 + macaddress: 00:00:00:00:01:04 ip: 10.1.0.4 + macaddress: 00:00:00:00:01:05 ip: 10.1.0.5 + macaddress: 00:00:00:00:01:06 ip: 10.1.0.6 + macaddress: 00:00:00:00:01:07 ip: 10.1.0.7 + macaddress: 00:00:00:00:01:08 ip: 10.1.0.8 + macaddress: 00:00:00:00:01:09 ip: 10.1.0.9 + macaddress: 00:00:00:00:01:10 ip: 10.1.0.10 + macaddress: 00:00:00:00:02:00 ip: 10.2.0.0 + macaddress: 00:00:00:00:02:01 ip: 10.2.0.1 + macaddress: 00:00:00:00:02:02 ip: 10.2.0.2 + macaddress: 00:00:00:00:02:03 ip: 10.2.0.3 + macaddress: 00:00:00:00:02:04 ip: 10.2.0.4 + macaddress: 00:00:00:00:02:05 ip: 10.2.0.5 + macaddress: 00:00:00:00:02:06 ip: 10.2.0.6 + macaddress: 00:00:00:00:02:07 ip: 10.2.0.7 + macaddress: 00:00:00:00:02:08 ip: 10.2.0.8 + macaddress: 00:00:00:00:03:00 ip: 10.3.0.0 + macaddress: 00:00:00:00:03:01 ip: 10.3.0.1 + macaddress: 00:00:00:00:03:02 ip: 10.3.0.2 + macaddress: 00:00:00:00:03:03 ip: 10.3.0.3 + macaddress: 00:00:00:00:03:04 ip: 10.3.0.4 + macaddress: 00:00:00:00:03:05 ip: 10.3.0.5 + macaddress: 00:00:00:00:03:06 ip: 10.3.0.6 + macaddress: 00:00:00:00:03:07 ip: 10.3.0.7 + macaddress: 00:00:00:00:03:08 ip: 10.3.0.8 + macaddress: 00:00:00:00:03:09 ip: 10.3.0.9 + macaddress: 00:00:00:00:04:00 ip: 10.4.0.0 + macaddress: 00:00:00:00:04:01 ip: 10.4.0.1 + macaddress: 00:00:00:00:04:02 ip: 10.4.0.2 + macaddress: 00:00:00:00:04:03 ip: 10.4.0.3 + macaddress: 00:00:00:00:04:04 ip: 10.4.0.4 + macaddress: 00:00:00:00:04:05 ip: 10.4.0.5 + macaddress: 00:00:00:00:04:06 ip: 10.4.0.6 + +Subnets: + location: example1 + range: 10.0.0.1 - 10.0.0.20 + first_ip: 10.0.0.1 + last_ip: 10.0.0.20 + used: 11 + touched: 0 + defined: 20 + free: 9 + percent: 55 + touch_count: 11 + touch_percent: 55 + backup_count: 1 + backup_percent: 5 + status: 0 + + location: example1 + range: 10.1.0.1 - 10.1.0.20 + first_ip: 10.1.0.1 + last_ip: 10.1.0.20 + used: 10 + touched: 0 + defined: 20 + free: 10 + percent: 50 + touch_count: 10 + touch_percent: 50 + backup_count: 0 + backup_percent: 0 + status: 0 + + location: example2 + range: 10.2.0.1 - 10.2.0.20 + first_ip: 10.2.0.1 + last_ip: 10.2.0.20 + used: 8 + touched: 0 + defined: 20 + free: 12 + percent: 40 + touch_count: 8 + touch_percent: 40 + backup_count: 0 + backup_percent: 0 + status: 0 + + location: example2 + range: 10.3.0.1 - 10.3.0.20 + first_ip: 10.3.0.1 + last_ip: 10.3.0.20 + used: 9 + touched: 0 + defined: 20 + free: 11 + percent: 45 + touch_count: 9 + touch_percent: 45 + backup_count: 0 + backup_percent: 0 + status: 0 + + location: All networks + range: 10.4.0.1 - 10.4.0.20 + first_ip: 10.4.0.1 + last_ip: 10.4.0.20 + used: 5 + touched: 0 + defined: 20 + free: 15 + percent: 25 + touch_count: 5 + touch_percent: 25 + backup_count: 1 + backup_percent: 5 + status: 0 + + +Shared-networks: + location: example1 + defined: 40 + used: 21 + touched: 0 + free: 19 + percent: 52.5 + touch_count: 21 + touch_percent: 52.5 + backup_count: 1 + backup_percent: 2.5 + status: 0 + + location: example2 + defined: 40 + used: 17 + touched: 0 + free: 23 + percent: 42.5 + touch_count: 17 + touch_percent: 42.5 + backup_count: 0 + backup_percent: 0 + status: 0 + + +Summary: + location: All networks + defined: 100 + used: 43 + touched: 0 + free: 57 + percent: 43 + touch_count: 43 + touch_percent: 43 + backup_count: 2 + backup_percent: 2 + status: 0 + + +number_of_ranges: 5 +number_of_shared_networks: 2 + + + + +number_of_ranges_warning: 0 +number_of_ranges_critical: 0 +number_of_shared_networks: 2 +number_of_shared_networks_warning: 0 +number_of_shared_networks_critical: 0 +--- skip ok --- +Ethernets: + macaddress: 00:00:00:00:00:01 ip: 10.0.0.1 + macaddress: 00:00:00:00:00:02 ip: 10.0.0.2 + macaddress: 00:00:00:00:00:03 ip: 10.0.0.3 + macaddress: 00:00:00:00:00:04 ip: 10.0.0.4 + macaddress: 00:00:00:00:00:05 ip: 10.0.0.5 + macaddress: 00:00:00:00:00:06 ip: 10.0.0.6 + macaddress: 00:00:00:00:00:07 ip: 10.0.0.7 + macaddress: 00:00:00:00:00:08 ip: 10.0.0.8 + macaddress: 00:00:00:00:00:09 ip: 10.0.0.9 + macaddress: 00:00:00:00:00:10 ip: 10.0.0.10 + macaddress: 00:00:00:00:00:11 ip: 10.0.0.11 + macaddress: 00:00:00:00:00:12 ip: 10.0.0.12 + macaddress: 00:00:00:00:01:00 ip: 10.1.0.0 + macaddress: 00:00:00:00:01:01 ip: 10.1.0.1 + macaddress: 00:00:00:00:01:02 ip: 10.1.0.2 + macaddress: 00:00:00:00:01:03 ip: 10.1.0.3 + macaddress: 00:00:00:00:01:04 ip: 10.1.0.4 + macaddress: 00:00:00:00:01:05 ip: 10.1.0.5 + macaddress: 00:00:00:00:01:06 ip: 10.1.0.6 + macaddress: 00:00:00:00:01:07 ip: 10.1.0.7 + macaddress: 00:00:00:00:01:08 ip: 10.1.0.8 + macaddress: 00:00:00:00:01:09 ip: 10.1.0.9 + macaddress: 00:00:00:00:01:10 ip: 10.1.0.10 + macaddress: 00:00:00:00:02:00 ip: 10.2.0.0 + macaddress: 00:00:00:00:02:01 ip: 10.2.0.1 + macaddress: 00:00:00:00:02:02 ip: 10.2.0.2 + macaddress: 00:00:00:00:02:03 ip: 10.2.0.3 + macaddress: 00:00:00:00:02:04 ip: 10.2.0.4 + macaddress: 00:00:00:00:02:05 ip: 10.2.0.5 + macaddress: 00:00:00:00:02:06 ip: 10.2.0.6 + macaddress: 00:00:00:00:02:07 ip: 10.2.0.7 + macaddress: 00:00:00:00:02:08 ip: 10.2.0.8 + macaddress: 00:00:00:00:03:00 ip: 10.3.0.0 + macaddress: 00:00:00:00:03:01 ip: 10.3.0.1 + macaddress: 00:00:00:00:03:02 ip: 10.3.0.2 + macaddress: 00:00:00:00:03:03 ip: 10.3.0.3 + macaddress: 00:00:00:00:03:04 ip: 10.3.0.4 + macaddress: 00:00:00:00:03:05 ip: 10.3.0.5 + macaddress: 00:00:00:00:03:06 ip: 10.3.0.6 + macaddress: 00:00:00:00:03:07 ip: 10.3.0.7 + macaddress: 00:00:00:00:03:08 ip: 10.3.0.8 + macaddress: 00:00:00:00:03:09 ip: 10.3.0.9 + macaddress: 00:00:00:00:04:00 ip: 10.4.0.0 + macaddress: 00:00:00:00:04:01 ip: 10.4.0.1 + macaddress: 00:00:00:00:04:02 ip: 10.4.0.2 + macaddress: 00:00:00:00:04:03 ip: 10.4.0.3 + macaddress: 00:00:00:00:04:04 ip: 10.4.0.4 + macaddress: 00:00:00:00:04:05 ip: 10.4.0.5 + macaddress: 00:00:00:00:04:06 ip: 10.4.0.6 + +Subnets: + location: example1 + range: 10.0.0.1 - 10.0.0.20 + first_ip: 10.0.0.1 + last_ip: 10.0.0.20 + used: 11 + touched: 0 + defined: 20 + free: 9 + percent: 55 + touch_count: 11 + touch_percent: 55 + backup_count: 1 + backup_percent: 5 + status: 1 + + location: example1 + range: 10.1.0.1 - 10.1.0.20 + first_ip: 10.1.0.1 + last_ip: 10.1.0.20 + used: 10 + touched: 0 + defined: 20 + free: 10 + percent: 50 + touch_count: 10 + touch_percent: 50 + backup_count: 0 + backup_percent: 0 + status: 1 + + location: example2 + range: 10.3.0.1 - 10.3.0.20 + first_ip: 10.3.0.1 + last_ip: 10.3.0.20 + used: 9 + touched: 0 + defined: 20 + free: 11 + percent: 45 + touch_count: 9 + touch_percent: 45 + backup_count: 0 + backup_percent: 0 + status: 1 + + +Shared-networks: + location: example1 + defined: 40 + used: 21 + touched: 0 + free: 19 + percent: 52.5 + touch_count: 21 + touch_percent: 52.5 + backup_count: 1 + backup_percent: 2.5 + status: 1 + + +Summary: + location: All networks + defined: 100 + used: 43 + touched: 0 + free: 57 + percent: 43 + touch_count: 43 + touch_percent: 43 + backup_count: 2 + backup_percent: 2 + status: 0 + + +number_of_ranges: 5 +number_of_shared_networks: 2 + + + + +number_of_ranges_warning: 3 +number_of_ranges_critical: 0 +number_of_shared_networks: 2 +number_of_shared_networks_warning: 1 +number_of_shared_networks_critical: 0 diff --git a/tests/expected/parser b/tests/expected/parser new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/expected/parser @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/expected/range4 b/tests/expected/range4 new file mode 100644 index 0000000..f22e11d --- /dev/null +++ b/tests/expected/range4 @@ -0,0 +1,16 @@ +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +example1 10.0.0.0 - 10.0.0.31 32 12 37.500 0 12 37.500 +example1 10.1.0.1 - 10.1.0.31 31 10 32.258 0 10 32.258 +example2 10.2.0.1 - 10.2.0.15 15 8 53.333 0 8 53.333 +example2 10.3.0.1 - 10.3.0.7 7 7 100.000 0 7 100.000 +All networks 10.4.0.1 - 10.4.0.1 1 1 100.000 0 1 100.000 + +Shared networks: +name max cur percent touch t+c t+c perc +example1 63 22 34.921 0 22 34.921 +example2 22 15 68.182 0 15 68.182 + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 86 38 44.186 0 38 44.186 diff --git a/tests/expected/range6 b/tests/expected/range6 new file mode 100644 index 0000000..fe06786 --- /dev/null +++ b/tests/expected/range6 @@ -0,0 +1,10 @@ +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +All networks dead:abba:1000:: - dead:abba:1000:ff:ffff:ffff:ffff:ffff 4.72237e+21 2 0.000 1 3 0.000 + +Shared networks: +name max cur percent touch t+c t+c perc + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 4.72237e+21 2 0.000 1 3 0.000 diff --git a/tests/expected/same-twice-json b/tests/expected/same-twice-json index 7d29ad9..f23d1b0 100644 --- a/tests/expected/same-twice-json +++ b/tests/expected/same-twice-json @@ -1,9 +1,9 @@ { "active_leases": [ - { "ip":"10.0.0.5", "macaddress":"00:00:00:00:00:00" } + { "ip":"10.0.0.5", "macaddress":"00:00:00:00:00:00", "starts":"", "ends":"", "hostname":"" } ], "subnets": [ - { "location":"All networks", "range":"10.0.0.1 - 10.0.0.10", "defined":10, "used":1, "touched":0, "free":9 } + { "location":"All networks", "range":"10.0.0.1 - 10.0.0.10", "first_ip":"10.0.0.1", "last_ip":"10.0.0.10", "defined":10, "used":1, "touched":0, "free":9, "percent":10, "touch_count":1, "touch_percent":10, "status":0 } ], "shared-networks": [ ], @@ -12,6 +12,12 @@ "defined":10, "used":1, "touched":0, - "free":9 + "free":9, + "percent":10, + "touch_count":1, + "touch_percent":10, + "status":0 + }, + "trivia": { } } diff --git a/tests/expected/same-twice-xml b/tests/expected/same-twice-xml index 93612d6..7897f7f 100644 --- a/tests/expected/same-twice-xml +++ b/tests/expected/same-twice-xml @@ -2,6 +2,9 @@ 10.0.0.5 00:00:00:00:00:00 + + + All networks diff --git a/tests/expected/shufled b/tests/expected/shufled new file mode 100644 index 0000000..ed34a85 --- /dev/null +++ b/tests/expected/shufled @@ -0,0 +1,16 @@ +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc +All networks 10.4.0.1 - 10.4.0.20 20 3 15.000 0 3 15.000 +example1 10.0.0.1 - 10.0.0.20 20 9 45.000 0 9 45.000 +example1 10.1.0.1 - 10.1.0.20 20 4 20.000 0 4 20.000 +example2 10.2.0.1 - 10.2.0.20 20 4 20.000 0 4 20.000 +example2 10.3.0.1 - 10.3.0.20 20 3 15.000 0 3 15.000 + +Shared networks: +name max cur percent touch t+c t+c perc +example1 40 13 32.500 0 13 32.500 +example2 40 7 17.500 0 7 17.500 + +Sum of all ranges: +name max cur percent touch t+c t+c perc +All networks 100 23 23.000 0 23 23.000 diff --git a/tests/expected/skip b/tests/expected/skip new file mode 100644 index 0000000..4553da8 --- /dev/null +++ b/tests/expected/skip @@ -0,0 +1,80 @@ +--- skip ok --- +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc bu bu perc +example1 10.0.0.1 - 10.0.0.20 20 11 55.000 0 11 55.000 1 5.000 +example1 10.1.0.1 - 10.1.0.20 20 10 50.000 0 10 50.000 0 0.000 +example2 10.3.0.1 - 10.3.0.20 20 9 45.000 0 9 45.000 0 0.000 + +Shared networks: +name max cur percent touch t+c t+c perc bu bu perc +example1 40 21 52.500 0 21 52.500 1 2.500 + +Sum of all ranges: +name max cur percent touch t+c t+c perc bu bu perc +All networks 100 43 43.000 0 43 43.000 2 2.000 +--- skip warning --- +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc bu bu perc +example1 10.0.0.1 - 10.0.0.20 20 11 55.000 0 11 55.000 1 5.000 +example2 10.2.0.1 - 10.2.0.20 20 8 40.000 0 8 40.000 0 0.000 +All networks 10.4.0.1 - 10.4.0.20 20 5 25.000 0 5 25.000 1 5.000 + +Shared networks: +name max cur percent touch t+c t+c perc bu bu perc +example1 40 21 52.500 0 21 52.500 1 2.500 +example2 40 17 42.500 0 17 42.500 0 0.000 + +Sum of all ranges: +name max cur percent touch t+c t+c perc bu bu perc +All networks 100 43 43.000 0 43 43.000 2 2.000 +--- skip critical ok --- +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc bu bu perc +example1 10.1.0.1 - 10.1.0.20 20 10 50.000 0 10 50.000 0 0.000 +example2 10.3.0.1 - 10.3.0.20 20 9 45.000 0 9 45.000 0 0.000 + +Shared networks: +name max cur percent touch t+c t+c perc bu bu perc + +Sum of all ranges: +name max cur percent touch t+c t+c perc bu bu perc +All networks 100 43 43.000 0 43 43.000 2 2.000 +--- skip suppressed --- +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc bu bu perc +All networks 10.4.0.1 - 10.4.0.20 20 5 25.000 0 5 25.000 1 5.000 + +Shared networks: +name max cur percent touch t+c t+c perc bu bu perc +example1 40 21 52.500 0 21 52.500 1 2.500 +example2 40 17 42.500 0 17 42.500 0 0.000 + +Sum of all ranges: +name max cur percent touch t+c t+c perc bu bu perc +All networks 100 43 43.000 0 43 43.000 2 2.000 +--- skip minsize --- +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc bu bu perc + +Shared networks: +name max cur percent touch t+c t+c perc bu bu perc +example1 40 21 52.500 0 21 52.500 1 2.500 +example2 40 17 42.500 0 17 42.500 0 0.000 + +Sum of all ranges: +name max cur percent touch t+c t+c perc bu bu perc +All networks 100 43 43.000 0 43 43.000 2 2.000 +--- skip count ok --- +Ranges: +shared net name first ip last ip max cur percent touch t+c t+c perc bu bu perc +example1 10.0.0.1 - 10.0.0.20 20 11 55.000 0 11 55.000 1 5.000 +example1 10.1.0.1 - 10.1.0.20 20 10 50.000 0 10 50.000 0 0.000 +example2 10.2.0.1 - 10.2.0.20 20 8 40.000 0 8 40.000 0 0.000 +example2 10.3.0.1 - 10.3.0.20 20 9 45.000 0 9 45.000 0 0.000 + +Shared networks: +name max cur percent touch t+c t+c perc bu bu perc + +Sum of all ranges: +name max cur percent touch t+c t+c perc bu bu perc +All networks 100 43 43.000 0 43 43.000 2 2.000 diff --git a/tests/expected/sorts b/tests/expected/sorts index c6a6f21..b5f1dc7 100644 --- a/tests/expected/sorts +++ b/tests/expected/sorts @@ -34,7 +34,7 @@ Sum of all ranges: name max cur percent touch t+c t+c perc All networks 100 43 43.000 0 43 43.000 0 -== maxium == +== maximum == Ranges: shared net name first ip last ip max cur percent touch t+c t+c perc All networks 10.4.0.1 - 10.4.0.20 20 5 25.000 0 5 25.000 diff --git a/tests/expected/statuses b/tests/expected/statuses new file mode 100644 index 0000000..908b5a2 --- /dev/null +++ b/tests/expected/statuses @@ -0,0 +1,29 @@ +{ + "subnets": [ + { "location":"example1", "range":"10.0.0.1 - 10.0.0.20", "first_ip":"10.0.0.1", "last_ip":"10.0.0.20", "defined":20, "used":11, "touched":0, "free":9, "percent":55, "touch_count":11, "touch_percent":55, "status":4 }, + { "location":"example1", "range":"10.1.0.1 - 10.1.0.20", "first_ip":"10.1.0.1", "last_ip":"10.1.0.20", "defined":20, "used":10, "touched":0, "free":10, "percent":50, "touch_count":10, "touch_percent":50, "status":4 }, + { "location":"example2", "range":"10.2.0.1 - 10.2.0.20", "first_ip":"10.2.0.1", "last_ip":"10.2.0.20", "defined":20, "used":8, "touched":0, "free":12, "percent":40, "touch_count":8, "touch_percent":40, "status":4 }, + { "location":"example2", "range":"10.3.0.1 - 10.3.0.20", "first_ip":"10.3.0.1", "last_ip":"10.3.0.20", "defined":20, "used":9, "touched":0, "free":11, "percent":45, "touch_count":9, "touch_percent":45, "status":4 }, + { "location":"10.4.0.0/24", "range":"10.4.0.1 - 10.4.0.20", "first_ip":"10.4.0.1", "last_ip":"10.4.0.20", "defined":20, "used":5, "touched":0, "free":15, "percent":25, "touch_count":5, "touch_percent":25, "status":4 } + ], + "shared-networks": [ + { "location":"example1", "defined":40, "used":21, "touched":0, "free":19, "percent":52.5, "touch_count":21, "touch_percent":52.5, "status":2 }, + { "location":"example2", "defined":40, "used":17, "touched":0, "free":23, "percent":42.5, "touch_count":17, "touch_percent":42.5, "status":2 }, + { "location":"10.4.0.0/24", "defined":20, "used":5, "touched":0, "free":15, "percent":25, "touch_count":5, "touch_percent":25, "status":1 } + ] +} +== minsize +{ + "subnets": [ + { "location":"example1", "range":"10.0.0.1 - 10.0.0.20", "first_ip":"10.0.0.1", "last_ip":"10.0.0.20", "defined":20, "used":11, "touched":0, "free":9, "percent":55, "touch_count":11, "touch_percent":55, "status":3 }, + { "location":"example1", "range":"10.1.0.1 - 10.1.0.20", "first_ip":"10.1.0.1", "last_ip":"10.1.0.20", "defined":20, "used":10, "touched":0, "free":10, "percent":50, "touch_count":10, "touch_percent":50, "status":3 }, + { "location":"example2", "range":"10.2.0.1 - 10.2.0.20", "first_ip":"10.2.0.1", "last_ip":"10.2.0.20", "defined":20, "used":8, "touched":0, "free":12, "percent":40, "touch_count":8, "touch_percent":40, "status":3 }, + { "location":"example2", "range":"10.3.0.1 - 10.3.0.20", "first_ip":"10.3.0.1", "last_ip":"10.3.0.20", "defined":20, "used":9, "touched":0, "free":11, "percent":45, "touch_count":9, "touch_percent":45, "status":3 }, + { "location":"10.4.0.0/24", "range":"10.4.0.1 - 10.4.0.20", "first_ip":"10.4.0.1", "last_ip":"10.4.0.20", "defined":20, "used":5, "touched":0, "free":15, "percent":25, "touch_count":5, "touch_percent":25, "status":3 } + ], + "shared-networks": [ + { "location":"example1", "defined":40, "used":21, "touched":0, "free":19, "percent":52.5, "touch_count":21, "touch_percent":52.5, "status":2 }, + { "location":"example2", "defined":40, "used":17, "touched":0, "free":23, "percent":42.5, "touch_count":17, "touch_percent":42.5, "status":2 }, + { "location":"10.4.0.0/24", "defined":20, "used":5, "touched":0, "free":15, "percent":25, "touch_count":5, "touch_percent":25, "status":3 } + ] +} diff --git a/tests/expected/v6-perfdata b/tests/expected/v6-perfdata new file mode 100644 index 0000000..50ea786 --- /dev/null +++ b/tests/expected/v6-perfdata @@ -0,0 +1,3 @@ +OK: Ranges - crit: 0 warn: 0 ok: 2; | range_crit=0 range_warn=0 range_ok=2 dead:abba:4000::2_r=1;203.2;228.6;0;254 dead:abba:4000::2_rt=0 dead:abba:1000::2_r=2;3.77789e+21;4.25013e+21;0;4.72237e+21 dead:abba:1000::2_rt=1 +Shared nets - crit: 0 warn: 0 ok: 0; | snet_crit=0 snet_warn=0 snet_ok=0 + diff --git a/tests/formats b/tests/formats new file mode 100755 index 0000000..254a37b --- /dev/null +++ b/tests/formats @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Minimal regression test suite. + +IAM=$(basename $0) + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +echo '=== color text' > tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/$IAM --color=always \ + -l $top_srcdir/tests/leases/$IAM >> tests/outputs/$IAM + +echo '=== html' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/$IAM --color=always --format=H \ + -l $top_srcdir/tests/leases/$IAM | + sed '/was last modified at/d; /Generated using/d' >> tests/outputs/$IAM + +echo '=== xml' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/$IAM -fx \ + -l $top_srcdir/tests/leases/$IAM >> tests/outputs/$IAM + +echo '=== csv' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/$IAM -fc \ + -l $top_srcdir/tests/leases/$IAM >> tests/outputs/$IAM + +echo '=== json' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/$IAM -fj --limit=33 \ + -l $top_srcdir/tests/leases/$IAM >> tests/outputs/$IAM + +echo '=== perfdata' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/$IAM --perfdata --warning=20 --critical=40 \ + -l $top_srcdir/tests/leases/$IAM >> tests/outputs/$IAM + +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/tests/full-json b/tests/full-json index 31b0c93..881b02d 100755 --- a/tests/full-json +++ b/tests/full-json @@ -7,7 +7,8 @@ if [ ! -d tests/outputs ]; then fi dhcpd-pools -f J -c $top_srcdir/tests/confs/same-twice \ - -l $top_srcdir/tests/leases/same-twice \ - -o tests/outputs/same-twice-json + -l $top_srcdir/tests/leases/same-twice | + sed '/"version":"/d; /"conf_file_.*":/d; /"lease_file_.*":/d' \ + >| tests/outputs/same-twice-json diff -u $top_srcdir/tests/expected/same-twice-json tests/outputs/same-twice-json exit $? diff --git a/tests/leases/big-small b/tests/leases/big-small new file mode 120000 index 0000000..8fd3246 --- /dev/null +++ b/tests/leases/big-small @@ -0,0 +1 @@ +simple \ No newline at end of file diff --git a/tests/leases/binding-states b/tests/leases/binding-states new file mode 100644 index 0000000..0d9929f --- /dev/null +++ b/tests/leases/binding-states @@ -0,0 +1,241 @@ +lease 10.0.0.0 { + binding state active; + hardware ethernet 00:00:00:00:00:00; +} + +lease 10.0.0.1 { + binding state free; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.0.0.1 { + binding state active; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.0.0.1 { + binding state abandoned; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.0.0.1 { + binding state expired; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.0.0.1 { + binding state released; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.0.0.1 { + binding state backup; + hardware ethernet 00:00:00:00:00:01; +} + +lease 10.0.0.2 { + binding state free; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.0.0.2 { + binding state abandoned; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.0.0.2 { + binding state expired; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.0.0.2 { + binding state released; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.0.0.2 { + binding state backup; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.0.0.2 { + binding state active; + hardware ethernet 00:00:00:00:00:02; +} + +lease 10.0.0.3 { + binding state free; + hardware ethernet 00:00:00:00:00:03; +} +lease 10.0.0.4 { + binding state active; + hardware ethernet 00:00:00:00:00:04; +} +lease 10.0.0.5 { + binding state expired; + hardware ethernet 00:00:00:00:00:05; +} +lease 10.0.0.6 { + binding state released; + hardware ethernet 00:00:00:00:00:06; +} +lease 10.0.0.7 { + binding state backup; + hardware ethernet 00:00:00:00:00:07; +} +lease 10.0.0.8 { + binding state none-existing-state; + hardware ethernet 00:00:00:00:00:08; +} +lease 10.0.0.9 { + binding state active; + hardware ethernet 00:00:00:00:00:09; +} +lease 10.0.0.20 { + binding state active; + hardware ethernet 00:00:00:00:00:10; +} +lease 10.0.0.21 { + binding state active; + hardware ethernet 00:00:00:00:00:11; +} + + +lease 10.1.0.0 { + binding state active; + hardware ethernet 00:00:00:00:00:00; +} +lease 10.1.0.1 { + binding state active; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.1.0.2 { + binding state active; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.1.0.3 { + binding state active; + hardware ethernet 00:00:00:00:00:03; +} +lease 10.1.0.4 { + binding state active; + hardware ethernet 00:00:00:00:00:04; +} +lease 10.1.0.5 { + binding state active; + hardware ethernet 00:00:00:00:00:05; +} +lease 10.1.0.6 { + binding state active; + hardware ethernet 00:00:00:00:00:06; +} +lease 10.1.0.7 { + binding state active; + hardware ethernet 00:00:00:00:00:07; +} +lease 10.1.0.8 { + binding state active; + hardware ethernet 00:00:00:00:00:08; +} +lease 10.1.0.9 { + binding state active; + hardware ethernet 00:00:00:00:00:09; +} +lease 10.1.0.10 { + binding state active; + hardware ethernet 00:00:00:00:00:10; +} + + +lease 10.2.0.0 { + binding state active; + hardware ethernet 00:00:00:00:00:00; +} +lease 10.2.0.1 { + binding state active; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.2.0.2 { + binding state active; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.2.0.3 { + binding state active; + hardware ethernet 00:00:00:00:00:03; +} +lease 10.2.0.4 { + binding state active; + hardware ethernet 00:00:00:00:00:04; +} +lease 10.2.0.5 { + binding state active; + hardware ethernet 00:00:00:00:00:05; +} +lease 10.2.0.6 { + binding state active; + hardware ethernet 00:00:00:00:00:06; +} +lease 10.2.0.7 { + binding state active; + hardware ethernet 00:00:00:00:00:07; +} +lease 10.2.0.8 { + binding state active; + hardware ethernet 00:00:00:00:00:08; +} + +lease 10.3.0.0 { + binding state active; + hardware ethernet 00:00:00:00:00:00; +} +lease 10.3.0.1 { + binding state active; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.3.0.2 { + binding state active; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.3.0.3 { + binding state active; + hardware ethernet 00:00:00:00:00:03; +} +lease 10.3.0.4 { + binding state active; + hardware ethernet 00:00:00:00:00:04; +} +lease 10.3.0.5 { + binding state active; + hardware ethernet 00:00:00:00:00:05; +} +lease 10.3.0.6 { + binding state active; + hardware ethernet 00:00:00:00:00:06; +} +lease 10.3.0.7 { + binding state active; + hardware ethernet 00:00:00:00:00:07; +} +lease 10.3.0.8 { + binding state active; + hardware ethernet 00:00:00:00:00:08; +} +lease 10.3.0.9 { + binding state active; + hardware ethernet 00:00:00:00:00:09; +} + +lease 10.4.0.0 { + binding state active; + hardware ethernet 00:00:00:00:00:00; +} +lease 10.4.0.1 { + binding state active; + hardware ethernet 00:00:00:00:00:01; +} +lease 10.4.0.2 { + binding state active; + hardware ethernet 00:00:00:00:00:02; +} +lease 10.4.0.3 { + binding state active; + hardware ethernet 00:00:00:00:00:03; +} +lease 10.4.0.4 { + binding state active; + hardware ethernet 00:00:00:00:00:04; +} +lease 10.4.0.5 { + binding state active; + hardware ethernet 00:00:00:00:00:05; +} diff --git a/tests/leases/cidr-v4 b/tests/leases/cidr-v4 new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/leases/cidr-v4 @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/leases/cidr-v6 b/tests/leases/cidr-v6 new file mode 120000 index 0000000..82e3978 --- /dev/null +++ b/tests/leases/cidr-v6 @@ -0,0 +1 @@ +v6 \ No newline at end of file diff --git a/tests/leases/formats b/tests/leases/formats new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/leases/formats @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/leases/parser b/tests/leases/parser new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/leases/parser @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/leases/range4 b/tests/leases/range4 new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/leases/range4 @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/leases/range6 b/tests/leases/range6 new file mode 120000 index 0000000..82e3978 --- /dev/null +++ b/tests/leases/range6 @@ -0,0 +1 @@ +v6 \ No newline at end of file diff --git a/tests/leases/shufled b/tests/leases/shufled new file mode 100644 index 0000000..723c6ec --- /dev/null +++ b/tests/leases/shufled @@ -0,0 +1,198 @@ +lease 10.0.0.7 { +} + binding state active; + hardware ethernet 00:00:00:00:00:07; + hardware ethernet 00:00:00:00:00:10; +lease 10.2.0.4 { +lease 10.2.0.6 { +lease 10.0.0.11 { + binding state active; +} + hardware ethernet 00:00:00:00:00:03; +lease 10.3.0.9 { +} + hardware ethernet 00:00:00:00:00:04; +} + binding state active; +lease 10.1.0.7 { +} +} +lease 10.1.0.9 { + + hardware ethernet 00:00:00:00:00:07; + hardware ethernet 00:00:00:00:00:01; + binding state active; + binding state active; +} +} +} + binding state active; +lease 10.4.0.1 { +} +lease 10.0.0.0 { +} + hardware ethernet 00:00:00:00:00:10; +} +} + binding state active; + hardware ethernet 00:00:00:00:00:00; +} +} + binding state active; +} +lease 10.3.0.2 { + hardware ethernet 00:00:00:00:00:01; + binding state active; + hardware ethernet 00:00:00:00:00:02; + binding state active; +lease 10.1.0.3 { + binding state active; +} + binding state active; +} +lease 10.1.0.1 { +lease 10.3.0.8 { +lease 10.2.0.1 { + hardware ethernet 00:00:00:00:00:07; +} +} +lease 10.4.0.0 { + hardware ethernet 00:00:00:00:00:01; +} + binding state active; + hardware ethernet 00:00:00:00:00:02; +lease 10.0.0.10 { + binding state active; +} + hardware ethernet 00:00:00:00:00:04; + hardware ethernet 00:00:00:00:00:11; + hardware ethernet 00:00:00:00:00:08; + binding state active; + hardware ethernet 00:00:00:00:00:08; + binding state active; +lease 10.2.0.5 { + hardware ethernet 00:00:00:00:00:01; +lease 10.1.0.8 { + +lease 10.0.0.3 { + binding state active; + binding state active; +lease 10.0.0.1 { +lease 10.4.0.5 { + hardware ethernet 00:00:00:00:00:06; + + hardware ethernet 00:00:00:00:00:06; + hardware ethernet 00:00:00:00:00:02; +lease 10.2.0.3 { + binding state active; +lease 10.1.0.10 { + binding state active; +} +} + binding state active; + hardware ethernet 00:00:00:00:00:03; +} +} + hardware ethernet 00:00:00:00:00:05; +lease 10.0.0.2 { +} + binding state active; +} + hardware ethernet 00:00:00:00:00:06; +lease 10.3.0.6 { +lease 10.0.0.8 { + hardware ethernet 00:00:00:00:00:01; +lease 10.4.0.3 { +} +} +} + binding state active; + binding state active; +lease 10.1.0.6 { +lease 10.1.0.2 { + hardware ethernet 00:00:00:00:00:05; + binding state active; +} +} +lease 10.2.0.7 { +} +} + hardware ethernet 00:00:00:00:00:03; + binding state active; + +} + binding state active; + hardware ethernet 00:00:00:00:00:03; +} +lease 10.1.0.0 { + hardware ethernet 00:00:00:00:00:04; + binding state active; +} +lease 10.3.0.1 { +lease 10.2.0.2 { + binding state active; + hardware ethernet 00:00:00:00:00:09; +} + binding state active; + hardware ethernet 00:00:00:00:00:00; + binding state active; +lease 10.0.0.6 { + hardware ethernet 00:00:00:00:00:02; + binding state active; +} + binding state active; +} +lease 10.3.0.0 { +lease 10.0.0.9 { + binding state active; +lease 10.0.0.4 { + hardware ethernet 00:00:00:00:00:08; + binding state active; + hardware ethernet 00:00:00:00:00:07; + hardware ethernet 00:00:00:00:00:00; + binding state active; +lease 10.1.0.4 { +lease 10.4.0.2 { + binding state active; +} +} +lease 10.3.0.4 { + hardware ethernet 00:00:00:00:00:05; + hardware ethernet 00:00:00:00:00:05; + binding state active; + binding state active; +lease 10.0.0.5 { + hardware ethernet 00:00:00:00:00:04; + binding state active; + hardware ethernet 00:00:00:00:00:02; + binding state active; + hardware ethernet 00:00:00:00:00:09; + hardware ethernet 00:00:00:00:00:08; +lease 10.2.0.8 { +} + binding state active; + binding state active; + hardware ethernet 00:00:00:00:00:06; + hardware ethernet 00:00:00:00:00:00; +lease 10.3.0.7 { +lease 10.4.0.4 { +} + hardware ethernet 00:00:00:00:00:04; +} + binding state active; +lease 10.3.0.3 { + hardware ethernet 00:00:00:00:00:03; +lease 10.2.0.0 { + binding state active; + binding state active; + hardware ethernet 00:00:00:00:00:05; + + binding state active; +} + hardware ethernet 00:00:00:00:00:00; + binding state active; +lease 10.1.0.5 { + hardware ethernet 00:00:00:00:00:09; +lease 10.3.0.5 { +} + diff --git a/tests/leases/statuses b/tests/leases/statuses new file mode 120000 index 0000000..ceb7cbb --- /dev/null +++ b/tests/leases/statuses @@ -0,0 +1 @@ +complete \ No newline at end of file diff --git a/tests/mustach b/tests/mustach new file mode 100755 index 0000000..d893aae --- /dev/null +++ b/tests/mustach @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Minimal regression test suite. + +IAM=$(basename $0) + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +remove='/gettimeofday:/d; /^localtime:/d; /^conf_file_/d; /lease_file_/d; /^template_file_/d; /^version:/d' + +dhcpd-pools -c $top_srcdir/samples/dhcpd.conf -l $top_srcdir/samples/dhcpd.leases \ + --mustach $top_srcdir/samples/mustach.template | + sed "$remove" >| tests/outputs/$IAM + +echo "--- skip ok ---" >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/samples/dhcpd.conf -l $top_srcdir/samples/dhcpd.leases \ + --mustach $top_srcdir/samples/mustach.template --skip=ok --warning 44 | + sed "$remove" >> tests/outputs/$IAM + +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/tests/parser b/tests/parser new file mode 100755 index 0000000..94b4e7d --- /dev/null +++ b/tests/parser @@ -0,0 +1,30 @@ +#!/bin/sh +# +# Minimal regression test suite. + +IAM=$(basename $0) + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi +cat <"./${IAM}-conf" +#shared-network "example3 " { +# subnet 10.0.6.0 netmask 255.255.255.0 { +# pool { +# range 10.0.6.1 10.0.6.20; +# } +# } +# subnet 10.1.7.0 netmask 255.255.255.0 { +# pool { +# range 10.1.7.1 10.1.7.20; +# } +# } +#} + +include "tests/confs/complete"; +EOF +dhcpd-pools -c "./${IAM}-conf" --color=never \ + -l $top_srcdir/tests/leases/$IAM -o tests/outputs/$IAM +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +rm -f "./${IAM}-conf" +exit $? diff --git a/tests/range4 b/tests/range4 new file mode 120000 index 0000000..61a58b0 --- /dev/null +++ b/tests/range4 @@ -0,0 +1 @@ +test.sh \ No newline at end of file diff --git a/tests/range6 b/tests/range6 new file mode 120000 index 0000000..61a58b0 --- /dev/null +++ b/tests/range6 @@ -0,0 +1 @@ +test.sh \ No newline at end of file diff --git a/tests/shufled b/tests/shufled new file mode 100755 index 0000000..4f38984 --- /dev/null +++ b/tests/shufled @@ -0,0 +1,15 @@ +#!/bin/sh +# +# Minimal regression test suite. + +IAM=$(basename $0) + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +dhcpd-pools -c $top_srcdir/tests/confs/$IAM --color=never \ + --sort nimcptTenimcptTe --sort nimcptTe \ + -l $top_srcdir/tests/leases/$IAM -o tests/outputs/$IAM +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/tests/skip b/tests/skip new file mode 100755 index 0000000..7578cf9 --- /dev/null +++ b/tests/skip @@ -0,0 +1,41 @@ +#!/bin/sh +# +# --skip option tests + +IAM=$(basename $0) + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +echo "--- skip ok ---" >| tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/samples/dhcpd.conf -l $top_srcdir/samples/dhcpd.leases \ + --color=always -ft --skip=ok --warning 44 >> tests/outputs/$IAM + +echo "--- skip warning ---" >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/samples/dhcpd.conf -l $top_srcdir/samples/dhcpd.leases \ + --color=always --format text --skip=warning --warning 44 --critical=50 \ + >> tests/outputs/$IAM + +echo "--- skip critical ok ---" >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/samples/dhcpd.conf -l $top_srcdir/samples/dhcpd.leases \ + --color=always --format text --skip=ok,critical --warning 44 --critical=50 \ + >> tests/outputs/$IAM + +echo "--- skip suppressed ---" >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/samples/dhcpd.conf -l $top_srcdir/samples/dhcpd.leases \ + --color=always --format text --critical 1 --snet-alarms --skip=suppressed \ + >> tests/outputs/$IAM + +echo "--- skip minsize ---" >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/samples/dhcpd.conf -l $top_srcdir/samples/dhcpd.leases \ + --color=always --format text --critical=1 --minsize 20 --skip=minsize \ + >> tests/outputs/$IAM + +echo "--- skip count ok ---" >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/samples/dhcpd.conf -l $top_srcdir/samples/dhcpd.leases \ + --color=always --format=t --skip=ok --critical=1 --crit-count=12 --warning=1 \ + --warn-count=15 >> tests/outputs/$IAM + +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/tests/sorts b/tests/sorts index 74185cb..bc6b9be 100755 --- a/tests/sorts +++ b/tests/sorts @@ -20,7 +20,7 @@ dhcpd-pools --config $top_srcdir/tests/confs/complete --leases $top_srcdir/tests echo $? >> tests/outputs/$IAM-too cat tests/outputs/$IAM-too >> tests/outputs/$IAM -echo '== maxium ==' >> tests/outputs/$IAM +echo '== maximum ==' >> tests/outputs/$IAM dhcpd-pools --config $top_srcdir/tests/confs/complete --leases $top_srcdir/tests/leases/complete \ --sort=m --output=tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too diff --git a/tests/statuses b/tests/statuses new file mode 100755 index 0000000..e603b8a --- /dev/null +++ b/tests/statuses @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Test json status field values. + +IAM=$(basename $0) + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +dhcpd-pools -c $top_srcdir/tests/confs/$IAM --format=json -L33 \ + -l $top_srcdir/tests/leases/$IAM -o tests/outputs/$IAM \ + --warning=20 --critical=41 --all-as-shared --snet-alarms --all-as-shared + +echo '== minsize' >> tests/outputs/$IAM +dhcpd-pools -c $top_srcdir/tests/confs/$IAM --format=json --limit 33 \ + -l $top_srcdir/tests/leases/$IAM --minsize=20 \ + --warning=20 --critical=41 --all-as-shared --snet-alarms --all-as-shared \ + >> tests/outputs/$IAM + +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/tests/test.sh b/tests/test.sh index 6694344..a5e98d0 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -8,7 +8,7 @@ if [ ! -d tests/outputs ]; then mkdir tests/outputs fi -dhcpd-pools -c $top_srcdir/tests/confs/$IAM \ +dhcpd-pools -c $top_srcdir/tests/confs/$IAM --color=never \ -l $top_srcdir/tests/leases/$IAM -o tests/outputs/$IAM diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM exit $? diff --git a/tests/v6-perfdata b/tests/v6-perfdata new file mode 100755 index 0000000..2765fd4 --- /dev/null +++ b/tests/v6-perfdata @@ -0,0 +1,16 @@ +#!/bin/sh +# +# Minimal regression test suite. + +IAM=$(basename $0) +TESTDATA=${IAM%-*} + +if [ ! -d tests/outputs ]; then + mkdir tests/outputs +fi + +dhcpd-pools -c $top_srcdir/tests/confs/$TESTDATA \ + -l $top_srcdir/tests/leases/$TESTDATA -o tests/outputs/$IAM \ + --warning 80 --perfdata +diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM +exit $? diff --git a/contrib/dhcpd-pools.awk b/webpages/dhcpd-pools.awk similarity index 96% rename from contrib/dhcpd-pools.awk rename to webpages/dhcpd-pools.awk index 38d5f18..9c1f299 100644 --- a/contrib/dhcpd-pools.awk +++ b/webpages/dhcpd-pools.awk @@ -4,12 +4,11 @@ # analysis algorithm is sane. This script is no longer # maintained, and can be considered as historic reference. # -# Licensed under the Open Software License version 1.1 -# http://opensource.org/licenses/osl.php +# Licensed under the Open Software License version 1.0 +# https://opensource.org/license/osl-1-0/ # # Sami Kerola # -# Latest version is available from http://www.iki.fi/kerolasa/dhcp/ # This is version 1.4 BEGIN { diff --git a/webpages/google3df4cc29123f74e0.html b/webpages/google3df4cc29123f74e0.html new file mode 100644 index 0000000..806122e --- /dev/null +++ b/webpages/google3df4cc29123f74e0.html @@ -0,0 +1 @@ +google-site-verification: google3df4cc29123f74e0.html \ No newline at end of file diff --git a/webpages/index.html b/webpages/index.html new file mode 100644 index 0000000..7d5b748 --- /dev/null +++ b/webpages/index.html @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + +ISC dhcpd lease analysis and reporting + + + +
+

ISC dhcpd lease analysis and reporting

+

This is dhcpd-pools +ISC dhcp +shared network and pool range +usage analysis. +Purpose of command is to count usage ratio of +each IP range and shared network pool which ISC dhcpd is in control +of. Users of the command are most likely ISPs and other organizations +that have large IP space.

+

Program is written C. Design goal is to get analysis done quickly where +there is lots of data. On cheap laptop the speed of analysis is more than +100k leases per second. Number of ranges, or shared networks, does not make +any significant difference in getting analysis done.

+
+
+prompt> ./dhcpd-pools --config=/etc/dhcpd.conf +--leases=/var/lib/dhcp/dhcpd.leases --format=H +--color=always --warning=40 --critical=50 --output=sampleoutput.html +
+
+

Is dhcpd-pools still developed?

+

In short no, it is not.

+

+ISC DHCPD has reached +EOL. Users are Kea DHCP server instead. Kea has native +statistics, so it does not require this tool and therefore the +dhcpd-pools is obsolte. +

+

Where I can get dhcpd-pools?

+

Download +release package +or clone from git. Build instructions are in +README +file. +

+
git clone git://git.code.sf.net/p/dhcpd-pools/code dhcpd-pools
+

See also +SourceForge project page.

+

Documentation

+

The dhcpd-pools +manual page, and +Doxygen software +reference documentation are available online.

+

Getting the output you need

+
    +
  1. Start with:
    +dhcpd-pools --config=path +--leases=path --color=always --format=text
  2. +
  3. Play with options and see how output changes. Options to +try:
    +--all-as-shared --crit-count +--critical --minsize --snet-alarms --warn-count +--warning
  4. +
  5. Start filtering output. Options to try:
    +--skip --limit
  6. +
  7. Change --format +option to what you need, and maybe include +--perfdata if you want nagios alarm +format.
  8. +
  9. Done.
  10. +
+

Reference information

+

+ +

+

The program was written because +DHCPStatus, +DHCP Usage Statistics, +reportdhcp.pl, +lease_analyzer and +dhcpd-snmp +where too slow to handle huge number of leases. There is also difference in +printed details. The dhcpd-pools does not print quite as much information as +some other tools.

+

Notice that this utility is not the same as +dhcpd-pool +that is maintained by Trond Hasle Amundsen.

+

Just for laugh, obsolete awk proof of concept (version 1.4) +dhcpd-pools.awk

+

Where to send questions, bug reports, code contributions...

+

Sami Kerola +<kerolasa@iki.fi>

+
+ +Sat Nov 26 09:58:28 GMT 2022 +3.0 +FreeBSD License + +
+0 + +
+
+5 ( +2 ratings ) +
+
+
+ + diff --git a/webpages/robots.txt b/webpages/robots.txt new file mode 100644 index 0000000..c9eedb4 --- /dev/null +++ b/webpages/robots.txt @@ -0,0 +1,3 @@ +Sitemap: https://dhcpd-pools.sourceforge.net/sitemap.txt +User-agent: * +Disallow: diff --git a/webpages/sampleoutput.html b/webpages/sampleoutput.html new file mode 100644 index 0000000..d27d3b6 --- /dev/null +++ b/webpages/sampleoutput.html @@ -0,0 +1,169 @@ + + + +ISC dhcpd dhcpd-pools output + + + + + + + + +
+

ISC DHCPD status

+File tests/leases/formats was last modified at 2025-04-15T14:32:48+0100
+

Sum of all

+ + + + + + + + + + + + + + + + + + + + + + + + + +
namemaxcurfreepercenttoucht+ct+c perc
All networks100435743.00004343.000
+

Shared networks

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
namemaxcurfreepercenttoucht+ct+c perc
example140211952.502152.500
example240172342.501742.500
+

Ranges

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
shared net namefirst iplast ipmaxcurfreepercenttoucht+ct+c perc
example110.0.0.110.0.0.20201195501155.000
example110.1.0.110.1.0.202010105001050.000
example210.2.0.110.2.0.2020812400840.000
example210.3.0.110.3.0.2020911450945.000
All networks10.4.0.110.4.0.2020515250525.000
+
+Generated using dhcpd-pools 3.3
+More info at https://dhcpd-pools.sourceforge.net/ +
+ + + + + diff --git a/webpages/sitemap.txt b/webpages/sitemap.txt new file mode 100644 index 0000000..28f6306 --- /dev/null +++ b/webpages/sitemap.txt @@ -0,0 +1,546 @@ +https://dhcpd-pools.sourceforge.net/ +https://dhcpd-pools.sourceforge.net/dhcpd-pools.awk +https://dhcpd-pools.sourceforge.net/man.html +https://dhcpd-pools.sourceforge.net/doxygen/alloca_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/alloca_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/alloca_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/alloca_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/analyze_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/annotated.html +https://dhcpd-pools.sourceforge.net/doxygen/arg-nonnull_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/arg-nonnull_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/arpa__inet_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/arpa__inet_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/basename-lgpl_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/c_09_09defs_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/c_09_09defs_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/c-ctype_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/c-ctype_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/c-ctype_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/classes.html +https://dhcpd-pools.sourceforge.net/doxygen/close_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/closeout_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/closeout_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/closeout_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/close-stream_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/close-stream_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/close-stream_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/config_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/config_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/configmake_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/configmake_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/c-strcase_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/c-strcase_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/c-strcasecmp_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/c-strcaseeq_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/c-strcaseeq_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/c-strncasecmp_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/dhcpd-pools_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/dhcpd-pools_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/dhcpd-pools_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/dir_000000_000001.html +https://dhcpd-pools.sourceforge.net/doxygen/dir_000001_000002.html +https://dhcpd-pools.sourceforge.net/doxygen/dir_1dacc5f4fcb865f1d9e042339dad3519.html +https://dhcpd-pools.sourceforge.net/doxygen/dir_97aefd0d527b934f1d99a682da8fe6a9.html +https://dhcpd-pools.sourceforge.net/doxygen/dir_fb441f6c77667ce4c9ebd4a5c47eafac.html +https://dhcpd-pools.sourceforge.net/doxygen/dirname_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/dirname_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/dirname-lgpl_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/dosname_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/dosname_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/doxygen.css +https://dhcpd-pools.sourceforge.net/doxygen/errno_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/errno_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/error_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/error_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/error_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/exitfail_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/exitfail_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/exitfail_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/fclose_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/fcntl_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/fcntl_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/fcntl_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/fcntl_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/fd-hook_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/fd-hook_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/fd-hook_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/fdopen_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/fflush_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/filename_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/filename_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/files.html +https://dhcpd-pools.sourceforge.net/doxygen/flexmember_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/flexmember_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/float_09_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/float_09_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/float_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/float_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/float_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/fopen_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/fpending_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/fpending_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/fpending_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/fpurge_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/freading_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/freading_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/freading_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/fseek_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/fseeko_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/fstat_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/ftell_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/ftello_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/functions_func.html +https://dhcpd-pools.sourceforge.net/doxygen/functions.html +https://dhcpd-pools.sourceforge.net/doxygen/functions_vars.html +https://dhcpd-pools.sourceforge.net/doxygen/getdata_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt1_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-cdefs_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-cdefs_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-core_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-core_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-ext_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-ext_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt__int_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt__int_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-pfx-core_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-pfx-core_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-pfx-ext_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/getopt-pfx-ext_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/getprogname_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/getprogname_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/getprogname_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/gettext_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/gettext_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_a.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_b.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_c.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_a.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_b.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_c.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_d.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_e.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_f.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_g.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_h.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_i.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_k.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_l.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_m.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_n.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_o.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_p.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_r.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_s.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_t.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_u.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_v.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_w.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_x.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_defs_y.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_d.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_e.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_enum.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_eval.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_f.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_a.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_b.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_c.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_d.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_e.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_f.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_g.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_h.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_i.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_l.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_m.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_n.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_o.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_p.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_q.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_r.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_s.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_t.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_u.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_func_x.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_g.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_h.html +https://dhcpd-pools.sourceforge.net/doxygen/globals.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_i.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_k.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_l.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_m.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_n.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_o.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_p.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_q.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_r.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_s.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_t.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_type.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_u.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_vars.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_v.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_w.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_x.html +https://dhcpd-pools.sourceforge.net/doxygen/globals_y.html +https://dhcpd-pools.sourceforge.net/doxygen/graph_legend.html +https://dhcpd-pools.sourceforge.net/doxygen/hard-locale_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/hard-locale_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/hard-locale_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/hash_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/index.html +https://dhcpd-pools.sourceforge.net/doxygen/inet_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/inet_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/inet__pton_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/intprops_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/intprops_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/introduction_8dox.html +https://dhcpd-pools.sourceforge.net/doxygen/isnan_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/isnand_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/isnanf_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/isnanl_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/itold_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/limits_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/limits_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/limits_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/limits_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/localcharset_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/localcharset_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/localcharset_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/localtime-buffer_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/localtime-buffer_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/localtime-buffer_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/lseek_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/malloc_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/malloca_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/malloca_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/malloca_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/math_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/math_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/math_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/math_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/math_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/mbrtowc_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/mbsinit_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/memchr_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/minmax_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/minmax_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/mktime_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/mktime-internal_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/mktime-internal_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/msvc-inval_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/msvc-inval_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/msvc-inval_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/msvc-nothrow_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/msvc-nothrow_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/msvc-nothrow_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/mustach_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/mustach_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/mustach_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/mustach-dhcpd-pools_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/netinet__in_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/netinet__in_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/__Noreturn_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/__Noreturn_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/nstrftime_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/other_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/output_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/pathmax_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/pathmax_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/progname_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/progname_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/progname_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/quote_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/quote_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/quotearg_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/quotearg_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/quotearg_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/realloc_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/sampleoutput.html +https://dhcpd-pools.sourceforge.net/doxygen/search +https://dhcpd-pools.sourceforge.net/doxygen/search/all_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_10.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_11.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_12.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_13.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_14.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_15.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_16.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_17.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_18.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_7.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_8.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_9.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_a.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_b.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_c.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_d.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_e.html +https://dhcpd-pools.sourceforge.net/doxygen/search/all_f.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_7.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_8.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_9.html +https://dhcpd-pools.sourceforge.net/doxygen/search/classes_a.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_10.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_11.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_12.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_13.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_14.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_15.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_16.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_17.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_7.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_8.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_9.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_a.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_b.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_c.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_d.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_e.html +https://dhcpd-pools.sourceforge.net/doxygen/search/defines_f.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enums_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enums_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enums_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enums_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enums_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enums_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enums_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_7.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_8.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_9.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_a.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_b.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_c.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_d.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_e.html +https://dhcpd-pools.sourceforge.net/doxygen/search/enumvalues_f.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_10.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_11.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_12.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_13.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_14.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_15.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_16.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_7.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_8.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_9.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_a.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_b.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_c.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_d.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_e.html +https://dhcpd-pools.sourceforge.net/doxygen/search/files_f.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_10.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_11.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_12.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_13.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_14.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_7.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_8.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_9.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_a.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_b.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_c.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_d.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_e.html +https://dhcpd-pools.sourceforge.net/doxygen/search/functions_f.html +https://dhcpd-pools.sourceforge.net/doxygen/search/nomatches.html +https://dhcpd-pools.sourceforge.net/doxygen/search/search.css +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_7.html +https://dhcpd-pools.sourceforge.net/doxygen/search/typedefs_8.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_0.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_10.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_11.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_12.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_13.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_14.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_15.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_16.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_1.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_2.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_3.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_4.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_5.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_6.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_7.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_8.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_9.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_a.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_b.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_c.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_d.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_e.html +https://dhcpd-pools.sourceforge.net/doxygen/search/variables_f.html +https://dhcpd-pools.sourceforge.net/doxygen/setenv_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/socket_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/socket_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/sort_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/stat_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/stat_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stat_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stat-w32_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/stat-w32_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stat-w32_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stdalign_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stdalign_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stdbool_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stdbool_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stddef_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stddef_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stdint_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stdint_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stdio_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stdio_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stdio_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stdio_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stdio-impl_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stdio-impl_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stdlib_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stdlib_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stdlib_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/stdlib_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stpncpy_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/strdup_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/streq_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/streq_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/strerror_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/strerror-override_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/strerror-override_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/strerror-override_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/strftime_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/strftime_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/string_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/string_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/string_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/string_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/stripslash_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/strstr_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/strtod_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/str-two-way_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/str-two-way_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/structconf__t.html +https://dhcpd-pools.sourceforge.net/doxygen/structexpl.html +https://dhcpd-pools.sourceforge.net/doxygen/struct__getopt__data.html +https://dhcpd-pools.sourceforge.net/doxygen/structiovec.html +https://dhcpd-pools.sourceforge.net/doxygen/structleases__t.html +https://dhcpd-pools.sourceforge.net/doxygen/structmsghdr.html +https://dhcpd-pools.sourceforge.net/doxygen/structmustach__itf.html +https://dhcpd-pools.sourceforge.net/doxygen/structoption.html +https://dhcpd-pools.sourceforge.net/doxygen/structoutput__helper__t.html +https://dhcpd-pools.sourceforge.net/doxygen/structoutput__sort.html +https://dhcpd-pools.sourceforge.net/doxygen/structquoting__options.html +https://dhcpd-pools.sourceforge.net/doxygen/structrange__t.html +https://dhcpd-pools.sourceforge.net/doxygen/structshared__network__t.html +https://dhcpd-pools.sourceforge.net/doxygen/structslotvec.html +https://dhcpd-pools.sourceforge.net/doxygen/structsockaddr__storage.html +https://dhcpd-pools.sourceforge.net/doxygen/structtimespec.html +https://dhcpd-pools.sourceforge.net/doxygen/struct____time__t__must__be__integral.html +https://dhcpd-pools.sourceforge.net/doxygen/structtm__zone.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__socket_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__socket_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__socket_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__stat_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__stat_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__types_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__types_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__uio_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/sys__uio_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/tabs.css +https://dhcpd-pools.sourceforge.net/doxygen/time_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/time_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/time_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/time_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/timegm_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/time-internal_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/time-internal_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/time__r_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/time__rz_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/types_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/types_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/tzset_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/uio_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/uio_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/unionipaddr__t.html +https://dhcpd-pools.sourceforge.net/doxygen/unionmemory__double.html +https://dhcpd-pools.sourceforge.net/doxygen/unionrpl__max__align__t.html +https://dhcpd-pools.sourceforge.net/doxygen/unistd_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/unistd_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/unistd_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/unistd_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/unistd_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/unsetenv_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/unused-parameter_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/unused-parameter_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/verify_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/verify_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/warn-on-use_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/warn-on-use_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/wchar_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/wchar_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/wchar_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/wchar_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/wctype_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/wctype_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/wctype_8in_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/wctype_8in_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/wctype-h_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/xalloc_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/xalloc_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/xalloc-die_8c.html +https://dhcpd-pools.sourceforge.net/doxygen/xalloc-oversized_8h.html +https://dhcpd-pools.sourceforge.net/doxygen/xalloc-oversized_8h_source.html +https://dhcpd-pools.sourceforge.net/doxygen/xmalloc_8c.html