/*
* out_html.c HTML Output
*
* Copyright (c) 2001-2005 Thomas Graf <tgraf@suug.ch>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <bmon/bmon.h>
#include <bmon/node.h>
#include <bmon/output.h>
#include <bmon/graph.h>
#include <bmon/utils.h>
static const char *c_path;
static int c_graph_height = 6;
static int c_update_interval = 1;
static int c_rowspan = 1;
static int c_hide_item = 0;
static char *c_title = "Traffic Rate Estimation and Monitoring";
static FILE * open_file(const char *p)
{
FILE *fd;
if (!(fd = fopen(p, "w")))
quit("fopen(%s) failed: %s\n", p, strerror(errno));
return fd;
}
static void write_css(const char *path)
{
char ofile[FILENAME_MAX];
FILE *f;
snprintf(ofile, sizeof(ofile), "%s/layout.css", path);
/* do not overwrite an existing .css */
if (!access(ofile, R_OK))
return;
f = open_file(ofile);
fprintf(f,
"/*\n" \
" * Generated by %s\n" \
" */\n" \
"\n" \
"body {\n" \
" background: #f7f7f7;\n" \
" margin-top: 20px;\n" \
"}\n" \
"\n" \
"p, b, td {\n" \
" color: #000000;\n" \
" font-family: Verdana, Arial, Helvetica, sans-serif;\n" \
" font-size: 10pt;\n" \
"}\n" \
"\n" \
"a:link,a:visited,a:hover {\n" \
" color: #4E7AAF;\n" \
" text-decoration: underline;\n" \
"}\n" \
"\n" \
"p.banner {\n" \
" margin-top: 8px;\n" \
" margin-bottom: 8px;\n" \
" text-align: center;\n" \
" font-size: 18pt;\n" \
" font-weight: bold;\n" \
"}\n" \
"\n" \
"p.node_title {\n" \
" text-align: center;\n" \
" margin: 0pt;\n" \
" margin-bottom: 10px;\n" \
" font-weight: bold;\n" \
" font-size: 18pt;\n" \
"}\n" \
"\n" \
"p.title {\n" \
" margin: 0pt;\n" \
" margin-top: 5px;\n" \
" text-align: center;\n" \
" font-weight: bold;\n" \
" font-size: 14pt;\n" \
"}\n" \
"\n" \
"a.a_node_link:link,a.a_node_link:visited,a.a_node_link:hover {\n" \
" color: #000;\n" \
" font-weight: bold;\n" \
" text-decoration: none;\n" \
"}\n" \
"\n" \
"a.a_node_link:hover {\n" \
" font-weight: bold;\n" \
" text-decoration: underline;\n" \
"}\n" \
"\n" \
"a.a_intf_link:link,a.a_intf_link:visited,a.a_intf_link:hover {\n" \
" color: #000;\n" \
" text-decoration: none;\n" \
"}\n" \
"\n" \
"a.a_intf_link:hover {\n" \
" text-decoration: underline;\n" \
"}\n" \
"\n" \
"p.selection {\n" \
" text-align: center;\n" \
" margin: 0pt;\n" \
"}\n" \
"\n" \
"table.intf_list {\n" \
" border: 1px solid #cdcdcd;\n" \
" width: 100%%;\n" \
" border-spacing: 1px;\n" \
" background: #f8f8f8;\n" \
" margin-bottom: 10px;\n" \
"}\n" \
"\n" \
"tr.intf_list_hdr {\n" \
" font-weight: bold;\n" \
"}\n" \
"\n" \
"th.intf_list_hdr_name {\n" \
" text-align: left;\n" \
" padding-left: 5px;\n" \
"}\n" \
"\n" \
"td.intf_list_nr {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" text-align: center;\n" \
"}\n" \
"\n" \
"td.intf_list_name {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" border-left: 1px dashed #cdcdcd;\n" \
" text-align: left;\n" \
" padding-left: 5px;\n" \
"}\n" \
"\n" \
"td.intf_list_rx {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" border-left: 1px dashed #cdcdcd;\n" \
" text-align: center;\n" \
"}\n" \
"\n" \
"td.intf_list_rxp {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" border-left: 1px dashed #cdcdcd;\n" \
" text-align: center;\n" \
"}\n" \
"\n" \
"td.intf_list_tx {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" border-left: 1px dashed #cdcdcd;\n" \
" text-align: center;\n" \
"}\n" \
"\n" \
"td.intf_list_txp {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" border-left: 1px dashed #cdcdcd;\n" \
" text-align: center;\n" \
"}\n" \
" \n" \
"table.details {\n" \
" border: 1px solid #cdcdcd;\n" \
" width: 100%%;\n" \
" border-spacing: 1px;\n" \
" background: #f8f8f8;\n" \
" margin-top: 10px;\n" \
"}\n" \
"\n" \
"tr.details_hdr {\n" \
" font-weight: bold;\n" \
"}\n" \
"\n" \
"th.details_hdr_name {\n" \
" text-align: left;\n" \
" padding-left: 20px;\n" \
"}\n" \
"\n" \
"td.details_name {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" padding-left: 20px;\n" \
"}\n" \
"\n" \
"td.details_rx {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" border-left: 1px dashed #cdcdcd;\n" \
" text-align: center;\n" \
"}\n" \
"\n" \
"td.details_tx {\n" \
" border-top: 1px dashed #cdcdcd;\n" \
" border-left: 1px dashed #cdcdcd;\n" \
" text-align: center;\n" \
"}\n" \
"\n" \
"table.overall {\n" \
" width: 750px;\n" \
" border-spacing: 0px;\n" \
"}\n" \
"\n" \
"td.header {\n" \
" background: #b8c4db;\n" \
" text-align: center;\n" \
" border: 1px solid #000;\n" \
"}\n" \
"\n" \
"td.left_col {\n" \
" background: #eeeeee;\n" \
" padding-left: 10px;\n" \
" border-left: 1px solid #cdcdcd;\n" \
" border-right: 1px solid #cdcdcd;\n" \
" border-bottom: 1px solid #cdcdcd;\n" \
" vertical-align: top;\n" \
" width: 130px;\n" \
"}\n" \
"\n" \
"td.right_col {\n" \
" background: #fff;\n" \
" padding: 20px;\n" \
" padding-top: 10px;\n" \
" border-bottom: 1px solid #cdcdcd;\n" \
" border-right: 1px solid #cdcdcd;\n" \
" vertical-align: top;\n" \
"}\n" \
"\n" \
"td.fg {\n" \
" background: #6a94b7;\n" \
"}\n" \
"\n" \
"td.noise {\n" \
" background: #9fc5db;\n" \
"}\n" \
"\n" \
"td.bg {\n" \
" background: #eeeeee;\n" \
"}\n" \
"\n" \
"td.unknown {\n" \
" background: #f69191;\n" \
"}\n" \
"\n" \
"table.graph {\n" \
" border: 1px solid #cdcdcd;\n" \
" width: 100%%;\n" \
" border-spacing: 1px;\n" \
" background: #f8f8f8;\n" \
" margin-top: 10px;\n" \
" table-layout: fixed;\n" \
" empty-cells: show;\n" \
" padding-right: 5px;\n" \
" padding-bottom: 5px;\n" \
"}\n" \
"\n" \
"th.graph_hdr {\n" \
"}\n" \
"\n" \
"th.scale_hdr {\n" \
" text-align: right;\n" \
" width: 60px;\n" \
"}\n" \
"\n" \
"td.scale {\n" \
" text-align: right;\n" \
" vertical-align: top;\n" \
"}\n" \
"\n" \
"tr.graph_row {\n" \
" line-height: 7px;\n" \
"}\n" \
"\n" \
"ul.node_list {\n" \
" padding-left: 15px;\n" \
"}\n" \
"\n" \
"ul.node_intf_list {\n" \
" padding-left: 8px;\n" \
"}\n" \
"\n" \
"ul.sub_intf {\n" \
" padding-left: 8px;\n" \
"}\n" \
"\n" \
"tr.legend {\n" \
" line-height: 10px;\n" \
"}\n" \
"\n" \
"td.legend {\n" \
" font-size: 8pt;\n" \
"}\n", PACKAGE_STRING);
fclose(f);
}
struct bxd {
FILE *fd;
node_t *node;
int clevel;
};
static void list_add_child(item_t *it, void *arg)
{
struct bxd *x = (struct bxd *) arg;
fprintf(x->fd, "<li><a class=\"a_intf_link\" href=\"%s.%d.bytes.s.html\">%s</a>\n",
x->node->n_name, it->i_index, it->i_name);
if (it->i_flags & ITEM_FLAG_HAS_CHILDS) {
fprintf(x->fd, "<ul class=\"sub_intf\">");
foreach_child(x->node, it, list_add_child, arg);
fprintf(x->fd, "</ul>\n");
}
fprintf(x->fd, "</li>\n");
}
static void list_add_item(item_t *it, void *arg)
{
struct bxd *x = (struct bxd *) arg;
if (it->i_flags & ITEM_FLAG_IS_CHILD)
return;
fprintf(x->fd, "<li><a class=\"a_intf_link\" href=\"%s.%d.bytes.s.html\">%s</a>\n",
x->node->n_name, it->i_index, it->i_name);
if (it->i_flags & ITEM_FLAG_HAS_CHILDS) {
fprintf(x->fd, "<ul class=\"sub_intf\">");
foreach_child(x->node, it, list_add_child, arg);
fprintf(x->fd, "</ul>\n");
}
fprintf(x->fd, "</li>\n");
}
static void list_add(node_t *node, void *arg)
{
FILE *fd = (FILE *) arg;
struct bxd x = {
.fd = fd,
.node = node,
};
fprintf(fd, "<li><a class=\"a_node_link\" href=\"%s.html\">%s%s</a>\n",
node->n_name, node->n_name, !c_hide_item ? ":" : "");
if (!c_hide_item) {
fprintf(fd, "<ul class=\"node_intf_list\">\n");
foreach_item(node, list_add_item, &x);
fprintf(fd, "</ul>\n");
}
fprintf(fd, "</li>\n");
}
static void write_header(FILE *fd, const char *title)
{
fprintf(fd,
"<!DOCTYPE html PUBLIC \"-//W3c/DTD XHTML 1.0 Strict//EN\" \n" \
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" \
"<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\"\n" \
" xml:lang=\"en\">\n" \
"<head>\n" \
"<title>%s</title>\n" \
"<link rel=\"stylesheet\" type=\"text/css\" href=\"layout.css\" />\n" \
"</head>\n" \
"<body>\n" \
"<table class=\"overall\" align=\"center\">\n" \
"<tr>\n" \
"<td colspan=\"2\" class=\"header\">\n" \
"<p class=\"banner\">%s</p>\n" \
"</td>\n" \
"</tr><tr>\n" \
"<td class=\"left_col\">\n" \
"<ul class=\"node_list\">\n",
title, c_title);
foreach_node(list_add, fd);
fprintf(fd,
"</ul></td>\n" \
"<td class=\"right_col\">\n");
}
static void write_footer(FILE *fd)
{
time_t now = time(0);
fprintf(fd,
"</td>\n" \
"</tr>\n" \
"<tr>\n" \
"<td colspan=\"2\" class=\"footer\">\n" \
"<p class=\"p_footer\">Updated: %s</p>\n" \
"</td>\n" \
"</tr>\n" \
"</table>\n" \
"</body>\n" \
"</html>\n",
asctime(localtime(&now)));
}
struct html_xdata {
FILE *fd;
node_t *node;
};
static void write_interface_entry(FILE *fd, item_t *intf, node_t *node)
{
stat_attr_t *bytes, *packets;
int i, rxprec, txprec;
double rx, tx;
char *rx_u, *tx_u;
bytes = lookup_attr(intf, intf->i_major_attr);
packets = lookup_attr(intf, intf->i_minor_attr);
if (!bytes || !packets)
return;
rx = cancel_down(attr_get_rx_rate(bytes), bytes->a_unit, &rx_u, &rxprec);
tx = cancel_down(attr_get_tx_rate(bytes), bytes->a_unit, &tx_u, &txprec);
fprintf(fd,
"<tr class=\"intf_list_row\">\n" \
"<td class=\"intf_list_nr\">%d</td>\n" \
"<td class=\"intf_list_name\">",
intf->i_index);
for (i = 0; i < intf->i_level; i++)
fprintf(fd, " ");
fprintf(fd,
"<a class=\"a_intf\" href=\"%s.%d.bytes.s.html\">%s</a></td>\n" \
"<td class=\"intf_list_rx\">%.*f %s</td>\n" \
"<td class=\"intf_list_rxp\">%u</td>\n" \
"<td class=\"intf_list_tx\">%.*f %s</td>\n" \
"<td class=\"intf_list_txp\">%u</td>\n" \
"</tr>\n",
node->n_name, intf->i_index, intf->i_name,
rxprec, rx, rx_u, attr_get_rx_rate(packets),
txprec, tx, tx_u, attr_get_tx_rate(packets));
}
static void handle_child(item_t *intf, void *arg)
{
struct html_xdata *x = arg;
write_interface_entry(x->fd, intf, x->node);
foreach_child(x->node, intf, handle_child, x);
}
static void add_to_interace_list(FILE *fd, node_t *node, item_t *intf)
{
struct html_xdata x = {
.fd = fd,
.node = node,
};
if (intf->i_parent)
return;
write_interface_entry(fd, intf, node);
foreach_child(node, intf, handle_child, &x);
}
static void write_interface_list(FILE *fd, node_t *node)
{
int i;
fprintf(fd,
"<p class=\"node_title\">%s</p>\n" \
"<table class=\"intf_list\">\n" \
"<tr class=\"intf_list_hdr\">\n" \
"<th class=\"intf_list_hdr_classx\">#</th>\n" \
"<th class=\"intf_list_hdr_name\">Name</th>\n" \
"<th class=\"intf_list_hdr_rx\">RX</th>\n" \
"<th class=\"intf_list_hdr_rxp\">#</th>\n" \
"<th class=\"intf_list_hdr_tx\">TX</th>\n" \
"<th class=\"intf_list_hdr_txp\">#</th>\n" \
"</tr>",
node->n_name);
for (i = 0; i < node->n_nitems; i++)
if (node->n_items[i].i_name[0])
add_to_interace_list(fd, node, &node->n_items[i]);
fprintf(fd,
"</table>\n");
}
static void write_graph(FILE *fd, node_t *node, item_t *intf,
stat_attr_hist_t *h, hist_elem_t *e, const char *x_unit)
{
int i, w, rem;
graph_t *g = create_graph(e, c_graph_height, h->a_unit);
fprintf(fd,
"<p class=\"selection\">");
for (i = 0; i < ATTR_HASH_MAX; i++) {
stat_attr_t *a;
for (a = intf->i_attrs[i]; a; a = a->a_next) {
if (!(a->a_flags & ATTR_FLAG_HISTORY))
continue;
fprintf(fd,
"[<a class=\"a_selection\" href=\"%s.%d.%s.s.html\">%s</a>] ",
node->n_name, intf->i_index, type2name(a->a_type),
type2desc(a->a_type));
}
}
fprintf(fd,
"</p><p class=\"selection\">[");
if (get_read_interval() != 1.0f)
fprintf(fd,
"<a class=\"a_selection\" href=\"%s.%d.%s.r.html\">Read interval</a> ] [",
node->n_name, intf->i_index, type2name(h->a_type));
fprintf(fd,
"<a class=\"a_selection\" href=\"%s.%d.%s.s.html\">Seconds</a>] [" \
"<a class=\"a_selection\" href=\"%s.%d.%s.m.html\">Minutes</a>] [" \
"<a class=\"a_selection\" href=\"%s.%d.%s.h.html\">Hours</a>] [" \
"<a class=\"a_selection\" href=\"%s.%d.%s.d.html\">Days</a>]</p>\n",
node->n_name, intf->i_index, type2name(h->a_type),
node->n_name, intf->i_index, type2name(h->a_type),
node->n_name, intf->i_index, type2name(h->a_type),
node->n_name, intf->i_index, type2name(h->a_type));
fprintf(fd, "<table class=\"graph\">\n");
fprintf(fd, "<tr><th class=\"scale_hdr\">%s</th>" \
"<th class=\"graph_hdr\" colspan=\"61\">RX %s</th></tr>\n",
g->g_tx.t_y_unit, type2desc(h->a_type));
for (rem = 1, w = (c_graph_height - 1); w >= 0; w--) {
char *p;
fprintf(fd, "<tr class=\"graph_row\">\n");
if (--rem == 0) {
fprintf(fd, "<td class=\"scale\" rowspan=\"%d\">%8.2f</td>",
c_rowspan, g->g_rx.t_y_scale[w]);
rem = c_rowspan;
}
for (p = (char *) (g->g_rx.t_data + (w * (HISTORY_SIZE + 1))); *p; p++) {
if (*p == get_fg_char())
fprintf(fd, "<td class=\"fg\"></td>");
else if (*p == get_noise_char())
fprintf(fd, "<td class=\"noise\"></td>");
else if (*p == get_unk_char())
fprintf(fd, "<td class=\"unknown\"></td>");
else
fprintf(fd, "<td class=\"bg\"></td>");
}
fprintf(fd, "<td> </td></tr>");
}
fprintf(fd, "<tr><td> </td>" \
"<td colspan=\"5\">1</td>" \
"<td colspan=\"5\">5</td>" \
"<td colspan=\"5\">10</td>" \
"<td colspan=\"5\">15</td>" \
"<td colspan=\"5\">20</td>" \
"<td colspan=\"5\">25</td>" \
"<td colspan=\"5\">30</td>" \
"<td colspan=\"5\">35</td>" \
"<td colspan=\"5\">40</td>" \
"<td colspan=\"5\">45</td>" \
"<td colspan=\"5\">50</td>" \
"<td colspan=\"5\">55</td>" \
"<td>%s</td></tr>\n" \
" <tr class=\"legend\">\n" \
" <td colspan=\"7\"></td>\n" \
" <td colspan=\"2\" class=\"fg\"> </td>\n" \
" <td colspan=\"15\" class=\"legend\">Consumed</td>\n" \
" <td colspan=\"2\" class=\"noise\"> </td>\n" \
" <td colspan=\"15\" class=\"legend\">Noise</td>\n" \
" <td colspan=\"2\" class=\"unknown\"> </td>\n" \
" <td colspan=\"15\" class=\"legend\">No input data</td>\n" \
" <td colspan=\"4\"></td>\n" \
" </tr>\n" \
"</table>\n", x_unit);
fprintf(fd, "<table class=\"graph\">\n");
fprintf(fd, "<tr><th class=\"scale_hdr\">%s</th>" \
"<th class=\"graph_hdr\" colspan=\"61\">TX %s</th></tr>\n",
g->g_tx.t_y_unit, type2desc(h->a_type));
for (rem = 1, w = (c_graph_height - 1); w >= 0; w--) {
char *p;
fprintf(fd, "<tr class=\"graph_row\">\n");
if (--rem == 0) {
fprintf(fd, "<td class=\"scale\" rowspan=\"%d\">%8.2f</td>",
c_rowspan, g->g_tx.t_y_scale[w]);
rem = c_rowspan;
}
for (p = (char *) (g->g_tx.t_data + (w * (HISTORY_SIZE + 1))); *p; p++) {
if (*p == get_fg_char())
fprintf(fd, "<td class=\"fg\"></td>");
else if (*p == get_noise_char())
fprintf(fd, "<td class=\"noise\"></td>");
else
fprintf(fd, "<td class=\"bg\"></td>");
}
fprintf(fd, "<td> </td></tr>");
}
fprintf(fd, "<tr><td> </td>" \
"<td colspan=\"5\">1</td>" \
"<td colspan=\"5\">5</td>" \
"<td colspan=\"5\">10</td>" \
"<td colspan=\"5\">15</td>" \
"<td colspan=\"5\">20</td>" \
"<td colspan=\"5\">25</td>" \
"<td colspan=\"5\">30</td>" \
"<td colspan=\"5\">35</td>" \
"<td colspan=\"5\">40</td>" \
"<td colspan=\"5\">45</td>" \
"<td colspan=\"5\">50</td>" \
"<td colspan=\"5\">55</td>" \
"<td>%s</td></tr>\n" \
" <tr class=\"legend\">\n" \
" <td colspan=\"7\"></td>\n" \
" <td colspan=\"2\" class=\"fg\"> </td>\n" \
" <td colspan=\"15\" class=\"legend\">Consumed</td>\n" \
" <td colspan=\"2\" class=\"noise\"> </td>\n" \
" <td colspan=\"15\" class=\"legend\">Noise</td>\n" \
" <td colspan=\"2\" class=\"unknown\"> </td>\n" \
" <td colspan=\"15\" class=\"legend\">No input data</td>\n" \
" <td colspan=\"4\"></td>\n" \
" </tr>\n" \
"</table>\n", x_unit);
free_graph(g);
}
static void print_attr_detail(stat_attr_t *a, void *arg)
{
double rx, tx;
char *rx_u, *tx_u;
int rxprec, txprec;
FILE *fd = (FILE *) arg;
rx = cancel_down(attr_get_rx(a), a->a_unit, &rx_u, &rxprec);
tx = cancel_down(attr_get_tx(a), a->a_unit, &tx_u, &txprec);
fprintf(fd,
"<tr class=\"details\">\n" \
"<td class=\"details_name\">%s</td>\n" \
"<td class=\"details_rx\">%.*f %s</td>\n" \
"<td class=\"details_tx\">%.*f %s</td>\n" \
"</tr>\n",
type2desc(a->a_type), rxprec, rx, rx_u, txprec, tx, tx_u);
}
static void write_details(FILE *fd, item_t *intf)
{
fprintf(fd,
"<table class=\"details\">\n" \
"<tr class=\"details_hdr\">\n" \
"<th class=\"details_hdr_name\">Details</th>\n" \
"<th class=\"details_hdr_rx\">RX</th>\n" \
"<th class=\"details_hdr_tx\">TX</th>\n" \
"</tr>\n");
foreach_attr(intf, print_attr_detail, (void *) fd);
fprintf(fd, "</table>\n");
}
static void __write_per_item(item_t *intf, node_t *node, stat_attr_hist_t *h,
hist_elem_t *e, char *x_unit)
{
char outf[FILENAME_MAX];
char title[256];
FILE *fd;
snprintf(outf, sizeof(outf), "%s/%s.%d.%s.%s.html",
c_path, node->n_name, intf->i_index, type2name(h->a_type), x_unit);
snprintf(title, sizeof(title), "%s on %s - %s/%s",
intf->i_name, node->n_name, type2desc(h->a_type), x_unit);
fd = open_file(outf);
write_header(fd, title);
write_interface_list(fd, node);
fprintf(fd, "<p class=\"title\">%s</p>\n", intf->i_name);
write_graph(fd, node, intf, h, e, x_unit);
write_details(fd, intf);
write_footer(fd);
fclose(fd);
}
struct xdata {
item_t *i;
node_t *n;
};
static void write_attr_graph(stat_attr_t *a, void *arg)
{
node_t *node = ((struct xdata *) arg)->n;
item_t *intf = ((struct xdata *) arg)->i;
stat_attr_hist_t *h = (stat_attr_hist_t *) a;
if (!(a->a_flags & ATTR_FLAG_HISTORY))
return;
if (get_read_interval() != 1.0f)
__write_per_item(intf, node, h, &h->a_hist.h_read, "r");
__write_per_item(intf, node, h, &h->a_hist.h_sec, "s");
__write_per_item(intf, node, h, &h->a_hist.h_min, "m");
__write_per_item(intf, node, h, &h->a_hist.h_hour, "h");
__write_per_item(intf, node, h, &h->a_hist.h_day, "d");
}
static void write_per_item(item_t *intf, void *arg)
{
struct xdata x = {
.i = intf,
.n = (node_t *) arg
};
foreach_attr(intf, &write_attr_graph, &x);
}
static void write_per_node(node_t *node, void *arg)
{
char outf[FILENAME_MAX];
FILE *fd;
snprintf(outf, sizeof(outf), "%s/%s.html", c_path, node->n_name);
fd = open_file(outf);
write_header(fd, node->n_name);
write_interface_list(fd, node);
write_footer(fd);
fclose(fd);
foreach_item(node, write_per_item, (void *) node);
}
void html_draw(void)
{
static int rem = 1;
char outf[FILENAME_MAX];
FILE *fd;
if (--rem)
return;
else
rem = c_update_interval;
umask(0133);
write_css(c_path);
foreach_node(write_per_node, NULL);
snprintf(outf, sizeof(outf), "%s/index.html", c_path);
fd = open_file(outf);
write_header(fd, c_title);
write_footer(fd);
fclose(fd);
}
static void print_module_help(void)
{
printf(
"HTML - HTML Output\n" \
"\n" \
" Lightweight HTML output with CSS configuration.\n" \
" Author: Thomas Graf <tgraf@suug.ch>\n" \
"\n" \
" Options:\n" \
" path=PATH Output directory\n" \
" interval=SEC Update interval in seconds (default: 1)\n" \
" rowspan=NUM Summarize NUM rows into into a single scale step\n" \
" height=NUM Height of graphical statistics (default: 6)\n" \
" hideitems Hide interfaces in node list\n" \
" title=STRING Title of output\n");
}
static void html_set_opts(tv_t *attrs)
{
while (attrs) {
if (!strcasecmp(attrs->type, "path") && attrs->value)
c_path = attrs->value;
else if (!strcasecmp(attrs->type, "height") && attrs->value)
c_graph_height = strtol(attrs->value, NULL, 0);
else if (!strcasecmp(attrs->type, "interval") && attrs->value)
c_update_interval = strtol(attrs->value, NULL, 0);
else if (!strcasecmp(attrs->type, "rowspan") && attrs->value)
c_rowspan = strtol(attrs->value, NULL, 0);
else if (!strcasecmp(attrs->type, "title") && attrs->value)
c_title = attrs->value;
else if (!strcasecmp(attrs->type, "hideitems"))
c_hide_item = 1;
else if (!strcasecmp(attrs->type, "help")) {
print_module_help();
exit(0);
}
attrs = attrs->next;
}
}
static int html_probe(void)
{
if (NULL == c_path)
quit("You must specify a path (-O html:path=DIR)\n");
return 1;
}
static struct output_module html_ops = {
.om_name = "html",
.om_draw = html_draw,
.om_set_opts = html_set_opts,
.om_probe = html_probe,
};
static void __init html_init(void)
{
register_secondary_output_module(&html_ops);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>