From 1299737d76dfabc17bb79d8eb2475b1a3784b9c7 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 28 Nov 2015 17:59:42 +0000 Subject: [PATCH] make html output to use Bootstrap and DataTables This make the table output good looking, and allows users to click table headings to sort data by column without rerunning the analysis. Unfortunately this change is breaking change, meaning the old CSS tags are no longer supported, nor partial html output that printed only the table. Proposed-by: Aaron Paetznick Signed-off-by: Sami Kerola --- README | 6 + man/dhcpd-pools.1.in | 8 +- src/dhcpd-pools.c | 4 +- src/dhcpd-pools.h | 1 - src/other.c | 1 - src/output.c | 359 +++++++++++++++++++------------------------ 6 files changed, 170 insertions(+), 209 deletions(-) diff --git a/README b/README index 40c8171..747e8f1 100644 --- a/README +++ b/README @@ -43,6 +43,12 @@ Dependencies to other projects. See quick start. + https://getbootstrap.com/ + https://datatables.net/ + + Java Bootstrap and DataTables java scripts are used in html + output. + Test data wanted. Maintainer is interested to get copy of your dhcpd.conf diff --git a/man/dhcpd-pools.1.in b/man/dhcpd-pools.1.in index e55f5b6..4f5ca76 100644 --- a/man/dhcpd-pools.1.in +++ b/man/dhcpd-pools.1.in @@ -68,12 +68,10 @@ Sort results in reverse order. Output format. Text .RI ( t ). -Standard html -.RI ( h ) -outputs only the HTML tables, and is useful for embedding more complex web -pages. Full-html +Full-html .RI ( H ) -provides complete HTML headers, etc., including in-line CSS. The +page output. +The .RI ( c ) stands for comma-separated values. Output format xml .RI ( x ) diff --git a/src/dhcpd-pools.c b/src/dhcpd-pools.c index 7b765d7..8c92b3a 100644 --- a/src/dhcpd-pools.c +++ b/src/dhcpd-pools.c @@ -145,7 +145,6 @@ int main(int argc, char **argv) config.output_limit[0] = (*tmp - '0'); tmp++; config.output_limit[1] = (*tmp - '0'); - config.fullhtml = false; /* Make sure some output format is selected by default */ strncpy(config.output_format, OUTPUT_FORMAT, (size_t)1); /* Default sort order is by IPs small to big */ @@ -245,11 +244,10 @@ int main(int argc, char **argv) output_analysis = output_alarming; break; case 'h': - output_analysis = output_html; + error(EXIT_FAILURE, 0, "html table only output format is deprecated"); break; case 'H': output_analysis = output_html; - config.fullhtml = true; break; case 'x': output_analysis = output_xml; diff --git a/src/dhcpd-pools.h b/src/dhcpd-pools.h index 79335fb..afee609 100644 --- a/src/dhcpd-pools.h +++ b/src/dhcpd-pools.h @@ -124,7 +124,6 @@ struct configuration_t { char *dhcpdconf_file; char *dhcpdlease_file; char output_format[2]; - bool fullhtml; char sort[6]; bool reverse_order; char *output_file; diff --git a/src/other.c b/src/other.c index 040c55a..bfdb6bb 100644 --- a/src/other.c +++ b/src/other.c @@ -448,7 +448,6 @@ This is ISC dhcpd pools usage analyzer.\n\ fprintf(out, "\ -f, --format=[thHcxXjJ] output format\n\ t for text\n\ - h for html table\n\ H for full html page\n\ x for xml\n\ X for xml with active lease details\n\ diff --git a/src/output.c b/src/output.c index 0828646..add823f 100644 --- a/src/output.c +++ b/src/output.c @@ -478,63 +478,23 @@ static void html_header(FILE *restrict f) 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"); fprintf(f, "\n"); - fprintf(f, "\n"); - fprintf(f, "ISC dhcpd stats\n"); + fprintf(f, "ISC dhcpd dhcpd-pools output\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, ""); fprintf(f, "\n"); fprintf(f, "\n"); fprintf(f, "\n"); - fprintf(f, "The lease file mtime: %s", outstr); + fprintf(f, "
\n"); + fprintf(f, "

ISC DHCPD status

\n"); + fprintf(f, "File %s was last modified at %s
\n", config.dhcpdlease_file, outstr); } /*! \brief Footer for full html output format. @@ -543,36 +503,36 @@ static void html_header(FILE *restrict f) */ static void html_footer(FILE *restrict f) { - fprintf(f, "


\n"); - fprintf(f, "
\n"); - fprintf(f, "

\nData generated by "); - fprintf(f, "", PACKAGE_URL); - fprintf(f, "%s.\n

\n", PACKAGE_STRING); - fprintf(f, "

\n"); - fprintf(f, "\n
\n

