IPTraffic: get rid of nested loop for realtime conntrack counting
[tomato.git] / release / src / router / cstats / cstats.c
blobaa620c9fa969a388746d5665f9a62c8d8594a725
1 /*
3 cstats
4 Copyright (C) 2011 Augusto Bott
6 based on rstats
7 Copyright (C) 2006-2009 Jonathan Zarate
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <signal.h>
27 #include <time.h>
28 #include <sys/types.h>
29 #include <sys/sysinfo.h>
30 #include <sys/stat.h>
31 #include <stdint.h>
32 #include <syslog.h>
34 #include <bcmnvram.h>
35 #include <shutils.h>
36 #include <shared.h>
38 #include <tree.h>
40 //#define DEBUG_NOISY
41 //#define DEBUG_STIME
43 //#ifdef DEBUG_NOISY
44 //#define _dprintf(args...) cprintf(args)
45 //#define _dprintf(args...) printf(args)
46 //#else
47 //#define _dprintf(args...) do { } while (0)
48 //#endif
50 #define K 1024
51 #define M (1024 * 1024)
52 #define G (1024 * 1024 * 1024)
54 #define SMIN 60
55 #define SHOUR (60 * 60)
56 #define SDAY (60 * 60 * 24)
57 #define Y2K 946684800UL
59 #define INTERVAL 120
61 #define MAX_NSPEED ((24 * SHOUR) / INTERVAL)
62 #define MAX_NDAILY 62
63 #define MAX_NMONTHLY 25
64 //#define MAX_SPEED_IP 64
65 #define MAX_ROLLOVER (225 * M)
67 #define MAX_COUNTER 2
68 #define RX 0
69 #define TX 1
71 #define DAILY 0
72 #define MONTHLY 1
74 #define ID_V0 0x30305352
75 #define ID_V1 0x31305352
76 #define ID_V2 0x32305352
77 #define CURRENT_ID ID_V2
79 #define HI_BACK 5
81 typedef struct {
82 uint32_t xtime;
83 uint64_t counter[MAX_COUNTER];
84 } data_t;
86 long save_utime;
87 char save_path[96];
88 long uptime;
90 volatile int gothup = 0;
91 volatile int gotuser = 0;
92 volatile int gotterm = 0;
94 const char history_fn[] = "/var/lib/misc/cstats-history";
95 const char uncomp_fn[] = "/var/tmp/cstats-uncomp";
96 const char source_fn[] = "/var/lib/misc/cstats-source";
98 typedef struct _Node {
99 char ipaddr[INET_ADDRSTRLEN];
101 uint32_t id;
103 data_t daily[MAX_NDAILY];
104 int dailyp;
105 data_t monthly[MAX_NMONTHLY];
106 int monthlyp;
108 long utime;
109 unsigned long speed[MAX_NSPEED][MAX_COUNTER];
110 unsigned long last[MAX_COUNTER];
111 int tail;
112 char sync;
114 TREE_ENTRY(_Node) linkage;
115 } Node;
117 typedef TREE_HEAD(_Tree, _Node) Tree;
119 TREE_DEFINE(_Node, linkage);
122 void Node_print(Node *self, FILE *stream) {
123 fprintf(stream, "%s", self->ipaddr);
126 void Node_printer(Node *self, void *stream) {
127 Node_print(self, (FILE *)stream);
128 fprintf((FILE *)stream, " ");
131 void Tree_info(void) {
132 _dprintf("Tree = ");
133 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_printer, stdout);
134 _dprintf("\n");
135 _dprintf("Tree depth = %d\n", TREE_DEPTH(&tree, linkage));
139 Node *Node_new(char *ipaddr) {
140 Node *self;
141 if ((self = malloc(sizeof(Node))) != NULL) {
142 memset(self, 0, sizeof(Node));
143 self->id = CURRENT_ID;
144 strncpy(self->ipaddr, ipaddr, INET_ADDRSTRLEN);
145 _dprintf("%s: new node ip=%s, version=%d, sizeof(Node)=%d (bytes)\n", __FUNCTION__, self->ipaddr, self->id, sizeof(Node));
147 return self;
150 int Node_compare(Node *lhs, Node *rhs) {
151 return strncmp(lhs->ipaddr, rhs->ipaddr, INET_ADDRSTRLEN);
154 Tree tree = TREE_INITIALIZER(Node_compare);
156 static int get_stime(void) {
157 #ifdef DEBUG_STIME
158 return 90;
159 #else
160 int t;
161 t = nvram_get_int("cstats_stime");
162 if (t < 1) t = 1;
163 else if (t > 8760) t = 8760;
164 return t * SHOUR;
165 #endif
168 typedef struct {
169 int mode;
170 int kn;
171 FILE *stream;
172 } node_print_mode_t;
174 void Node_save(Node *self, void *t) {
175 node_print_mode_t *info = (node_print_mode_t *)t;
176 if(fwrite(self, sizeof(Node), 1, info->stream) > 0) {
177 info->kn++;
181 static int save_history_from_tree(const char *fname) {
182 FILE *f;
183 node_print_mode_t info;
184 char s[256];
186 info.kn=0;
187 _dprintf("%s: fname=%s\n", __FUNCTION__, fname);
189 unlink(uncomp_fn);
190 if ((f = fopen(uncomp_fn, "wb")) != NULL) {
191 info.mode=0;
192 info.stream=f;
193 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_save, &info);
194 fclose(f);
196 sprintf(s, "%s.gz", fname);
197 unlink(s);
199 if (rename(uncomp_fn, fname) == 0) {
200 sprintf(s, "gzip %s", fname);
201 system(s);
205 return info.kn;
208 static void save(int quick) {
209 int i;
210 int n;
211 int b;
212 char hgz[256];
213 char tmp[256];
214 char bak[256];
215 char bkp[256];
216 time_t now;
217 struct tm *tms;
218 static int lastbak = -1;
220 _dprintf("%s: quick=%d\n", __FUNCTION__, quick);
222 f_write("/var/lib/misc/cstats-stime", &save_utime, sizeof(save_utime), 0, 0);
224 n = save_history_from_tree(history_fn);
225 _dprintf("%s: saved %d records from tree on file %s\n", __FUNCTION__, n, history_fn);
227 _dprintf("%s: write source=%s\n", __FUNCTION__, save_path);
228 f_write_string(source_fn, save_path, 0, 0);
230 if (quick) {
231 return;
234 sprintf(hgz, "%s.gz", history_fn);
236 if (save_path[0] != 0) {
237 strcpy(tmp, save_path);
238 strcat(tmp, ".tmp");
240 for (i = 15; i > 0; --i) {
241 if (!wait_action_idle(10)) {
242 _dprintf("%s: busy, not saving\n", __FUNCTION__);
244 else {
245 _dprintf("%s: cp %s %s\n", __FUNCTION__, hgz, tmp);
246 if (eval("cp", hgz, tmp) == 0) {
247 _dprintf("%s: copy ok\n", __FUNCTION__);
249 if (!nvram_match("cstats_bak", "0")) {
250 now = time(0);
251 tms = localtime(&now);
252 if (lastbak != tms->tm_yday) {
253 strcpy(bak, save_path);
254 n = strlen(bak);
255 if ((n > 3) && (strcmp(bak + (n - 3), ".gz") == 0)) n -= 3;
256 // sprintf(bak + n, "_%d.bak", ((tms->tm_yday / 7) % 3) + 1);
257 // if (eval("cp", save_path, bak) == 0) lastbak = tms->tm_yday;
258 strcpy(bkp, bak);
259 for (b = HI_BACK-1; b > 0; --b) {
260 sprintf(bkp + n, "_%d.bak", b + 1);
261 sprintf(bak + n, "_%d.bak", b);
262 rename(bak, bkp);
264 if (eval("cp", "-p", save_path, bak) == 0) lastbak = tms->tm_yday;
268 _dprintf("%s: rename %s %s\n", __FUNCTION__, tmp, save_path);
269 if (rename(tmp, save_path) == 0) {
270 _dprintf("%s: rename ok\n", __FUNCTION__);
271 break;
276 // might not be ready
277 sleep(3);
278 if (gotterm) break;
283 static int load_history_to_tree(const char *fname) {
284 int n;
285 FILE *f;
286 char s[256];
287 Node tmp;
288 Node *ptr;
289 char *exclude;
291 exclude = nvram_safe_get("cstats_exclude");
292 _dprintf("%s: cstats_exclude='%s'\n", __FUNCTION__, exclude);
293 _dprintf("%s: fname=%s\n", __FUNCTION__, fname);
294 unlink(uncomp_fn);
296 n = 0;
297 sprintf(s, "gzip -dc %s > %s", fname, uncomp_fn);
298 if (system(s) == 0) {
299 if ((f = fopen(uncomp_fn, "rb")) != NULL) {
300 while (fread(&tmp, sizeof(Node), 1, f) > 0) {
301 if ((find_word(exclude, tmp.ipaddr))) {
302 _dprintf("%s: not loading excluded ip '%s'\n", __FUNCTION__, tmp.ipaddr);
303 continue;
306 if (tmp.id == CURRENT_ID) {
307 _dprintf("%s: found data for ip %s\n", __FUNCTION__, tmp.ipaddr);
309 ptr = TREE_FIND(&tree, _Node, linkage, &tmp);
310 if (ptr) {
311 _dprintf("%s: removing/reloading new data for ip %s\n", __FUNCTION__, ptr->ipaddr);
312 TREE_REMOVE(&tree, _Node, linkage, ptr);
313 free(ptr);
314 ptr = NULL;
317 TREE_INSERT(&tree, _Node, linkage, Node_new(tmp.ipaddr));
319 ptr = TREE_FIND(&tree, _Node, linkage, &tmp);
321 memcpy(ptr->daily, &tmp.daily, sizeof(data_t) * MAX_NDAILY);
322 ptr->dailyp = tmp.dailyp;
323 memcpy(ptr->monthly, &tmp.monthly, sizeof(data_t) * MAX_NMONTHLY);
324 ptr->monthlyp = tmp.monthlyp;
326 ptr->utime = tmp.utime;
327 memcpy(ptr->speed, &tmp.speed, sizeof(unsigned long) * MAX_NSPEED * MAX_COUNTER);
328 memcpy(ptr->last, &tmp.last, sizeof(unsigned long) * MAX_COUNTER);
329 ptr->tail = tmp.tail;
330 // ptr->sync = tmp.sync;
331 ptr->sync = -1;
333 if (ptr->utime > uptime) {
334 ptr->utime = uptime;
335 ptr->sync = 1;
338 ++n;
339 } else {
340 _dprintf("%s: data for ip '%s' version %d not loaded (current version is %d)\n", __FUNCTION__, tmp.ipaddr, tmp.id, CURRENT_ID);
344 fclose(f);
347 else {
348 _dprintf("%s: %s != 0\n", __FUNCTION__, s);
350 unlink(uncomp_fn);
352 _dprintf("%s: loaded %d records\n", __FUNCTION__, n);
354 return n;
357 static int load_history(const char *fname) {
358 _dprintf("%s: fname=%s\n", __FUNCTION__, fname);
359 return load_history_to_tree(fname);
362 /* Try loading from the backup versions.
363 * We'll try from oldest to newest, then
364 * retry the requested one again last. In case the drive mounts while
365 * we are trying to find a good version.
367 static int try_hardway(const char *fname) {
368 char fn[256];
369 int n, b, found = 0;
371 strcpy(fn, fname);
372 n = strlen(fn);
373 if ((n > 3) && (strcmp(fn + (n - 3), ".gz") == 0))
374 n -= 3;
375 for (b = HI_BACK; b > 0; --b) {
376 sprintf(fn + n, "_%d.bak", b);
377 found |= load_history(fn);
379 found |= load_history(fname);
381 return found;
384 static void load_new(void)
386 char hgz[256];
388 sprintf(hgz, "%s.gz.new", history_fn);
389 if (load_history(hgz)) save(0);
390 unlink(hgz);
393 static void load(int new) {
394 int i;
395 long t;
396 int n;
397 char hgz[256];
398 unsigned char mac[6];
400 uptime = get_uptime();
402 _dprintf("%s: new=%d, uptime=%lu\n", __FUNCTION__, new, uptime);
404 strlcpy(save_path, nvram_safe_get("cstats_path"), sizeof(save_path) - 32);
405 if (((n = strlen(save_path)) > 0) && (save_path[n - 1] == '/')) {
406 ether_atoe(nvram_safe_get("et0macaddr"), mac);
407 sprintf(save_path + n, "tomato_cstats_%02x%02x%02x%02x%02x%02x.gz",
408 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
411 if (f_read("/var/lib/misc/cstats-stime", &save_utime, sizeof(save_utime)) != sizeof(save_utime)) {
412 save_utime = 0;
414 t = uptime + get_stime();
415 if ((save_utime < uptime) || (save_utime > t)) save_utime = t;
416 _dprintf("%s: uptime = %lum, save_utime = %lum\n", __FUNCTION__, uptime / 60, save_utime / 60);
418 sprintf(hgz, "%s.gz", history_fn);
420 if (new) {
421 unlink(hgz);
422 save_utime = 0;
423 return;
426 if (save_path[0] != 0) {
427 i = 1;
428 while (1) {
429 if (wait_action_idle(10)) {
431 // cifs quirk: try forcing refresh
432 eval("ls", save_path);
434 /* If we can't access the path, keep trying - maybe it isn't mounted yet.
435 * If we can, and we can sucessfully load it, oksy.
436 * If we can, and we cannot load it, then maybe it has been deleted, or
437 * maybe it's corrupted (like 0 bytes long).
438 * In these cases, try the backup files.
440 // if (load_history(save_path)) {
441 if (load_history(save_path) || try_hardway(save_path)) {
442 f_write_string(source_fn, save_path, 0, 0);
443 break;
447 // not ready...
448 sleep(i);
449 if ((i *= 2) > 900) i = 900; // 15m
451 if (gotterm) {
452 save_path[0] = 0;
453 return;
456 if (i > (3 * 60)) {
457 syslog(LOG_WARNING, "Problem loading %s. Still trying...", save_path);
463 void Node_print_speedjs(Node *self, void *t) {
464 int j, k, p;
465 uint64_t total, tmax;
466 unsigned long n;
467 char c;
469 node_print_mode_t *info = (node_print_mode_t *)t;
471 fprintf(info->stream, "%s'%s': {\n", info->kn ? " },\n" : "", self->ipaddr);
472 for (j = 0; j < MAX_COUNTER; ++j) {
473 total = tmax = 0;
474 fprintf(info->stream, "%sx: [", j ? ",\n t" : " r");
475 p = self->tail;
476 for (k = 0; k < MAX_NSPEED; ++k) {
477 p = (p + 1) % MAX_NSPEED;
478 n = self->speed[p][j];
479 fprintf(info->stream, "%s%lu", k ? "," : "", n);
480 total += n;
481 if (n > tmax) tmax = n;
483 fprintf(info->stream, "],\n");
485 c = j ? 't' : 'r';
486 fprintf(info->stream, " %cx_avg: %llu,\n %cx_max: %llu,\n %cx_total: %llu",
487 c, total / MAX_NSPEED, c, tmax, c, total);
489 info->kn++;
492 static void save_speedjs(long next) {
493 FILE *f;
495 if ((f = fopen("/var/tmp/cstats-speed.js", "w")) == NULL) return;
497 node_print_mode_t info;
498 info.mode=0;
499 info.stream=f;
500 info.kn=0;
502 fprintf(f, "\nspeed_history = {\n");
503 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_print_speedjs, &info);
504 fprintf(f, "%s_next: %ld};\n", info.kn ? "},\n" : "", ((next >= 1) ? next : 1));
506 fclose(f);
508 rename("/var/tmp/cstats-speed.js", "/var/spool/cstats-speed.js");
511 void Node_print_datajs(Node *self, void *t) {
512 data_t *data;
513 int p, max, k;
515 node_print_mode_t *info = (node_print_mode_t *)t;
517 if (info->mode == DAILY) {
518 data = self->daily;
519 p = self->dailyp;
520 max = MAX_NDAILY;
523 else {
524 data = self->monthly;
525 p = self->monthlyp;
526 max = MAX_NMONTHLY;
529 for (k = max; k > 0; --k) {
530 p = (p + 1) % max;
531 if (data[p].xtime == 0) continue;
532 fprintf(info->stream, "%s[0x%lx,'%s',%llu,%llu]", info->kn ? "," : "",
533 (unsigned long)data[p].xtime, self->ipaddr, data[p].counter[0] / K, data[p].counter[1] / K);
534 info->kn++;
538 static void save_datajs(FILE *f, int mode) {
539 node_print_mode_t info;
540 info.mode=mode;
541 info.stream=f;
542 info.kn=0;
543 fprintf(f, "\n%s_history = [\n", (mode == DAILY) ? "daily" : "monthly");
544 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_print_datajs, &info);
545 fprintf(f, "\n];\n");
548 static void save_histjs(void) {
549 FILE *f;
551 if ((f = fopen("/var/tmp/cstats-history.js", "w")) != NULL) {
552 save_datajs(f, DAILY);
553 save_datajs(f, MONTHLY);
554 fclose(f);
555 rename("/var/tmp/cstats-history.js", "/var/spool/cstats-history.js");
559 static void bump(data_t *data, int *tail, int max, uint32_t xnow, unsigned long *counter) {
560 int t, i;
562 t = *tail;
563 if (data[t].xtime != xnow) {
564 for (i = max - 1; i >= 0; --i) {
565 if (data[i].xtime == xnow) {
566 t = i;
567 break;
570 if (i < 0) {
571 *tail = t = (t + 1) % max;
572 data[t].xtime = xnow;
573 memset(data[t].counter, 0, sizeof(data[0].counter));
576 for (i = 0; i < MAX_COUNTER; ++i) {
577 data[t].counter[i] += counter[i];
581 void Node_housekeeping(Node *self, void *info) {
582 if (self->sync == -1) {
583 self->sync = 0;
584 } else {
585 self->sync = 1;
589 static void calc(void) {
590 FILE *f;
591 char buf[512];
592 char *ipaddr = NULL;
593 unsigned long counter[MAX_COUNTER];
594 int i, j;
595 time_t now;
596 time_t mon;
597 struct tm *tms;
598 uint32_t c;
599 uint32_t sc;
600 unsigned long diff;
601 long tick;
602 int n;
603 char *exclude;
604 char *include;
606 Node *ptr;
607 Node test;
609 now = time(0);
610 exclude = nvram_safe_get("cstats_exclude");
611 include = nvram_safe_get("cstats_include");
613 _dprintf("%s: cstats_exclude='%s'\n", __FUNCTION__, exclude);
615 unsigned long tx;
616 unsigned long rx;
617 char ip[INET_ADDRSTRLEN];
619 char br;
620 char name[] = "/proc/net/ipt_account/lanX";
622 for(br=0 ; br<=3 ; br++) {
624 char wholenetstatsline = 1;
626 char bridge[2] = "0";
627 if (br!=0)
628 bridge[0]+=br;
629 else
630 strcpy(bridge, "");
632 sprintf(name, "/proc/net/ipt_account/lan%s", bridge);
634 if ((f = fopen(name, "r")) == NULL) continue;
636 // _dprintf("%s: file %s opened\n", __FUNCTION__, name);
637 // if (!wholenetstatsline)
638 // fgets(buf, sizeof(buf), f); // network
640 while (fgets(buf, sizeof(buf), f)) {
641 // _dprintf("%s: read\n", __FUNCTION__);
642 if(sscanf(buf,
643 "ip = %s bytes_src = %lu %*u %*u %*u %*u packets_src = %*u %*u %*u %*u %*u bytes_dst = %lu %*u %*u %*u %*u packets_dst = %*u %*u %*u %*u %*u time = %*u",
644 // "ip = %s bytes_src = %Lu %*Lu %*Lu %*Lu %*Lu packets_src = %*Lu %*Lu %*Lu %*Lu %*Lu bytes_dest = %Lu %*Lu %*Lu %*Lu %*Lu packets_dest = %*Lu %*Lu %*Lu %*Lu %*Lu time = %*lu",
645 ip, &rx, &tx) != 3 ) continue;
646 // _dprintf("%s: %s tx=%lu rx=%lu\n", __FUNCTION__, ip, tx, rx);
648 // if ((tx < 1) || (rx < 1)) continue;
650 counter[0] = tx;
651 counter[1] = rx;
652 ipaddr=ip;
654 // _dprintf("%s: %s tx=%lu rx=%lu\n", __FUNCTION__, ipaddr, tx, rx);
656 if (find_word(exclude, ipaddr)) {
657 wholenetstatsline = 0;
658 continue;
661 strncpy(test.ipaddr, ipaddr, INET_ADDRSTRLEN);
662 ptr = TREE_FIND(&tree, _Node, linkage, &test);
664 if ((find_word(include, ipaddr)) || (wholenetstatsline == 1) || (ptr) || ((nvram_get_int("cstats_all")) && ((counter[0] > 0) || (counter[1] > 0)) )) {
666 wholenetstatsline = 0;
668 if (!ptr) {
669 _dprintf("%s: new ip: %s\n", __FUNCTION__, ipaddr);
670 TREE_INSERT(&tree, _Node, linkage, Node_new(ipaddr));
671 ptr = TREE_FIND(&tree, _Node, linkage, &test);
672 ptr->sync = 1;
673 ptr->utime = uptime;
676 // Tree_info();
678 _dprintf("%s: sync[%s]=%d\n", __FUNCTION__, ptr->ipaddr, ptr->sync);
679 if (ptr->sync) {
680 _dprintf("%s: sync[%s] changed to -1\n", __FUNCTION__, ptr->ipaddr);
681 ptr->sync = -1;
683 for (i = 0; i < MAX_COUNTER; ++i) {
684 _dprintf("%s: counter[%d]=%lu ptr->last[%d]=%lu\n", __FUNCTION__, i, counter[i], i, ptr->last[i]);
687 memcpy(ptr->last, counter, sizeof(ptr->last));
688 memset(counter, 0, sizeof(counter));
689 for (i = 0; i < MAX_COUNTER; ++i) {
690 _dprintf("%s: counter[%d]=%lu ptr->last[%d]=%lu\n", __FUNCTION__, i, counter[i], i, ptr->last[i]);
693 else {
694 // _dprintf("%s: sync[%s] = %d \n", __FUNCTION__, ptr->ipaddr, ptr->sync);
695 ptr->sync = -1;
696 _dprintf("%s: sync[%s] = %d \n", __FUNCTION__, ptr->ipaddr, ptr->sync);
697 tick = uptime - ptr->utime;
698 n = tick / INTERVAL;
699 if (n < 1) {
700 _dprintf("%s: %s is a little early... %lu < %d\n", __FUNCTION__, ipaddr, tick, INTERVAL);
701 } else {
702 ptr->utime += (n * INTERVAL);
703 _dprintf("%s: %s n=%d tick=%lu utime=%lu ptr->utime=%lu\n", __FUNCTION__, ipaddr, n, tick, uptime, ptr->utime);
704 for (i = 0; i < MAX_COUNTER; ++i) {
705 c = counter[i];
706 sc = ptr->last[i];
707 // _dprintf("%s: counter[%d]=%lu ptr->last[%d]=%lu c=%u sc=%u\n", __FUNCTION__, i, counter[i], i, ptr->last[i], c, sc);
708 if (c < sc) {
709 diff = (0xFFFFFFFF - sc) + c;
710 if (diff > MAX_ROLLOVER) diff = 0;
712 else {
713 diff = c - sc;
715 ptr->last[i] = c;
716 counter[i] = diff;
717 _dprintf("%s: counter[%d]=%lu ptr->last[%d]=%lu c=%u sc=%u diff=%lu\n", __FUNCTION__, i, counter[i], i, ptr->last[i], c, sc, diff);
719 _dprintf("%s: ip=%s n=%d ptr->tail=%d\n", __FUNCTION__, ptr->ipaddr, n, ptr->tail);
720 for (j = 0; j < n; ++j) {
721 ptr->tail = (ptr->tail + 1) % MAX_NSPEED;
722 // _dprintf("%s: ip=%s j=%d n=%d ptr->tail=%d\n", __FUNCTION__, ptr->ipaddr, j, n, ptr->tail);
723 for (i = 0; i < MAX_COUNTER; ++i) {
724 ptr->speed[ptr->tail][i] = counter[i] / n;
727 _dprintf("%s: ip=%s j=%d n=%d ptr->tail=%d\n", __FUNCTION__, ptr->ipaddr, j, n, ptr->tail);
731 if (now > Y2K) { /* Skip this if the time&date is not set yet */
732 // _dprintf("%s: calling bump %s ptr->dailyp=%d\n", __FUNCTION__, ptr->ipaddr, ptr->dailyp);
733 tms = localtime(&now);
734 bump(ptr->daily, &ptr->dailyp, MAX_NDAILY,
735 (tms->tm_year << 16) | ((uint32_t)tms->tm_mon << 8) | tms->tm_mday, counter);
737 // _dprintf("%s: calling bump %s ptr->monthlyp=%d\n", __FUNCTION__, ptr->ipaddr, ptr->monthlyp);
738 n = nvram_get_int("cstats_offset");
739 if ((n < 1) || (n > 31)) n = 1;
740 mon = now + ((1 - n) * (60 * 60 * 24));
741 tms = localtime(&mon);
742 bump(ptr->monthly, &ptr->monthlyp, MAX_NMONTHLY,
743 (tms->tm_year << 16) | ((uint32_t)tms->tm_mon << 8), counter);
748 fclose(f);
751 // cleanup entries for next time
752 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_housekeeping, NULL);
754 // remove/exclude history (if we still have any data previously stored)
755 char *nvp, *nv, *b;
756 nvp = nv = strdup(nvram_safe_get("cstats_exclude"));
757 if (nv) {
758 while ((b = strsep(&nvp, ",")) != NULL) {
759 _dprintf("%s: check exclude='%s'\n", __FUNCTION__, b);
760 strncpy(test.ipaddr, b, INET_ADDRSTRLEN);
761 ptr = TREE_FIND(&tree, _Node, linkage, &test);
762 if (ptr) {
763 _dprintf("%s: excluding '%s'\n", __FUNCTION__, ptr->ipaddr);
764 TREE_REMOVE(&tree, _Node, linkage, ptr);
765 free(ptr);
766 ptr = NULL;
769 free(nv);
772 // todo: total > user ???
773 if (uptime >= save_utime) {
774 save(0);
775 save_utime = uptime + get_stime();
776 _dprintf("%s: uptime = %lum, save_utime = %lum\n", __FUNCTION__, uptime / 60, save_utime / 60);
779 _dprintf("%s: ====================================\n", __FUNCTION__);
782 static void sig_handler(int sig) {
783 switch (sig) {
784 case SIGTERM:
785 case SIGINT:
786 gotterm = 1;
787 break;
788 case SIGHUP:
789 gothup = 1;
790 break;
791 case SIGUSR1:
792 gotuser = 1;
793 break;
794 case SIGUSR2:
795 gotuser = 2;
796 break;
800 int main(int argc, char *argv[]) {
802 struct sigaction sa;
803 long z;
804 int new;
806 printf("cstats - Copyright (C) 2011 Augusto Bott\n");
807 printf("based on rstats - Copyright (C) 2006-2009 Jonathan Zarate\n\n");
809 if (fork() != 0) return 0;
811 openlog("cstats", LOG_PID, LOG_USER);
813 new = 0;
814 if (argc > 1) {
815 if (strcmp(argv[1], "--new") == 0) {
816 new = 1;
817 _dprintf("new=1\n");
821 unlink("/var/tmp/cstats-load");
823 sa.sa_handler = sig_handler;
824 sa.sa_flags = 0;
825 sigemptyset(&sa.sa_mask);
826 sigaction(SIGUSR1, &sa, NULL);
827 sigaction(SIGUSR2, &sa, NULL);
828 sigaction(SIGHUP, &sa, NULL);
829 sigaction(SIGTERM, &sa, NULL);
830 sigaction(SIGINT, &sa, NULL);
832 load(new);
834 z = uptime = get_uptime();
835 while (1) {
836 while (uptime < z) {
837 sleep(z - uptime);
838 if (gothup) {
839 if (unlink("/var/tmp/cstats-load") == 0) load_new();
840 else save(0);
841 gothup = 0;
843 if (gotterm) {
844 save(!nvram_match("cstats_sshut", "1"));
845 exit(0);
847 if (gotuser == 1) {
848 save_speedjs(z - get_uptime());
849 gotuser = 0;
851 else if (gotuser == 2) {
852 save_histjs();
853 gotuser = 0;
855 uptime = get_uptime();
857 calc();
858 z += INTERVAL;
861 return 0;