2 * copyright (c) 2006-2008 Emil Mikulic.
4 * graph_db.c: round robin database for graph data
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
10 #include <sys/types.h>
19 #include "html.h" /* FIXME: should be pushed into a .c file? */
25 #include <string.h> /* for memcpy() */
27 #define GRAPH_WIDTH "320"
28 #define GRAPH_HEIGHT "200"
30 extern const char *interface
;
34 unsigned int offset
; /* i.e. seconds start at 0, days start at 1 */
35 unsigned int pos
, num_bars
;
37 unsigned int bar_secs
; /* one bar represents <n> seconds */
41 graph_secs
= {NULL
, NULL
, 0, 0, 60, "seconds", 1},
42 graph_mins
= {NULL
, NULL
, 0, 0, 60, "minutes", 60},
43 graph_hrs
= {NULL
, NULL
, 0, 0, 24, "hours", 3600},
44 graph_days
= {NULL
, NULL
, 1, 0, 31, "days", 86400};
46 static struct graph
*graph_db
[] = {
47 &graph_secs
, &graph_mins
, &graph_hrs
, &graph_days
50 static unsigned int graph_db_size
= sizeof(graph_db
)/sizeof(*graph_db
);
52 static time_t start_time
, last_time
;
58 for (i
=0; i
<graph_db_size
; i
++) {
59 graph_db
[i
]->in
= xmalloc(sizeof(uint64_t) * graph_db
[i
]->num_bars
);
60 graph_db
[i
]->out
= xmalloc(sizeof(uint64_t) * graph_db
[i
]->num_bars
);
62 start_time
= time(NULL
);
67 zero_graph(struct graph
*g
)
69 memset(g
->in
, 0, sizeof(uint64_t) * g
->num_bars
);
70 memset(g
->out
, 0, sizeof(uint64_t) * g
->num_bars
);
77 for (i
=0; i
<graph_db_size
; i
++)
78 zero_graph(graph_db
[i
]);
86 for (i
=0; i
<graph_db_size
; i
++) {
87 free(graph_db
[i
]->in
);
88 free(graph_db
[i
]->out
);
93 graph_acct(uint64_t amount
, enum graph_dir dir
)
96 for (i
=0; i
<graph_db_size
; i
++)
98 case GRAPH_IN
: graph_db
[i
]->in
[ graph_db
[i
]->pos
] += amount
; break;
99 case GRAPH_OUT
: graph_db
[i
]->out
[ graph_db
[i
]->pos
] += amount
; break;
100 default: errx(1, "unknown graph_dir in graph_acct: %d", dir
);
104 /* Advance a graph: advance the pos, zeroing out bars as we move. */
106 advance(struct graph
*g
, const unsigned int pos
)
109 return; /* didn't need to advance */
111 g
->pos
= (g
->pos
+ 1) % g
->num_bars
;
112 g
->in
[g
->pos
] = g
->out
[g
->pos
] = 0;
113 } while (g
->pos
!= pos
);
116 /* Rotate a graph: rotate all bars so that the bar at the current pos is moved
117 * to the newly given pos. This is non-destructive. */
119 rotate(struct graph
*g
, const unsigned int pos
)
126 return; /* nothing to rotate */
128 size
= sizeof(*tmp
) * g
->num_bars
;
130 ofs
= g
->num_bars
+ pos
- g
->pos
;
132 for (i
=0; i
<g
->num_bars
; i
++)
133 tmp
[ (i
+ofs
) % g
->num_bars
] = g
->in
[i
];
134 memcpy(g
->in
, tmp
, size
);
136 for (i
=0; i
<g
->num_bars
; i
++)
137 tmp
[ (i
+ofs
) % g
->num_bars
] = g
->out
[i
];
138 memcpy(g
->out
, tmp
, size
);
141 assert(pos
== ( (g
->pos
+ ofs
) % g
->num_bars
));
146 graph_resync(const time_t new_time
)
150 * If time went backwards, we assume that real time is continuous and that
151 * the time adjustment should only affect display. i.e., if we have:
153 * second 15: 12 bytes
154 * second 16: 345 bytes
155 * second 17: <-- current pos
157 * and time goes backwards to second 8, we will shift the graph around to
161 * second 7: 345 bytes
162 * second 8: <-- current pos
164 * Note that we don't make any corrections for time being stepped forward.
165 * We rely on graph advancement to happen at the correct real time to
166 * account for, for example, bandwidth used per day.
168 assert(new_time
< last_time
);
170 tm
= localtime(&new_time
);
171 if (tm
->tm_sec
== 60)
172 tm
->tm_sec
= 59; /* mis-handle leap seconds */
174 rotate(&graph_secs
, tm
->tm_sec
);
175 rotate(&graph_mins
, tm
->tm_min
);
176 rotate(&graph_hrs
, tm
->tm_hour
);
177 rotate(&graph_days
, tm
->tm_mday
- 1);
179 last_time
= new_time
;
191 if (last_time
== 0) {
192 verbosef("first rotate");
195 if (tm
->tm_sec
== 60)
196 tm
->tm_sec
= 59; /* mis-handle leap seconds */
198 graph_secs
.pos
= tm
->tm_sec
;
199 graph_mins
.pos
= tm
->tm_min
;
200 graph_hrs
.pos
= tm
->tm_hour
;
201 graph_days
.pos
= tm
->tm_mday
- 1;
206 return; /* superfluous rotate */
209 verbosef("time went backwards! (from %u to %u, offset is %d)",
210 (unsigned int)last_time
, (unsigned int)t
, (int)(t
- last_time
));
215 /* else, normal rotation */
219 if (tm
->tm_sec
== 60)
220 tm
->tm_sec
= 59; /* mis-handle leap seconds */
222 /* zero out graphs which have been completely rotated through */
223 for (i
=0; i
<graph_db_size
; i
++)
224 if (td
>= (int)(graph_db
[i
]->num_bars
* graph_db
[i
]->bar_secs
))
225 zero_graph(graph_db
[i
]);
227 /* advance the current position, zeroing up to it */
228 advance(&graph_secs
, tm
->tm_sec
);
229 advance(&graph_mins
, tm
->tm_min
);
230 advance(&graph_hrs
, tm
->tm_hour
);
231 advance(&graph_days
, tm
->tm_mday
- 1);
234 /* ---------------------------------------------------------------------------
235 * Database Import: Grab graphs from a file provided by the caller.
237 * This function will retrieve the data sans the header. We expect the caller
238 * to have validated the header of the segment, and left the file position at
239 * the start of the data.
242 graph_import(const int fd
)
247 if (!read64(fd
, &last
)) return 0;
248 last_time
= (time_t)last
;
250 for (i
=0; i
<graph_db_size
; i
++) {
251 unsigned char num_bars
, pos
;
252 unsigned int filepos
= xtell(fd
);
254 if (!read8(fd
, &num_bars
)) return 0;
255 if (!read8(fd
, &pos
)) return 0;
257 verbosef("at file pos %u, importing graph with %u bars",
258 filepos
, (unsigned int)num_bars
);
260 if (pos
>= num_bars
) {
261 warn("pos is %u, should be < num_bars which is %u",
262 (unsigned int)pos
, (unsigned int)num_bars
);
266 if (graph_db
[i
]->num_bars
!= num_bars
) {
267 warn("num_bars is %u, expecting %u",
268 (unsigned int)num_bars
, graph_db
[i
]->num_bars
);
272 graph_db
[i
]->pos
= pos
;
273 for (j
=0; j
<num_bars
; j
++) {
274 if (!read64(fd
, &(graph_db
[i
]->in
[j
]))) return 0;
275 if (!read64(fd
, &(graph_db
[i
]->out
[j
]))) return 0;
282 /* ---------------------------------------------------------------------------
283 * Database Export: Dump hosts_db into a file provided by the caller.
284 * The caller is responsible for writing out the header first.
287 graph_export(const int fd
)
291 if (!write64(fd
, (uint64_t)last_time
)) return 0;
292 for (i
=0; i
<graph_db_size
; i
++) {
293 if (!write8(fd
, graph_db
[i
]->num_bars
)) return 0;
294 if (!write8(fd
, graph_db
[i
]->pos
)) return 0;
296 for (j
=0; j
<graph_db
[i
]->num_bars
; j
++) {
297 if (!write64(fd
, graph_db
[i
]->in
[j
])) return 0;
298 if (!write64(fd
, graph_db
[i
]->out
[j
])) return 0;
304 /* ---------------------------------------------------------------------------
305 * Web interface: front page!
308 html_front_page(void)
310 struct str
*buf
, *rf
;
312 char start_when
[100];
315 str_append(buf
, html_header_1
);
316 str_appendf(buf
, "<title>" PACKAGE_STRING
" : graphs (%s)</title>\n",
318 str_append(buf
, "<script src=\"graph.js\" type=\"text/javascript\">"
320 str_append(buf
, html_header_2
);
321 str_appendf(buf
, "<h2 class=\"pageheader\">Graphs (%s)</h2>\n", interface
);
323 str_append(buf
, "<p>\n");
325 str_append(buf
, "<b>Running for</b> <span id=\"rf\">");
326 rf
= length_of_time(now
- start_time
);
327 /* FIXME: use a more monotonic clock perhaps? */
328 str_appendstr(buf
, rf
);
330 str_append(buf
, "</span>");
332 if (strftime(start_when
, sizeof(start_when
),
333 "%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_time
)) != 0)
334 str_appendf(buf
, "<b>, since</b> %s", start_when
);
336 str_appendf(buf
,"<b>.</b><br/>\n"
337 "<b>Total</b> <span id=\"tb\">%'qu</span> <b>bytes, "
338 "in</b> <span id=\"tp\">%'qu</span> <b>packets.</b> "
339 "(<span id=\"pc\">%'u</span> <b>captured,</b> "
340 "<span id=\"pd\">%'u</span> <b>dropped)</b><br/>\n"
344 pkts_recv
, pkts_drop
);
347 "<div id=\"graphs\">\n"
348 "Graphs require JavaScript.\n"
349 "<script type=\"text/javascript\">\n"
351 "var graph_width = " GRAPH_WIDTH
";\n"
352 "var graph_height = " GRAPH_HEIGHT
";\n"
354 "var graphs_uri = \"/graphs.xml\";\n"
358 for (i
=0; i
<graph_db_size
; i
++)
362 "title:\"last %u %s\", "
365 i
, graph_db
[i
]->unit
, graph_db
[i
]->num_bars
, graph_db
[i
]->unit
,
366 graph_db
[i
]->bar_secs
, (i
< graph_db_size
-1) ? "," : "");
367 /* trailing comma breaks on IE, makes the array one element longer */
371 "window.onload = graphs_init;\n"
377 str_append(buf
, html_footer
);
381 /* ---------------------------------------------------------------------------
382 * Web interface: graphs.xml
388 struct str
*buf
= str_make(), *rf
;
390 str_appendf(buf
, "<graphs tp=\"%qu\" tb=\"%qu\" pc=\"%u\" pd=\"%u\" rf=\"",
391 total_packets
, total_bytes
, pkts_recv
, pkts_drop
);
392 rf
= length_of_time(now
- start_time
);
393 str_appendstr(buf
, rf
);
395 str_append(buf
, "\">\n");
397 for (i
=0; i
<graph_db_size
; i
++) {
398 const struct graph
*g
= graph_db
[i
];
400 str_appendf(buf
, "<%s>\n", g
->unit
);
403 j
= (j
+ 1) % g
->num_bars
;
404 /* <element pos="" in="" out=""/> */
405 str_appendf(buf
, "<e p=\"%u\" i=\"%qu\" o=\"%qu\"/>\n",
406 g
->offset
+ j
, g
->in
[j
], g
->out
[j
]);
407 } while (j
!= g
->pos
);
408 str_appendf(buf
, "</%s>\n", g
->unit
);
410 str_append(buf
, "</graphs>\n");
414 /* vim:set ts=3 sw=3 tw=78 expandtab: */