\n"); - fprintf(f, "\n"); - fprintf(f, "\n"); + fprintf(f, "
\n"); + 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"); } -/*! \brief A new row for html output format. +/*! \brief Start a html tag. * * \param f Output file descriptor. + * \param tag The html tag. */ -static void newrow(FILE *restrict f) +static void start_tag(FILE *restrict f, char const *restrict tag) { - fprintf(f, "\n"); + fprintf(f, "<%s>\n", tag); } -/*! \brief End a row for html output format. +/*! \brief End a html tag. * * \param f Output file descriptor. + * \param tag The html tag. */ -static void endrow(FILE *restrict f) +static void end_tag(FILE *restrict f, char const *restrict tag) { - fprintf(f, "\n"); + fprintf(f, "\n", tag); } /*! \brief Line with text in html output format. @@ -583,9 +543,9 @@ static void endrow(FILE *restrict f) * \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) + char const *restrict text) { - fprintf(f, "<%s class=\"%s\">%s\n", type, class, text, type); + fprintf(f, "<%s>%s\n", type, text, type); } /*! \brief Line with digit in html output format. @@ -596,7 +556,7 @@ static void output_line(FILE *restrict f, char const *restrict type, */ static void output_double(FILE *restrict f, char const *restrict type, double d) { - fprintf(f, "<%s class=\"ralign\">%g\n", type, d, type); + fprintf(f, "<%s>%g\n", type, d, type); } /*! \brief Line with float in html output format. @@ -605,20 +565,18 @@ static void output_double(FILE *restrict f, char const *restrict type, double d) * \param type HTML tag name. * \param fl Actual payload of the printout. */ -static void output_float(FILE *f, char const *restrict type, float fl) +static void output_float(FILE *restrict f, char const *restrict type, float fl) { - fprintf(f, "<%s class=\"ralign\">%.3f\n", type, fl, type); + fprintf(f, "<%s>%.3f\n", type, fl, type); } /*! \brief Begin table in html output format. * * \param f Output file descriptor. */ -static void table_start(FILE *restrict f) +static void table_start(FILE *restrict f, char const *restrict id, char const *restrict summary) { - fprintf(f, "\n"); + fprintf(f, "
\n", id, summary); } /*! \brief End table in html output format. @@ -637,12 +595,7 @@ static void table_end(FILE *restrict f) */ static void newsection(FILE *restrict f, char const *restrict title) { - newrow(f); - output_line(f, "td", "lalign", " "); - endrow(f); - newrow(f); - output_line(f, "th", "section", title); - endrow(f); + output_line(f, "h3", title); } /*! \brief Output html format. @@ -669,123 +622,30 @@ int output_html(void) 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", "lalign", "shared net name"); - output_line(outfile, "th", "lalign", "first ip"); - output_line(outfile, "th", "lalign", "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", "lalign", range_p->shared_net->name); - } else { - output_line(outfile, "td", "lalign", "not_defined"); - } - output_line(outfile, "td", "lalign", ntop_ipaddr(&range_p->first_ip)); - output_line(outfile, "td", "lalign", 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", "lalign", "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", "lalign", 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->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 == true) { - output_double(outfile, "td", shared_p->backups); - output_float(outfile, "td", - shared_p->available == 0 ? -NAN : (float)(100 * - shared_p->backups) - / shared_p->available); - } - - endrow(outfile); - } - } + html_header(outfile); + newsection(outfile, "Sum of all"); + table_start(outfile, "a", "all"); if (config.output_limit[0] & BIT3) { - newsection(outfile, "Sum of all ranges:"); - newrow(outfile); - output_line(outfile, "th", "lalign", "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"); + 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", "percent"); + output_line(outfile, "th", "touch"); + output_line(outfile, "th", "t+c"); + output_line(outfile, "th", "t+c perc"); if (config.backups_found == true) { - output_line(outfile, "th", "ralign", "bu"); - output_line(outfile, "th", "ralign", "bu perc"); + output_line(outfile, "th", "bu"); + output_line(outfile, "th", "bu perc"); } - - endrow(outfile); + end_tag(outfile, "tr"); + end_tag(outfile, "thead"); } if (config.output_limit[1] & BIT3) { - newrow(outfile); - output_line(outfile, "td", "lalign", shared_networks->name); + 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", @@ -807,12 +667,113 @@ int output_html(void) shared_networks->backups) / shared_networks->available); } - endrow(outfile); + end_tag(outfile, "tr"); + end_tag(outfile, "tbody"); } table_end(outfile); - if (config.fullhtml) { - html_footer(outfile); + newsection(outfile, "Shared networks"); + table_start(outfile, "s", "snet"); + if (config.output_limit[0] & BIT2) { + 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", "percent"); + output_line(outfile, "th", "touch"); + output_line(outfile, "th", "t+c"); + output_line(outfile, "th", "t+c perc"); + if (config.backups_found == true) { + output_line(outfile, "th", "bu"); + output_line(outfile, "th", "bu perc"); + } + end_tag(outfile, "tr"); + end_tag(outfile, "thead"); } + if (config.output_limit[1] & BIT2) { + start_tag(outfile, "tbody"); + for (i = 0; i < num_shared_networks; i++) { + shared_p++; + 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->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 == true) { + output_double(outfile, "td", shared_p->backups); + output_float(outfile, "td", + shared_p->available == 0 ? -NAN : (float)(100 * + shared_p->backups) + / shared_p->available); + } + end_tag(outfile, "tr"); + } + end_tag(outfile, "tbody"); + } + table_end(outfile); + newsection(outfile, "Ranges"); + table_start(outfile, "r", "ranges"); + if (config.output_limit[0] & BIT1) { + start_tag(outfile, "thead"); + start_tag(outfile, "tr"); + output_line(outfile, "th", "shared net name"); + output_line(outfile, "th", "first ip"); + output_line(outfile, "th", "last ip"); + output_line(outfile, "th", "max"); + output_line(outfile, "th", "cur"); + 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 == true) { + output_line(outfile, "th", "bu"); + output_line(outfile, "th", "bu perc"); + } + end_tag(outfile, "tr"); + end_tag(outfile, "thead"); + } + if (config.output_limit[1] & BIT1) { + start_tag(outfile, "tbody"); + for (i = 0; i < num_ranges; i++) { + start_tag(outfile, "tr"); + if (range_p->shared_net) { + output_line(outfile, "td", range_p->shared_net->name); + } else { + output_line(outfile, "td", "not_defined"); + } + 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", 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); + } + 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) {