mirror of
git://git.code.sf.net/p/dhcpd-pools/code
synced 2025-12-16 07:47:00 +00:00
The earlier behavior made alarming nearly impossible to use for shared networks only, as they often are expected to be whole lot less full than ranges. Unfortunately if a alarm level was exceeded either by range or shared network the exit value changed. In most of the cases that lead alarms to be sent, without sensible message. Reported-by: Dan Pritts <danno@umich.edu> Signed-off-by: Sami Kerola <kerolasa@iki.fi>
1097 lines
30 KiB
C
1097 lines
30 KiB
C
/*
|
|
* 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 output.c
|
|
* \brief All about output formats.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <langinfo.h>
|
|
#include <netinet/in.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
|
|
#include "close-stream.h"
|
|
#include "dhcpd-pools.h"
|
|
#include "progname.h"
|
|
#include "strftime.h"
|
|
|
|
/*! \brief Text output format, which is the default.
|
|
* FIXME: This function should return void. */
|
|
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.dhcp_version == VERSION_6 ? 39 : 16;
|
|
|
|
if (config.output_file[0]) {
|
|
outfile = fopen(config.output_file, "w+");
|
|
if (outfile == NULL) {
|
|
err(EXIT_FAILURE, "output_txt: %s", config.output_file);
|
|
}
|
|
} else {
|
|
outfile = stdout;
|
|
}
|
|
|
|
range_p = ranges;
|
|
range_size = get_range_size(range_p);
|
|
shared_p = shared_networks;
|
|
|
|
if (config.output_limit[0] & BIT1) {
|
|
fprintf(outfile, "Ranges:\n");
|
|
fprintf
|
|
(outfile,
|
|
"%-20s%-*s %-*s %5s %5s %10s %5s %5s %9s",
|
|
"shared net name",
|
|
max_ipaddr_length,
|
|
"first ip",
|
|
max_ipaddr_length,
|
|
"last ip",
|
|
"max", "cur", "percent", "touch", "t+c", "t+c perc");
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, " bu bu perc");
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[1] & BIT1) {
|
|
for (i = 0; i < num_ranges; i++) {
|
|
if (range_p->shared_net) {
|
|
fprintf(outfile, "%-20s",
|
|
range_p->shared_net->name);
|
|
} else {
|
|
fprintf(outfile, "not_defined ");
|
|
}
|
|
/* Outputting of first_ip and last_ip need to be
|
|
* separate since ntop_ipaddr always returns the
|
|
* same buffer */
|
|
fprintf(outfile, "%-*s",
|
|
max_ipaddr_length,
|
|
ntop_ipaddr(&range_p->first_ip));
|
|
fprintf(outfile,
|
|
" - %-*s %5g %5g %10.3f %5g %5g %9.3f",
|
|
max_ipaddr_length,
|
|
ntop_ipaddr(&range_p->last_ip),
|
|
range_size,
|
|
range_p->count,
|
|
(float)(100 * range_p->count) / range_size,
|
|
range_p->touched,
|
|
range_p->touched + range_p->count,
|
|
(float)(100 *
|
|
(range_p->touched +
|
|
range_p->count)) / range_size);
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, "%7g %8.3f",
|
|
range_p->backups,
|
|
(float)(100 * range_p->backups) /
|
|
range_size);
|
|
}
|
|
fprintf(outfile, "\n");
|
|
range_p++;
|
|
range_size = get_range_size(range_p);
|
|
}
|
|
}
|
|
if (config.output_limit[1] & BIT1 && config.output_limit[0] & BIT2) {
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[0] & BIT2) {
|
|
fprintf(outfile, "Shared networks:\n");
|
|
fprintf(outfile,
|
|
"name max cur percent touch t+c t+c perc");
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, " bu bu perc");
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[1] & BIT2) {
|
|
for (i = 0; i < num_shared_networks; i++) {
|
|
shared_p++;
|
|
fprintf(outfile,
|
|
"%-20s %5g %5g %10.3f %7g %6g %9.3f",
|
|
shared_p->name, shared_p->available,
|
|
shared_p->used,
|
|
(float)(100 * shared_p->used) /
|
|
shared_p->available, shared_p->touched,
|
|
shared_p->touched + shared_p->used,
|
|
(float)(100 *
|
|
(shared_p->touched +
|
|
shared_p->used)) /
|
|
shared_p->available);
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, "%7g %8.3f",
|
|
shared_p->backups,
|
|
(float)(100 * shared_p->backups) /
|
|
shared_p->available);
|
|
}
|
|
|
|
fprintf(outfile, "\n");
|
|
}
|
|
}
|
|
if (config.output_limit[1] & BIT2 && config.output_limit[0] & BIT3) {
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[0] & BIT3) {
|
|
fprintf(outfile, "Sum of all ranges:\n");
|
|
fprintf(outfile,
|
|
"name max cur percent touch t+c t+c perc");
|
|
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, " bu bu perc");
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[1] & BIT3) {
|
|
fprintf(outfile, "%-20s %5g %5g %10.3f %7g %6g %9.3f",
|
|
shared_networks->name,
|
|
shared_networks->available,
|
|
shared_networks->used,
|
|
(float)(100 * shared_networks->used) /
|
|
shared_networks->available,
|
|
shared_networks->touched,
|
|
shared_networks->touched + shared_networks->used,
|
|
(float)(100 *
|
|
(shared_networks->touched +
|
|
shared_networks->used)) /
|
|
shared_networks->available);
|
|
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, "%7g %8.3f",
|
|
shared_networks->backups,
|
|
(float)(100 * shared_networks->backups) /
|
|
shared_networks->available);
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (outfile == stdout) {
|
|
ret = fflush(stdout);
|
|
if (ret) {
|
|
warn("output_txt: fflush");
|
|
}
|
|
} else {
|
|
ret = close_stream(outfile);
|
|
if (ret) {
|
|
warn("output_txt: fclose");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! \brief The xml output formats.
|
|
* FIXME: This function should return void.
|
|
*/
|
|
int output_xml(void)
|
|
{
|
|
unsigned int i;
|
|
struct range_t *range_p;
|
|
double range_size;
|
|
struct shared_network_t *shared_p;
|
|
int ret;
|
|
FILE *outfile;
|
|
|
|
if (config.output_file[0]) {
|
|
outfile = fopen(config.output_file, "w+");
|
|
if (outfile == NULL) {
|
|
err(EXIT_FAILURE, "output_xml: %s", config.output_file);
|
|
}
|
|
} else {
|
|
outfile = stdout;
|
|
}
|
|
|
|
range_p = ranges;
|
|
range_size = get_range_size(range_p);
|
|
shared_p = shared_networks;
|
|
|
|
fprintf(outfile, "<dhcpstatus>\n");
|
|
|
|
if (config.output_format[0] == 'X' || config.output_format[0] == 'J') {
|
|
struct leases_t *l;
|
|
for (l = leases; l != NULL; l = l->hh.next) {
|
|
if (l->type == ACTIVE) {
|
|
fputs("<active_lease>\n\t<ip>", outfile);
|
|
fputs(ntop_ipaddr(&l->ip), outfile);
|
|
fputs("</ip>\n\t<macaddress>", outfile);
|
|
if (l->ethernet != NULL) {
|
|
fputs(l->ethernet, outfile);
|
|
}
|
|
fputs("</macaddress>\n</active_lease>\n",
|
|
outfile);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (config.output_limit[1] & BIT1) {
|
|
for (i = 0; i < num_ranges; i++) {
|
|
fprintf(outfile, "<subnet>\n");
|
|
if (range_p->shared_net) {
|
|
fprintf(outfile,
|
|
"\t<location>%s</location>\n",
|
|
range_p->shared_net->name);
|
|
} else {
|
|
fprintf(outfile, "\t<location></location>\n");
|
|
}
|
|
|
|
fprintf(outfile, "\t<network></network>\n");
|
|
fprintf(outfile, "\t<netmask></netmask>\n");
|
|
fprintf(outfile, "\t<range>%s ",
|
|
ntop_ipaddr(&range_p->first_ip));
|
|
fprintf(outfile, "- %s</range>\n",
|
|
ntop_ipaddr(&range_p->last_ip));
|
|
fprintf(outfile, "\t<gateway></gateway>\n");
|
|
fprintf(outfile, "\t<defined>%g</defined>\n",
|
|
range_size);
|
|
fprintf(outfile, "\t<used>%g</used>\n",
|
|
range_p->count);
|
|
fprintf(outfile, "\t<free>%g</free>\n",
|
|
range_size - range_p->count);
|
|
range_p++;
|
|
range_size = get_range_size(range_p);
|
|
fprintf(outfile, "</subnet>\n");
|
|
}
|
|
}
|
|
|
|
if (config.output_limit[1] & BIT2) {
|
|
for (i = 0; i < num_shared_networks; i++) {
|
|
shared_p++;
|
|
fprintf(outfile, "<shared-network>\n");
|
|
fprintf(outfile, "\t<location>%s</location>\n",
|
|
shared_p->name);
|
|
fprintf(outfile, "\t<defined>%g</defined>\n",
|
|
shared_p->available);
|
|
fprintf(outfile, "\t<used>%g</used>\n",
|
|
shared_p->used);
|
|
fprintf(outfile, "\t<free>%g</free>\n",
|
|
shared_p->available - shared_p->used);
|
|
fprintf(outfile, "</shared-network>\n");
|
|
}
|
|
}
|
|
|
|
if (config.output_limit[0] & BIT3) {
|
|
fprintf(outfile, "<summary>\n");
|
|
fprintf(outfile, "\t<location>%s</location>\n",
|
|
shared_networks->name);
|
|
fprintf(outfile, "\t<defined>%g</defined>\n",
|
|
shared_networks->available);
|
|
fprintf(outfile, "\t<used>%g</used>\n", shared_networks->used);
|
|
fprintf(outfile, "\t<free>%g</free>\n",
|
|
shared_networks->available - shared_networks->used);
|
|
fprintf(outfile, "</summary>\n");
|
|
}
|
|
|
|
fprintf(outfile, "</dhcpstatus>\n");
|
|
if (outfile == stdout) {
|
|
ret = fflush(stdout);
|
|
if (ret) {
|
|
warn("output_xml: fflush");
|
|
}
|
|
} else {
|
|
ret = close_stream(outfile);
|
|
if (ret) {
|
|
warn("output_xml: fclose");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! \brief The json output formats.
|
|
* FIXME: This function should return void.
|
|
*/
|
|
int output_json(void)
|
|
{
|
|
unsigned int i = 0;
|
|
struct range_t *range_p;
|
|
double range_size;
|
|
struct shared_network_t *shared_p;
|
|
int ret;
|
|
FILE *outfile;
|
|
unsigned int sep;
|
|
|
|
if (config.output_file[0]) {
|
|
outfile = fopen(config.output_file, "w+");
|
|
if (outfile == NULL) {
|
|
err(EXIT_FAILURE, "output_json: %s",
|
|
config.output_file);
|
|
}
|
|
} else {
|
|
outfile = stdout;
|
|
}
|
|
|
|
range_p = ranges;
|
|
range_size = get_range_size(range_p);
|
|
shared_p = shared_networks;
|
|
sep = 0;
|
|
|
|
fprintf(outfile, "{\n");
|
|
|
|
if (config.output_format[0] == 'X' || config.output_format[0] == 'J') {
|
|
struct leases_t *l;
|
|
fprintf(outfile, " \"active_leases\": [");
|
|
for (l = leases; l != NULL; l = l->hh.next) {
|
|
if (l->type == ACTIVE) {
|
|
if (i == 0) {
|
|
i = 1;
|
|
} else {
|
|
fputc(',', outfile);
|
|
}
|
|
fputs("\n { \"ip\":\"", outfile);
|
|
fputs(ntop_ipaddr(&l->ip), outfile);
|
|
fputs("\", \"macaddress\":\"", outfile);
|
|
if (l->ethernet != NULL) {
|
|
fputs(l->ethernet, outfile);
|
|
}
|
|
fputs("\" }", outfile);
|
|
}
|
|
}
|
|
fprintf(outfile, "\n ]"); /* end of active_leases */
|
|
sep++;
|
|
}
|
|
|
|
if (config.output_limit[1] & BIT1) {
|
|
if (sep) {
|
|
fprintf(outfile, ",\n");
|
|
}
|
|
fprintf(outfile, " \"subnets\": [\n");
|
|
for (i = 0; i < num_ranges; i++) {
|
|
fprintf(outfile, " ");
|
|
fprintf(outfile, "{ ");
|
|
if (range_p->shared_net) {
|
|
fprintf(outfile,
|
|
"\"location\":\"%s\", ",
|
|
range_p->shared_net->name);
|
|
} else {
|
|
fprintf(outfile, "\"location\":\"\", ");
|
|
}
|
|
|
|
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, "\"used\":%g, ", range_p->count);
|
|
fprintf(outfile, "\"free\":%g ",
|
|
range_size - range_p->count);
|
|
range_p++;
|
|
range_size = get_range_size(range_p);
|
|
if (i + 1 < num_ranges)
|
|
fprintf(outfile, "},\n");
|
|
else
|
|
fprintf(outfile, "}\n");
|
|
}
|
|
fprintf(outfile, " ]"); /* end of subnets */
|
|
sep++;
|
|
}
|
|
|
|
if (config.output_limit[1] & BIT2) {
|
|
if (sep) {
|
|
fprintf(outfile, ",\n");
|
|
}
|
|
fprintf(outfile, " \"shared-networks\": [\n");
|
|
for (i = 0; i < num_shared_networks; i++) {
|
|
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, "\"free\":%g ",
|
|
shared_p->available - shared_p->used);
|
|
if (i + 1 < num_shared_networks)
|
|
fprintf(outfile, "},\n");
|
|
else
|
|
fprintf(outfile, "}\n");
|
|
}
|
|
fprintf(outfile, " ]"); /* end of shared-networks */
|
|
sep++;
|
|
}
|
|
|
|
if (config.output_limit[0] & BIT3) {
|
|
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, " \"free\":%g\n",
|
|
shared_networks->available - shared_networks->used);
|
|
fprintf(outfile, " }"); /* end of summary */
|
|
sep++;
|
|
}
|
|
|
|
fprintf(outfile, "\n}\n");
|
|
if (outfile == stdout) {
|
|
ret = fflush(stdout);
|
|
if (ret) {
|
|
warn("output_json: fflush");
|
|
}
|
|
} else {
|
|
ret = close_stream(outfile);
|
|
if (ret) {
|
|
warn("output_json: fclose");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! \brief Header for full html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
*/
|
|
static void html_header(FILE *restrict f)
|
|
{
|
|
char outstr[200];
|
|
struct tm *tmp;
|
|
|
|
struct stat statbuf;
|
|
stat(config.dhcpdlease_file, &statbuf);
|
|
|
|
tmp = localtime(&statbuf.st_mtime);
|
|
if (tmp == NULL) {
|
|
err(EXIT_FAILURE, "html_header: localtime");
|
|
}
|
|
if (strftime(outstr, sizeof(outstr), nl_langinfo(D_T_FMT), tmp) == 0) {
|
|
errx(EXIT_FAILURE, "html_header: strftime returned 0");
|
|
}
|
|
|
|
fprintf(f, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \n");
|
|
fprintf(f, " \"http://www.w3.org/TR/html4/strict.dtd\">\n");
|
|
fprintf(f, "<html>\n");
|
|
fprintf(f, "<head>\n");
|
|
fprintf(f, "<meta http-equiv=\"Content-Type\" ");
|
|
fprintf(f, "content=\"text/html; charset=iso-8859-1\">\n");
|
|
fprintf(f, " <title>ISC dhcpd stats</title>\n");
|
|
fprintf(f, " <style TYPE=\"text/css\">\n");
|
|
fprintf(f, " <!--\n");
|
|
fprintf(f, " table.dhcpd-pools {\n");
|
|
fprintf(f, " color: black;\n");
|
|
fprintf(f, " vertical-align: middle;\n");
|
|
fprintf(f, " text-align: center;\n");
|
|
fprintf(f, " }\n");
|
|
fprintf(f, " table.dhcpd-pools th.section {\n");
|
|
fprintf(f, " color: black;\n");
|
|
fprintf(f, " font-size: large;\n");
|
|
fprintf(f, " vertical-align: middle;\n");
|
|
fprintf(f, " text-align: left;\n");
|
|
fprintf(f, " }\n");
|
|
fprintf(f, " table.dhcpd-pools th.calign {\n");
|
|
fprintf(f, " color: black;\n");
|
|
fprintf(f, " vertical-align: middle;\n");
|
|
fprintf(f, " text-align: center;\n");
|
|
fprintf(f, " text-decoration: underline;\n");
|
|
fprintf(f, " }\n");
|
|
fprintf(f, " table.dhcpd-pools th.ralign {\n");
|
|
fprintf(f, " color: black;\n");
|
|
fprintf(f, " vertical-align: middle;\n");
|
|
fprintf(f, " text-align: right;\n");
|
|
fprintf(f, " text-decoration: underline;\n");
|
|
fprintf(f, " }\n");
|
|
fprintf(f, " table.dhcpd-pools td.calign {\n");
|
|
fprintf(f, " color: black;\n");
|
|
fprintf(f, " vertical-align: middle;\n");
|
|
fprintf(f, " text-align: center;\n");
|
|
fprintf(f, " }\n");
|
|
fprintf(f, " table.dhcpd-pools td.ralign {\n");
|
|
fprintf(f, " color: black;\n");
|
|
fprintf(f, " vertical-align: middle;\n");
|
|
fprintf(f, " text-align: right;\n");
|
|
fprintf(f, " }\n");
|
|
fprintf(f, " p.created {\n");
|
|
fprintf(f, " font-size: small;\n");
|
|
fprintf(f, " color: grey;\n");
|
|
fprintf(f, " }\n");
|
|
fprintf(f, " p.updated {\n");
|
|
fprintf(f, " font-size: small;\n");
|
|
fprintf(f, " color: grey;\n");
|
|
fprintf(f, " font-style: italic;\n");
|
|
fprintf(f, " }\n");
|
|
fprintf(f, " -->\n");
|
|
fprintf(f, " </style>\n");
|
|
fprintf(f, "</head>\n");
|
|
fprintf(f, "<body>\n");
|
|
fprintf(f, "<a name=\"ranges\">The lease file mtime: %s</a>", outstr);
|
|
}
|
|
|
|
/*! \brief Footer for full html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
*/
|
|
static void html_footer(FILE *restrict f)
|
|
{
|
|
fprintf(f, "<p><br></p>\n");
|
|
fprintf(f, "<hr>\n");
|
|
fprintf(f, "<p class=created>\nData generated by ");
|
|
fprintf(f, "<a href=\"%s\">", PACKAGE_URL);
|
|
fprintf(f, "%s</a>.\n</p>\n", PACKAGE_STRING);
|
|
fprintf(f, "<p class=updated>\n");
|
|
fprintf(f, "<script type=\"text/javascript\">\n");
|
|
fprintf(f, " document.write(\"Last Updated On \" + ");
|
|
fprintf(f, "document.lastModified + \".\")\n");
|
|
fprintf(f, "</script>\n<br>\n</p>\n");
|
|
fprintf(f, "</body>\n");
|
|
fprintf(f, "</html>\n");
|
|
}
|
|
|
|
/*! \brief A new row for html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
*/
|
|
static void newrow(FILE *restrict f)
|
|
{
|
|
fprintf(f, "<tr>\n");
|
|
}
|
|
|
|
/*! \brief End a row for html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
*/
|
|
static void endrow(FILE *restrict f)
|
|
{
|
|
fprintf(f, "</tr>\n\n");
|
|
}
|
|
|
|
/*! \brief Line with text in html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
* \param type HTML tag name.
|
|
* \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 class, char const *restrict text)
|
|
{
|
|
fprintf(f, " <%s class=%s>%s</%s>\n", type, class, text, type);
|
|
}
|
|
|
|
/*! \brief Line with digit in html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
* \param type HMTL tag name.
|
|
* \param d Actual payload of the printout.
|
|
*/
|
|
static void output_double(FILE *restrict f, char const *restrict type,
|
|
double d)
|
|
{
|
|
fprintf(f, " <%s class=ralign>%g</%s>\n", type, d, 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 *f, char const *restrict type, float fl)
|
|
{
|
|
fprintf(f, " <%s class=ralign>%.3f</%s>\n", type, fl, type);
|
|
}
|
|
|
|
/*! \brief Begin table in html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
*/
|
|
static void table_start(FILE *restrict f)
|
|
{
|
|
fprintf(f, "<table width=\"75%%\" ");
|
|
fprintf(f, "class=\"%s\" ", PACKAGE_NAME);
|
|
fprintf(f, "summary=\"ISC dhcpd pool usage report\">\n");
|
|
}
|
|
|
|
/*! \brief End table in html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
*/
|
|
static void table_end(FILE *restrict f)
|
|
{
|
|
fprintf(f, "</table>\n");
|
|
}
|
|
|
|
/*! \brief New section in html output format.
|
|
*
|
|
* \param f Output file descriptor.
|
|
* \param title Table title.
|
|
*/
|
|
static void newsection(FILE *restrict f, char const *restrict title)
|
|
{
|
|
newrow(f);
|
|
output_line(f, "td", "calign", " ");
|
|
endrow(f);
|
|
newrow(f);
|
|
output_line(f, "th", "section", title);
|
|
endrow(f);
|
|
}
|
|
|
|
/*! \brief Output html format.
|
|
* FIXME: This function should return void.
|
|
*/
|
|
int output_html(void)
|
|
{
|
|
unsigned int i;
|
|
struct range_t *range_p;
|
|
double range_size;
|
|
struct shared_network_t *shared_p;
|
|
int ret;
|
|
FILE *outfile;
|
|
if (config.output_file[0]) {
|
|
outfile = fopen(config.output_file, "w+");
|
|
if (outfile == NULL) {
|
|
err(EXIT_FAILURE, "output_html: %s",
|
|
config.output_file);
|
|
}
|
|
} else {
|
|
outfile = stdout;
|
|
}
|
|
|
|
range_p = ranges;
|
|
range_size = get_range_size(range_p);
|
|
shared_p = shared_networks;
|
|
if (config.fullhtml) {
|
|
html_header(outfile);
|
|
}
|
|
table_start(outfile);
|
|
if (config.output_limit[0] & BIT1) {
|
|
newsection(outfile, "Ranges:");
|
|
newrow(outfile);
|
|
output_line(outfile, "th", "calign", "shared net name");
|
|
output_line(outfile, "th", "calign", "first ip");
|
|
output_line(outfile, "th", "calign", "last ip");
|
|
output_line(outfile, "th", "ralign", "max");
|
|
output_line(outfile, "th", "ralign", "cur");
|
|
output_line(outfile, "th", "ralign", "percent");
|
|
output_line(outfile, "th", "ralign", "touch");
|
|
output_line(outfile, "th", "ralign", "t+c");
|
|
output_line(outfile, "th", "ralign", "t+c perc");
|
|
if (config.backups_found == true) {
|
|
output_line(outfile, "th", "ralign", "bu");
|
|
output_line(outfile, "th", "ralign", "bu perc");
|
|
}
|
|
endrow(outfile);
|
|
}
|
|
if (config.output_limit[1] & BIT1) {
|
|
for (i = 0; i < num_ranges; i++) {
|
|
newrow(outfile);
|
|
if (range_p->shared_net) {
|
|
output_line(outfile, "td", "calign",
|
|
range_p->shared_net->name);
|
|
} else {
|
|
output_line(outfile, "td", "calign",
|
|
"not_defined");
|
|
}
|
|
output_line(outfile, "td", "calign",
|
|
ntop_ipaddr(&range_p->first_ip));
|
|
output_line(outfile, "td", "calign",
|
|
ntop_ipaddr(&range_p->last_ip));
|
|
output_double(outfile, "td", range_size);
|
|
output_double(outfile, "td", range_p->count);
|
|
output_float(outfile, "td",
|
|
(float)(100 * range_p->count) /
|
|
range_size);
|
|
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 == true) {
|
|
output_double(outfile, "td", range_p->backups);
|
|
output_float(outfile, "td",
|
|
(float)(100 *
|
|
range_p->backups) /
|
|
range_size);
|
|
}
|
|
endrow(outfile);
|
|
range_p++;
|
|
range_size = get_range_size(range_p);
|
|
}
|
|
}
|
|
table_end(outfile);
|
|
table_start(outfile);
|
|
if (config.output_limit[0] & BIT2) {
|
|
newsection(outfile, "Shared networks:");
|
|
newrow(outfile);
|
|
output_line(outfile, "th", "calign", "name");
|
|
output_line(outfile, "th", "ralign", "max");
|
|
output_line(outfile, "th", "ralign", "cur");
|
|
output_line(outfile, "th", "ralign", "percent");
|
|
output_line(outfile, "th", "ralign", "touch");
|
|
output_line(outfile, "th", "ralign", "t+c");
|
|
output_line(outfile, "th", "ralign", "t+c perc");
|
|
if (config.backups_found == true) {
|
|
output_line(outfile, "th", "ralign", "bu");
|
|
output_line(outfile, "th", "ralign", "bu perc");
|
|
}
|
|
endrow(outfile);
|
|
}
|
|
if (config.output_limit[1] & BIT2) {
|
|
for (i = 0; i < num_shared_networks; i++) {
|
|
shared_p++;
|
|
newrow(outfile);
|
|
output_line(outfile, "td", "calign", shared_p->name);
|
|
output_double(outfile, "td", shared_p->available);
|
|
output_double(outfile, "td", shared_p->used);
|
|
output_float(outfile, "td",
|
|
(float)(100 * shared_p->used) /
|
|
shared_p->available);
|
|
output_double(outfile, "td", shared_p->touched);
|
|
output_double(outfile, "td",
|
|
shared_p->touched + shared_p->used);
|
|
output_float(outfile, "td",
|
|
(float)(100 *
|
|
(shared_p->touched +
|
|
shared_p->used)) /
|
|
shared_p->available);
|
|
if (config.backups_found == true) {
|
|
output_double(outfile, "td", shared_p->backups);
|
|
output_float(outfile, "td",
|
|
(float)(100 *
|
|
shared_p->backups) /
|
|
shared_p->available);
|
|
}
|
|
|
|
endrow(outfile);
|
|
}
|
|
}
|
|
if (config.output_limit[0] & BIT3) {
|
|
newsection(outfile, "Sum of all ranges:");
|
|
newrow(outfile);
|
|
output_line(outfile, "th", "calign", "name");
|
|
output_line(outfile, "th", "ralign", "max");
|
|
output_line(outfile, "th", "ralign", "cur");
|
|
output_line(outfile, "th", "ralign", "percent");
|
|
output_line(outfile, "th", "ralign", "touch");
|
|
output_line(outfile, "th", "ralign", "t+c");
|
|
output_line(outfile, "th", "ralign", "t+c perc");
|
|
if (config.backups_found == true) {
|
|
output_line(outfile, "th", "ralign", "bu");
|
|
output_line(outfile, "th", "ralign", "bu perc");
|
|
}
|
|
|
|
endrow(outfile);
|
|
}
|
|
if (config.output_limit[1] & BIT3) {
|
|
newrow(outfile);
|
|
output_line(outfile, "td", "calign", shared_networks->name);
|
|
output_double(outfile, "td", shared_networks->available);
|
|
output_double(outfile, "td", shared_networks->used);
|
|
output_float(outfile, "td",
|
|
(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",
|
|
(float)(100 *
|
|
(shared_networks->touched +
|
|
shared_networks->used)) /
|
|
shared_networks->available);
|
|
if (config.backups_found == true) {
|
|
output_double(outfile, "td", shared_networks->backups);
|
|
output_float(outfile, "td",
|
|
(float)(100 *
|
|
shared_networks->backups) /
|
|
shared_networks->available);
|
|
}
|
|
endrow(outfile);
|
|
}
|
|
table_end(outfile);
|
|
if (config.fullhtml) {
|
|
html_footer(outfile);
|
|
}
|
|
if (outfile == stdout) {
|
|
ret = fflush(stdout);
|
|
if (ret) {
|
|
warn("output_html: fflush");
|
|
}
|
|
} else {
|
|
ret = close_stream(outfile);
|
|
if (ret) {
|
|
warn("output_html: fclose");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*! \brief Output cvs format.
|
|
* FIXME: This function should return void.
|
|
*/
|
|
int output_csv(void)
|
|
{
|
|
unsigned int i;
|
|
struct range_t *range_p;
|
|
double range_size;
|
|
struct shared_network_t *shared_p;
|
|
FILE *outfile;
|
|
int ret;
|
|
if (config.output_file[0]) {
|
|
outfile = fopen(config.output_file, "w+");
|
|
if (outfile == NULL) {
|
|
err(EXIT_FAILURE, "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.output_limit[0] & BIT1) {
|
|
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 == true) {
|
|
fprintf(outfile, ",\"bu\",\"bu perc\"");
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[1] & BIT1) {
|
|
for (i = 0; i < num_ranges; i++) {
|
|
if (range_p->shared_net) {
|
|
fprintf(outfile, "\"%s\",",
|
|
range_p->shared_net->name);
|
|
} else {
|
|
fprintf(outfile, "\"not_defined\",");
|
|
}
|
|
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,
|
|
range_p->count,
|
|
(float)(100 * range_p->count) / range_size,
|
|
range_p->touched,
|
|
range_p->touched + range_p->count,
|
|
(float)(100 *
|
|
(range_p->touched +
|
|
range_p->count)) / range_size);
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, ",\"%g\",\"%.3f\"",
|
|
range_p->backups,
|
|
(float)(100 * range_p->backups) /
|
|
range_size);
|
|
}
|
|
|
|
fprintf(outfile, "\n");
|
|
range_p++;
|
|
range_size = get_range_size(range_p);
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[0] & BIT2) {
|
|
fprintf(outfile, "\"Shared networks:\"\n");
|
|
fprintf(outfile,
|
|
"\"name\",\"max\",\"cur\",\"percent\",\"touch\",\"t+c\",\"t+c perc\"");
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, ",\"bu\",\"bu perc\"");
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[1] & BIT2) {
|
|
|
|
for (i = 0; i < num_shared_networks; i++) {
|
|
shared_p++;
|
|
fprintf(outfile,
|
|
"\"%s\",\"%g\",\"%g\",\"%.3f\",\"%g\",\"%g\",\"%.3f\"",
|
|
shared_p->name, shared_p->available,
|
|
shared_p->used,
|
|
(float)(100 * shared_p->used) /
|
|
shared_p->available, shared_p->touched,
|
|
shared_p->touched + shared_p->used,
|
|
(float)(100 *
|
|
(shared_p->touched +
|
|
shared_p->used)) /
|
|
shared_p->available);
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, ",\"%g\",\"%.3f\"",
|
|
shared_p->backups,
|
|
(float)(100 * shared_p->backups) /
|
|
shared_p->available);
|
|
}
|
|
|
|
fprintf(outfile, "\n");
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[0] & BIT3) {
|
|
fprintf(outfile, "\"Sum of all ranges:\"\n");
|
|
fprintf(outfile,
|
|
"\"name\",\"max\",\"cur\",\"percent\",\"touch\",\"t+c\",\"t+c perc\"");
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, ",\"bu\",\"bu perc\"");
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (config.output_limit[1] & BIT3) {
|
|
|
|
fprintf(outfile,
|
|
"\"%s\",\"%g\",\"%g\",\"%.3f\",\"%g\",\"%g\",\"%.3f\"",
|
|
shared_networks->name, shared_networks->available,
|
|
shared_networks->used,
|
|
(float)(100 * shared_networks->used) /
|
|
shared_networks->available,
|
|
shared_networks->touched,
|
|
shared_networks->touched + shared_networks->used,
|
|
(float)(100 *
|
|
(shared_networks->touched +
|
|
shared_networks->used)) /
|
|
shared_networks->available);
|
|
if (config.backups_found == true) {
|
|
fprintf(outfile, "%7g %8.3f",
|
|
shared_networks->backups,
|
|
(float)(100 * shared_networks->backups) /
|
|
shared_networks->available);
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
if (outfile == stdout) {
|
|
ret = fflush(stdout);
|
|
if (ret) {
|
|
warn("output_cvs: fflush");
|
|
}
|
|
} else {
|
|
ret = close_stream(outfile);
|
|
if (ret) {
|
|
warn("output_cvs: fclose");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*! \brief Output alarm text, and return program exit value.
|
|
* FIXME: This function should return void.
|
|
*/
|
|
int output_alarming(void)
|
|
{
|
|
FILE *outfile;
|
|
struct range_t *range_p;
|
|
double range_size;
|
|
struct shared_network_t *shared_p;
|
|
unsigned int i;
|
|
float perc;
|
|
int rw = 0, rc = 0, ro = 0, sw = 0, sc = 0, so = 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) {
|
|
err(EXIT_FAILURE, "output_alarming: %s",
|
|
config.output_file);
|
|
}
|
|
} else {
|
|
outfile = stdout;
|
|
}
|
|
|
|
if (config.output_limit[1] & BIT1) {
|
|
for (i = 0; i < num_ranges; i++) {
|
|
perc = (float)(100 * range_p->count) / range_size;
|
|
if (config.critical < perc)
|
|
rc++;
|
|
else if (config.warning < perc)
|
|
rw++;
|
|
else
|
|
ro++;
|
|
range_p++;
|
|
range_size = get_range_size(range_p);
|
|
}
|
|
}
|
|
if (config.output_limit[1] & BIT2) {
|
|
for (i = 0; i < num_shared_networks; i++) {
|
|
perc = (float)(100 * shared_p->used) /
|
|
shared_p->available;
|
|
if (config.critical < perc)
|
|
sc++;
|
|
else if (config.warning < perc)
|
|
sw++;
|
|
else
|
|
so++;
|
|
shared_p++;
|
|
}
|
|
}
|
|
if ((0 < rc && config.output_limit[1] & BIT1) || (0 < sc && config.output_limit[1] & BIT2)) {
|
|
ret_val = 2;
|
|
fprintf(outfile, "CRITICAL: %s: ",
|
|
program_name);
|
|
} else if ((0 < rw && config.output_limit[1] & BIT1) || (0 < sw && config.output_limit[1] & BIT2)) {
|
|
ret_val = 1;
|
|
fprintf(outfile, "WARNING: %s: ",
|
|
program_name);
|
|
} else {
|
|
ret_val = 0;
|
|
fprintf(outfile, "OK: ");
|
|
}
|
|
if (config.output_limit[0] & BIT1) {
|
|
fprintf(outfile, "Ranges; crit: %d warn: %d ok: %d ", rc, rw,
|
|
ro);
|
|
}
|
|
if (config.output_limit[0] & BIT2) {
|
|
fprintf(outfile, "Shared nets; crit: %d warn: %d ok: %d", sc,
|
|
sw, so);
|
|
}
|
|
fprintf(outfile, "\n");
|
|
if (outfile == stdout) {
|
|
ret = fflush(stdout);
|
|
if (ret) {
|
|
warn("output_alarming: fflush");
|
|
}
|
|
} else {
|
|
ret = close_stream(outfile);
|
|
if (ret) {
|
|
warn("output_alarming: fclose");
|
|
}
|
|
}
|
|
return ret_val;
|
|
}
|