4485d459bd81ff69a8433937d2c9caa311fc66ac
2 * copyright (c) 2006-2014 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>
25 #include <string.h> /* for memcpy() */
28 #define GRAPH_WIDTH "320"
29 #define GRAPH_HEIGHT "200"
33 unsigned int offset
; /* i.e. seconds start at 0, days start at 1 */
34 unsigned int pos
, num_bars
;
36 unsigned int bar_secs
; /* one bar represents <n> seconds */
40 graph_secs
= {NULL
, NULL
, 0, 0, 60, "seconds", 1},
41 graph_mins
= {NULL
, NULL
, 0, 0, 60, "minutes", 60},
42 graph_hrs
= {NULL
, NULL
, 0, 0, 24, "hours", 3600},
43 graph_days
= {NULL
, NULL
, 1, 0, 31, "days", 86400};
45 static struct graph
*graph_db
[] = {
46 &graph_secs
, &graph_mins
, &graph_hrs
, &graph_days
49 static unsigned int graph_db_size
= sizeof(graph_db
)/sizeof(*graph_db
);
50 static time_t start_mono
, start_real
, last_real
;
52 void graph_init(void) {
54 for (i
=0; i
<graph_db_size
; i
++) {
55 graph_db
[i
]->in
= xmalloc(sizeof(uint64_t) * graph_db
[i
]->num_bars
);
56 graph_db
[i
]->out
= xmalloc(sizeof(uint64_t) * graph_db
[i
]->num_bars
);
58 start_mono
= now_mono();
59 start_real
= now_real();
64 static void zero_graph(struct graph
*g
) {
65 memset(g
->in
, 0, sizeof(uint64_t) * g
->num_bars
);
66 memset(g
->out
, 0, sizeof(uint64_t) * g
->num_bars
);
69 void graph_reset(void) {
72 for (i
=0; i
<graph_db_size
; i
++)
73 zero_graph(graph_db
[i
]);
77 void graph_free(void) {
80 for (i
=0; i
<graph_db_size
; i
++) {
81 free(graph_db
[i
]->in
);
82 free(graph_db
[i
]->out
);
86 void graph_acct(uint64_t amount
, enum graph_dir dir
) {
88 for (i
=0; i
<graph_db_size
; i
++)
89 if (dir
== GRAPH_IN
) {
90 graph_db
[i
]->in
[ graph_db
[i
]->pos
] += amount
;
92 assert(dir
== GRAPH_OUT
);
93 graph_db
[i
]->out
[ graph_db
[i
]->pos
] += amount
;
97 /* Advance a graph: advance the pos, zeroing out bars as we move. */
98 static void advance(struct graph
*g
, const unsigned int pos
) {
100 return; /* didn't need to advance */
102 g
->pos
= (g
->pos
+ 1) % g
->num_bars
;
103 g
->in
[g
->pos
] = g
->out
[g
->pos
] = 0;
104 } while (g
->pos
!= pos
);
107 /* Rotate a graph: rotate all bars so that the bar at the current pos is moved
108 * to the newly given pos.
110 static void rotate(struct graph
*g
, const unsigned int pos
) {
116 return; /* nothing to rotate */
118 size
= sizeof(*tmp
) * g
->num_bars
;
120 ofs
= g
->num_bars
+ pos
- g
->pos
;
122 for (i
=0; i
<g
->num_bars
; i
++)
123 tmp
[ (i
+ofs
) % g
->num_bars
] = g
->in
[i
];
124 memcpy(g
->in
, tmp
, size
);
126 for (i
=0; i
<g
->num_bars
; i
++)
127 tmp
[ (i
+ofs
) % g
->num_bars
] = g
->out
[i
];
128 memcpy(g
->out
, tmp
, size
);
131 assert(g
->num_bars
> 0);
132 assert(pos
== ( (g
->pos
+ ofs
) % g
->num_bars
));
136 static void graph_resync(const time_t new_real
) {
139 * If real time went backwards, we assume that the time adjustment should
140 * only affect display. i.e., if we have:
142 * second 15: 12 bytes
143 * second 16: 345 bytes
144 * second 17: <-- current pos
146 * and time goes backwards to second 8, we will shift the graph around to
150 * second 7: 345 bytes
151 * second 8: <-- current pos
153 * We don't make any corrections for time being stepped forward,
154 * it's treated as though there was no traffic during that time.
156 * We rely on graph advancement to happen at the correct real time to
157 * account for, for example, bandwidth used per day.
159 assert(new_real
< last_real
);
161 tm
= localtime(&new_real
);
162 if (tm
->tm_sec
== 60)
163 tm
->tm_sec
= 59; /* mis-handle leap seconds */
165 rotate(&graph_secs
, tm
->tm_sec
);
166 rotate(&graph_mins
, tm
->tm_min
);
167 rotate(&graph_hrs
, tm
->tm_hour
);
168 rotate(&graph_days
, tm
->tm_mday
- 1);
170 last_real
= new_real
;
173 void graph_rotate(void) {
181 if (last_real
== 0) {
182 verbosef("first rotate");
185 graph_secs
.pos
= tm
->tm_sec
;
186 graph_mins
.pos
= tm
->tm_min
;
187 graph_hrs
.pos
= tm
->tm_hour
;
188 graph_days
.pos
= tm
->tm_mday
- 1;
193 return; /* time has not advanced a full second, don't rotate */
196 verbosef("graph_db: realtime went backwards! "
197 "(from %ld to %ld, offset is %ld)",
203 /* else, normal rotation */
207 /* zero out graphs which have been completely rotated through */
208 for (i
=0; i
<graph_db_size
; i
++)
209 if (td
>= (int)(graph_db
[i
]->num_bars
* graph_db
[i
]->bar_secs
))
210 zero_graph(graph_db
[i
]);
212 /* advance the current position, zeroing up to it */
213 advance(&graph_secs
, tm
->tm_sec
);
214 advance(&graph_mins
, tm
->tm_min
);
215 advance(&graph_hrs
, tm
->tm_hour
);
216 advance(&graph_days
, tm
->tm_mday
- 1);
219 /* ---------------------------------------------------------------------------
220 * Database Import: Grab graphs from a file provided by the caller.
222 * This function will retrieve the data sans the header. We expect the caller
223 * to have validated the header of the segment, and left the file position at
224 * the start of the data.
226 int graph_import(const int fd
) {
230 if (!read64(fd
, &last
)) return 0;
233 for (i
=0; i
<graph_db_size
; i
++) {
234 unsigned char num_bars
, pos
;
235 unsigned int filepos
= xtell(fd
);
237 if (!read8(fd
, &num_bars
)) return 0;
238 if (!read8(fd
, &pos
)) return 0;
240 verbosef("at file pos %u, importing graph with %u bars",
241 filepos
, (unsigned int)num_bars
);
243 if (pos
>= num_bars
) {
244 warn("pos is %u, should be < num_bars which is %u",
245 (unsigned int)pos
, (unsigned int)num_bars
);
249 if (graph_db
[i
]->num_bars
!= num_bars
) {
250 warn("num_bars is %u, expecting %u",
251 (unsigned int)num_bars
, graph_db
[i
]->num_bars
);
255 graph_db
[i
]->pos
= pos
;
256 for (j
=0; j
<num_bars
; j
++) {
257 if (!read64(fd
, &(graph_db
[i
]->in
[j
]))) return 0;
258 if (!read64(fd
, &(graph_db
[i
]->out
[j
]))) return 0;
265 /* ---------------------------------------------------------------------------
266 * Database Export: Dump hosts_db into a file provided by the caller.
267 * The caller is responsible for writing out the header first.
269 int graph_export(const int fd
) {
272 if (!write64(fd
, (uint64_t)last_real
)) return 0;
273 for (i
=0; i
<graph_db_size
; i
++) {
274 if (!write8(fd
, graph_db
[i
]->num_bars
)) return 0;
275 if (!write8(fd
, graph_db
[i
]->pos
)) return 0;
277 for (j
=0; j
<graph_db
[i
]->num_bars
; j
++) {
278 if (!write64(fd
, graph_db
[i
]->in
[j
])) return 0;
279 if (!write64(fd
, graph_db
[i
]->out
[j
])) return 0;
285 /* ---------------------------------------------------------------------------
286 * Web interface: front page!
288 struct str
*html_front_page(void) {
289 struct str
*buf
, *rf
;
291 char start_when
[100];
292 time_t d_real
, d_mono
;
295 html_open(buf
, "Graphs", /*path_depth=*/0, /*want_graph_js=*/1);
297 d_mono
= now_mono() - start_mono
;
298 d_real
= now_real() - start_real
;
299 str_append(buf
, "<p>\n");
300 str_append(buf
, "<b>Running for</b> <span id=\"rf\">");
301 rf
= length_of_time(d_mono
);
302 str_appendstr(buf
, rf
);
304 str_append(buf
, "</span>");
305 if (abs(d_real
- d_mono
) > 1)
306 str_appendf(buf
, " (real time is off by %qd sec)",
307 (qd
)d_real
- (qd
)d_mono
);
309 if (strftime(start_when
, sizeof(start_when
),
310 "%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_real
)) != 0)
311 str_appendf(buf
, "<b>, since</b> %s", start_when
);
313 str_appendf(buf
,"<b>.</b><br>\n"
314 "<b>Total</b> <span id=\"tb\">%'qu</span> <b>bytes, "
315 "in</b> <span id=\"tp\">%'qu</span> <b>packets.</b> "
316 "(<span id=\"pc\">%'u</span> <b>captured,</b> "
317 "<span id=\"pd\">%'u</span> <b>dropped)</b><br>\n"
319 (qu
)acct_total_bytes
,
320 (qu
)acct_total_packets
,
325 "<div id=\"graphs\">\n"
326 "Graphs require JavaScript.\n"
327 "<script type=\"text/javascript\">\n"
329 "var graph_width = " GRAPH_WIDTH
";\n"
330 "var graph_height = " GRAPH_HEIGHT
";\n"
332 "var graphs_uri = \"graphs.xml\";\n"
336 for (i
=0; i
<graph_db_size
; i
++)
340 "title:\"last %u %s\", "
343 i
, graph_db
[i
]->unit
, graph_db
[i
]->num_bars
, graph_db
[i
]->unit
,
344 graph_db
[i
]->bar_secs
, (i
< graph_db_size
-1) ? "," : "");
345 /* trailing comma breaks on IE, makes the array one element longer */
349 "window.onload = graphs_init;\n"
359 /* ---------------------------------------------------------------------------
360 * Web interface: graphs.xml
362 struct str
*xml_graphs(void) {
364 struct str
*buf
= str_make(), *rf
;
366 str_appendf(buf
, "<graphs tp=\"%qu\" tb=\"%qu\" pc=\"%u\" pd=\"%u\" rf=\"",
367 (qu
)acct_total_packets
,
368 (qu
)acct_total_bytes
,
371 rf
= length_of_time(now_real() - start_real
);
372 str_appendstr(buf
, rf
);
374 str_append(buf
, "\">\n");
376 for (i
=0; i
<graph_db_size
; i
++) {
377 const struct graph
*g
= graph_db
[i
];
379 str_appendf(buf
, "<%s>\n", g
->unit
);
382 j
= (j
+ 1) % g
->num_bars
;
383 /* <element pos="" in="" out=""/> */
384 str_appendf(buf
, "<e p=\"%u\" i=\"%qu\" o=\"%qu\"/>\n",
388 } while (j
!= g
->pos
);
389 str_appendf(buf
, "</%s>\n", g
->unit
);
391 str_append(buf
, "</graphs>\n");
395 /* vim:set ts=3 sw=3 tw=80 et: */