Latest updates from Teaman-IPTraffic
[tomato.git] / release / src / router / cstats / cstats.c
blobbbcc62c66090f63618b87328454148137f48d921
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>
39 // #define DEBUG_NOISY
40 // #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
51 #define K 1024
52 #define M (1024 * 1024)
53 #define G (1024 * 1024 * 1024)
55 #define SMIN 60
56 #define SHOUR (60 * 60)
57 #define SDAY (60 * 60 * 24)
58 #define Y2K 946684800UL
60 #define INTERVAL 120
62 #define MAX_NSPEED ((24 * SHOUR) / INTERVAL)
63 #define MAX_NDAILY 62
64 #define MAX_NMONTHLY 25
65 #define MAX_SPEED_IP 64
66 #define MAX_ROLLOVER (225 * M)
68 #define MAX_COUNTER 2
69 #define RX 0
70 #define TX 1
72 #define DAILY 0
73 #define MONTHLY 1
75 #define ID_V0 0x30305352
76 #define ID_V1 0x31305352
77 #define ID_V2 0x32305352
78 #define CURRENT_ID ID_V2
81 typedef struct {
82 uint32_t xtime;
83 uint64_t counter[MAX_COUNTER];
84 } data_t;
86 typedef struct {
87 uint32_t id;
89 data_t daily[MAX_SPEED_IP][MAX_NDAILY];
90 int dailyp[MAX_SPEED_IP];
92 data_t monthly[MAX_SPEED_IP][MAX_NMONTHLY];
93 int monthlyp[MAX_SPEED_IP];
95 char ipaddr[MAX_SPEED_IP][INET_ADDRSTRLEN];
97 int howmany;
99 } history_t;
101 typedef struct {
102 uint32_t id;
104 data_t daily[MAX_NDAILY];
105 int dailyp;
107 data_t monthly[MAX_NMONTHLY];
108 int monthlyp;
109 } history_v1_t;
111 typedef struct {
112 uint32_t id;
114 data_t daily[62];
115 int dailyp;
117 data_t monthly[12];
118 int monthlyp;
119 } history_v0_t;
122 typedef struct {
123 char ipaddr[INET_ADDRSTRLEN];
124 long utime;
125 unsigned long speed[MAX_NSPEED][MAX_COUNTER];
126 unsigned long last[MAX_COUNTER];
127 int tail;
128 char sync;
129 } speed_t;
131 history_t history;
132 speed_t speed[MAX_SPEED_IP];
133 int speed_count;
134 long save_utime;
135 char save_path[96];
136 long uptime;
138 volatile int gothup = 0;
139 volatile int gotuser = 0;
140 volatile int gotterm = 0;
142 const char history_fn[] = "/var/lib/misc/cstats-history";
143 const char speed_fn[] = "/var/lib/misc/cstats-speed";
144 const char uncomp_fn[] = "/var/tmp/cstats-uncomp";
145 const char source_fn[] = "/var/lib/misc/cstats-source";
148 static int get_stime(void)
150 #ifdef DEBUG_STIME
151 return 90;
152 #else
153 int t;
154 t = nvram_get_int("cstats_stime");
155 if (t < 1) t = 1;
156 else if (t > 8760) t = 8760;
157 return t * SHOUR;
158 #endif
161 static int comp(const char *path, void *buffer, int size)
163 char s[256];
165 if (f_write(path, buffer, size, 0, 0) != size) return 0;
167 sprintf(s, "%s.gz", path);
168 unlink(s);
170 sprintf(s, "gzip %s", path);
171 return system(s) == 0;
174 static void save(int quick)
176 int i;
177 // char *bi, *bo;
178 int n;
179 char hgz[256];
180 char tmp[256];
181 char bak[256];
182 time_t now;
183 struct tm *tms;
184 static int lastbak = -1;
186 _dprintf("%s: quick=%d\n", __FUNCTION__, quick);
188 f_write("/var/lib/misc/cstats-stime", &save_utime, sizeof(save_utime), 0, 0);
190 comp(speed_fn, speed, sizeof(speed[0]) * speed_count);
193 if ((now = time(0)) < Y2K) {
194 _dprintf("%s: time not set\n", __FUNCTION__);
195 return;
199 comp(history_fn, &history, sizeof(history));
201 _dprintf("%s: write source=%s\n", __FUNCTION__, save_path);
202 f_write_string(source_fn, save_path, 0, 0);
204 if (quick) {
205 return;
208 sprintf(hgz, "%s.gz", history_fn);
211 if (strcmp(save_path, "*nvram") == 0) {
212 if (!wait_action_idle(10)) {
213 _dprintf("%s: busy, not saving\n", __FUNCTION__);
214 return;
217 if ((n = f_read_alloc(hgz, &bi, 20 * 1024)) > 0) {
218 if ((bo = malloc(base64_encoded_len(n) + 1)) != NULL) {
219 n = base64_encode(bi, bo, n);
220 bo[n] = 0;
221 nvram_set("cstats_data", bo);
222 if (!nvram_match("debug_nocommit", "1")) nvram_commit();
224 _dprintf("%s: nvram commit\n", __FUNCTION__);
226 free(bo);
229 free(bi);
231 else if (save_path[0] != 0) {
233 if (save_path[0] != 0) {
234 strcpy(tmp, save_path);
235 strcat(tmp, ".tmp");
237 for (i = 15; i > 0; --i) {
238 if (!wait_action_idle(10)) {
239 _dprintf("%s: busy, not saving\n", __FUNCTION__);
241 else {
242 _dprintf("%s: cp %s %s\n", __FUNCTION__, hgz, tmp);
243 if (eval("cp", hgz, tmp) == 0) {
244 _dprintf("%s: copy ok\n", __FUNCTION__);
246 if (!nvram_match("cstats_bak", "0")) {
247 now = time(0);
248 tms = localtime(&now);
249 if (lastbak != tms->tm_yday) {
250 strcpy(bak, save_path);
251 n = strlen(bak);
252 if ((n > 3) && (strcmp(bak + (n - 3), ".gz") == 0)) n -= 3;
253 sprintf(bak + n, "_%d.bak", ((tms->tm_yday / 7) % 3) + 1);
254 if (eval("cp", save_path, bak) == 0) lastbak = tms->tm_yday;
258 _dprintf("%s: rename %s %s\n", __FUNCTION__, tmp, save_path);
259 if (rename(tmp, save_path) == 0) {
260 _dprintf("%s: rename ok\n", __FUNCTION__);
261 break;
266 // might not be ready
267 sleep(3);
268 if (gotterm) break;
274 static int decomp(const char *fname, void *buffer, int size, int max)
276 char s[256];
277 int n;
279 _dprintf("%s: fname=%s\n", __FUNCTION__, fname);
281 unlink(uncomp_fn);
283 n = 0;
284 sprintf(s, "gzip -dc %s > %s", fname, uncomp_fn);
285 if (system(s) == 0) {
286 n = f_read(uncomp_fn, buffer, size * max);
287 _dprintf("%s: n=%d\n", __FUNCTION__, n);
288 if (n <= 0) n = 0;
289 else n = n / size;
291 else {
292 _dprintf("%s: %s != 0\n", __FUNCTION__, s);
294 unlink(uncomp_fn);
295 memset((char *)buffer + (size * n), 0, (max - n) * size);
296 return n;
300 static void clear_history(void)
302 memset(&history, 0, sizeof(history));
303 history.id = CURRENT_ID;
304 history.howmany = 0;
305 _dprintf("%s: sizeof(history)= %d, CURRENT_ID= %d...\n", __FUNCTION__, sizeof(history), CURRENT_ID);
310 static int load_history(const char *fname)
312 history_t hist;
314 _dprintf("%s: fname=%s\n", __FUNCTION__, fname);
316 if ((decomp(fname, &hist, sizeof(hist), 1) != 1) || (hist.id != CURRENT_ID)) {
318 /* history_v0_t v0;
320 if ((decomp(fname, &v0, sizeof(v0), 1) != 1) || (v0.id != ID_V0)) {
321 _dprintf("%s: load failed\n", __FUNCTION__);
322 return 0;
324 else {
325 // --- temp conversion ---
326 clear_history();
328 // V0 -> V1
329 history.id = CURRENT_ID;
330 memcpy(history.daily, v0.daily, sizeof(history.daily));
331 history.dailyp = v0.dailyp;
332 memcpy(history.monthly, v0.monthly, sizeof(v0.monthly)); // v0 is just shorter
333 history.monthlyp = v0.monthlyp;
337 else {
338 memcpy(&history, &hist, sizeof(history));
340 // _dprintf("%s: howmany=%d dailyp=%d monthlyp=%d\n", __FUNCTION__, history.howmany, history.dailyp, history.monthlyp);
341 return 1;
344 static void load_new(void)
346 char hgz[256];
348 sprintf(hgz, "%s.gz.new", history_fn);
349 if (load_history(hgz)) save(0);
350 unlink(hgz);
353 static void load(int new)
355 int i;
356 long t;
357 // char *bi, *bo;
358 int n;
359 char hgz[256];
360 char sp[sizeof(save_path)];
361 unsigned char mac[6];
363 uptime = get_uptime();
365 _dprintf("%s: new=%d, uptime=%lu\n", __FUNCTION__, new, uptime);
367 strlcpy(save_path, nvram_safe_get("cstats_path"), sizeof(save_path) - 32);
368 if (((n = strlen(save_path)) > 0) && (save_path[n - 1] == '/')) {
369 ether_atoe(nvram_safe_get("et0macaddr"), mac);
370 sprintf(save_path + n, "tomato_cstats_%02x%02x%02x%02x%02x%02x.gz",
371 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
374 if (f_read("/var/lib/misc/cstats-stime", &save_utime, sizeof(save_utime)) != sizeof(save_utime)) {
375 save_utime = 0;
377 t = uptime + get_stime();
378 if ((save_utime < uptime) || (save_utime > t)) save_utime = t;
379 _dprintf("%s: uptime = %lum, save_utime = %lum\n", __FUNCTION__, uptime / 60, save_utime / 60);
383 sprintf(hgz, "%s.gz", speed_fn);
384 speed_count = decomp(hgz, speed, sizeof(speed[0]), MAX_SPEED_IP);
385 _dprintf("%s: speed_count = %d\n", __FUNCTION__, speed_count);
387 for (i = 0; i < speed_count; ++i) {
388 if (speed[i].utime > uptime) {
389 speed[i].utime = uptime;
390 speed[i].sync = 1;
396 sprintf(hgz, "%s.gz", history_fn);
398 if (new) {
399 unlink(hgz);
400 save_utime = 0;
401 return;
404 f_read_string(source_fn, sp, sizeof(sp)); // always terminated
405 _dprintf("%s: read source=%s save_path=%s\n", __FUNCTION__, sp, save_path);
406 if ((strcmp(sp, save_path) == 0) && (load_history(hgz))) {
407 _dprintf("%s: using local file\n", __FUNCTION__);
408 return;
411 if (save_path[0] != 0) {
413 if (strcmp(save_path, "*nvram") == 0) {
414 if (!wait_action_idle(60)) exit(0);
416 bi = nvram_safe_get("cstats_data");
417 if ((n = strlen(bi)) > 0) {
418 if ((bo = malloc(base64_decoded_len(n))) != NULL) {
419 n = base64_decode(bi, bo, n);
420 _dprintf("%s: nvram n=%d\n", __FUNCTION__, n);
421 f_write(hgz, bo, n, 0, 0);
422 free(bo);
423 load_history(hgz);
427 else {
429 // {
430 i = 1;
431 while (1) {
432 if (wait_action_idle(10)) {
434 // cifs quirk: try forcing refresh
435 eval("ls", save_path);
437 if (load_history(save_path)) {
438 f_write_string(source_fn, save_path, 0, 0);
439 break;
443 // not ready...
444 sleep(i);
445 if ((i *= 2) > 900) i = 900; // 15m
447 if (gotterm) {
448 save_path[0] = 0;
449 return;
452 if (i > (3 * 60)) {
453 syslog(LOG_WARNING, "Problem loading %s. Still trying...", save_path);
456 // }
461 static void save_speedjs(long next)
463 int i, j, k;
464 speed_t *sp;
465 int p;
466 FILE *f;
467 uint64_t total;
468 uint64_t tmax;
469 unsigned long n;
470 char c;
472 if ((f = fopen("/var/tmp/cstats-speed.js", "w")) == NULL) return;
474 _dprintf("%s: speed_count = %d\n", __FUNCTION__, speed_count);
476 fprintf(f, "\nspeed_history = {\n");
478 for (i = 0; i < speed_count; ++i) {
479 sp = &speed[i];
480 fprintf(f, "%s'%s': {\n", i ? " },\n" : "", sp->ipaddr);
481 for (j = 0; j < MAX_COUNTER; ++j) {
482 total = tmax = 0;
483 fprintf(f, "%sx: [", j ? ",\n t" : " r");
484 p = sp->tail;
485 for (k = 0; k < MAX_NSPEED; ++k) {
486 p = (p + 1) % MAX_NSPEED;
487 n = sp->speed[p][j];
488 fprintf(f, "%s%lu", k ? "," : "", n);
489 total += n;
490 if (n > tmax) tmax = n;
492 fprintf(f, "],\n");
494 c = j ? 't' : 'r';
495 fprintf(f, " %cx_avg: %llu,\n %cx_max: %llu,\n %cx_total: %llu",
496 c, total / MAX_NSPEED, c, tmax, c, total);
499 fprintf(f, "%s_next: %ld};\n", speed_count ? "},\n" : "", ((next >= 1) ? next : 1));
500 fclose(f);
502 rename("/var/tmp/cstats-speed.js", "/var/spool/cstats-speed.js");
506 static void save_datajs(FILE *f, int mode)
508 data_t *data;
509 int p;
510 int max;
511 int i, k, kn;
513 fprintf(f, "\n%s_history = [\n", (mode == DAILY) ? "daily" : "monthly");
515 _dprintf("%s: history.howmany=%d \n", __FUNCTION__, history.howmany);
517 kn = 0;
518 for (i = 0 ; i < history.howmany; i++) {
519 // _dprintf("%s: history.howmany=%d \n", __FUNCTION__, history.howmany);
520 if (mode == DAILY) {
521 data = history.daily[i];
522 p = history.dailyp[i];
523 max = MAX_NDAILY;
526 else {
527 data = history.monthly[i];
528 p = history.monthlyp[i];
529 max = MAX_NMONTHLY;
532 _dprintf("%s: ipaddr=%s i=%d p=%d max=%d\n", __FUNCTION__, history.ipaddr[i], i, p, max);
534 for (k = max; k > 0; --k) {
535 p = (p + 1) % max;
536 if (data[p].xtime == 0) continue;
537 // fprintf(f, "%s['%s',0x%lx,0x%llx,0x%llx]", kn ? "," : "", history.ipaddr[i],
538 // (unsigned long)data[p].xtime, data[p].counter[0] / K, data[p].counter[1] / K);
539 fprintf(f, "%s[0x%lx,'%s',%llu,%llu]", kn ? "," : "",
540 (unsigned long)data[p].xtime, history.ipaddr[i], data[p].counter[0] / K, data[p].counter[1] / K);
541 ++kn;
544 fprintf(f, "];\n");
547 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");
560 static void bump(data_t *data, int *tail, int max, uint32_t xnow, unsigned long *counter)
562 int t, i;
564 t = *tail;
566 // _dprintf("%s: counter[0]=%llu counter[1]=%llu tail=%d\n", __FUNCTION__, data[t].counter[0], data[t].counter[1], *tail);
567 // _dprintf("%s: counter[0]=%lu counter[1]=%lu tail=%d max=%d xnow=%d\n", __FUNCTION__, counter[0], counter[1], *tail, max, xnow);
569 if (data[t].xtime != xnow) {
570 for (i = max - 1; i >= 0; --i) {
571 if (data[i].xtime == xnow) {
572 t = i;
573 break;
576 if (i < 0) {
577 *tail = t = (t + 1) % max;
578 data[t].xtime = xnow;
579 memset(data[t].counter, 0, sizeof(data[0].counter));
583 for (i = 0; i < MAX_COUNTER; ++i) {
584 data[t].counter[i] += counter[i];
587 // _dprintf("%s: counter[0]=%llu counter[1]=%llu tail=%d\n", __FUNCTION__, data[t].counter[0], data[t].counter[1], *tail);
591 static void calc(void)
593 FILE *f;
594 char buf[512];
595 char *ipaddr = NULL;
596 // char *p;
597 unsigned long counter[MAX_COUNTER];
598 speed_t *sp;
599 // data_t *da;
600 int i, j;
601 time_t now;
602 time_t mon;
603 struct tm *tms;
604 uint32_t c;
605 uint32_t sc;
606 unsigned long diff;
607 long tick;
608 int n;
609 char *exclude;
611 now = time(0);
612 exclude = nvram_safe_get("cstats_exclude");
614 _dprintf("%s: cstats_exclude='%s'\n", __FUNCTION__, exclude);
616 unsigned long tx;
617 unsigned long rx;
618 char ip[INET_ADDRSTRLEN];
620 char br;
621 char name[] = "/proc/net/ipt_account/lanX";
623 for(br=0 ; br<=3 ; br++) {
624 char bridge[2] = "0";
625 if (br!=0)
626 bridge[0]+=br;
627 else
628 strcpy(bridge, "");
630 sprintf(name, "/proc/net/ipt_account/lan%s", bridge);
632 if ((f = fopen(name, "r")) == NULL) continue;
634 // _dprintf("%s: file %s opened\n", __FUNCTION__, name);
635 while (fgets(buf, sizeof(buf), f)) {
636 // _dprintf("%s: read\n", __FUNCTION__);
637 if(sscanf(buf,
638 "ip = %s bytes_src = %lu %*u %*u %*u %*u packets_src = %*u %*u %*u %*u %*u bytes_dest = %lu %*u %*u %*u %*u packets_dest = %*u %*u %*u %*u %*u time = %*u",
639 ip, &rx, &tx) != 3 ) continue;
640 // _dprintf("%s: %s tx=%lu rx=%lu\n", __FUNCTION__, ip, tx, rx);
641 if ((tx < 1) || (rx < 1)) continue;
643 counter[0] = rx;
644 counter[1] = tx;
645 ipaddr=ip;
647 // _dprintf("%s: %s tx=%lu rx=%lu\n", __FUNCTION__, ipaddr, tx, rx);
649 if (find_word(exclude, ipaddr)) continue;
651 // if ((counter[0] < 1) || (counter[1] < 1)) continue;
654 if ((f = fopen("/proc/net/dev", "r")) == NULL) return;
655 fgets(buf, sizeof(buf), f); // header
656 fgets(buf, sizeof(buf), f); // "
657 while (fgets(buf, sizeof(buf), f)) {
658 if ((p = strchr(buf, ':')) == NULL) continue;
659 *p = 0;
660 if ((ipaddr = strrchr(buf, ' ')) == NULL) ipaddr = buf;
661 else ++ipaddr;
662 if ((strcmp(ipaddr, "lo") == 0) || (find_word(exclude, ipaddr))) continue;
664 // <rx bytes, packets, errors, dropped, fifo errors, frame errors, compressed, multicast><tx ...>
665 if (sscanf(p + 1, "%lu%*u%*u%*u%*u%*u%*u%*u%lu", &counter[0], &counter[1]) != 2) continue;
668 sp = speed;
669 for (i = speed_count; i > 0; --i) {
670 if (strcmp(sp->ipaddr, ipaddr) == 0) break;
671 ++sp;
673 if (i == 0) {
674 if (speed_count >= MAX_SPEED_IP) continue;
676 _dprintf("%s: add %s as #%d\n", __FUNCTION__, ipaddr, speed_count);
678 i = speed_count++;
679 sp = &speed[i];
680 memset(sp, 0, sizeof(*sp));
681 strcpy(sp->ipaddr, ipaddr);
682 sp->sync = 1;
683 sp->utime = uptime;
685 if (sp->sync) {
686 _dprintf("%s: sync %s\n", __FUNCTION__, ipaddr);
687 sp->sync = -1;
689 memcpy(sp->last, counter, sizeof(sp->last));
690 memset(counter, 0, sizeof(counter));
692 else {
693 sp->sync = -1;
695 tick = uptime - sp->utime;
696 n = tick / INTERVAL;
697 if (n < 1) {
698 _dprintf("%s: %s is a little early... %lu < %d\n", __FUNCTION__, ipaddr, tick, INTERVAL);
699 continue;
702 sp->utime += (n * INTERVAL);
703 _dprintf("%s: %s n=%d tick=%lu\n", __FUNCTION__, ipaddr, n, tick);
705 for (i = 0; i < MAX_COUNTER; ++i) {
706 c = counter[i];
707 sc = sp->last[i];
708 if (c < sc) {
709 diff = (0xFFFFFFFF - sc) + c;
710 if (diff > MAX_ROLLOVER) diff = 0;
712 else {
713 diff = c - sc;
715 sp->last[i] = c;
716 counter[i] = diff;
719 for (j = 0; j < n; ++j) {
720 sp->tail = (sp->tail + 1) % MAX_NSPEED;
721 for (i = 0; i < MAX_COUNTER; ++i) {
722 sp->speed[sp->tail][i] = counter[i] / n;
727 // todo: split, delay
729 if (now > Y2K) { /* Skip this if the time&date is not set yet */
731 for (i = history.howmany; i > 0; --i) {
732 // _dprintf("%s: i=%d '%s' '%s'\n", __FUNCTION__, i, history.ipaddr[i], ipaddr);
733 if (strcmp(history.ipaddr[i], ipaddr) == 0) break;
735 if (i == 0) {
736 if (history.howmany >= MAX_SPEED_IP) continue;
737 if (strcmp(history.ipaddr[i], ipaddr) != 0) {
738 strncpy(history.ipaddr[history.howmany], ipaddr, INET_ADDRSTRLEN);
739 _dprintf("%s: history add %s/%s as #%d\n", __FUNCTION__, ipaddr, history.ipaddr[history.howmany], history.howmany);
740 i=history.howmany;
741 history.howmany++;
745 // _dprintf("%s: calling bump i=%d %s total #%d history.dailyp[i]=%d\n", __FUNCTION__, i, ipaddr, history.howmany, history.dailyp[i]);
746 tms = localtime(&now);
747 bump(history.daily[i], &history.dailyp[i], MAX_NDAILY,
748 (tms->tm_year << 16) | ((uint32_t)tms->tm_mon << 8) | tms->tm_mday, counter);
750 // _dprintf("%s: calling bump i=%d %s total #%d history.monthlyp=%d\n", __FUNCTION__, i, ipaddr, history.howmany, history.monthlyp);
751 n = nvram_get_int("cstats_offset");
752 if ((n < 1) || (n > 31)) n = 1;
753 mon = now + ((1 - n) * (60 * 60 * 24));
754 tms = localtime(&mon);
755 bump(history.monthly[i], &history.monthlyp[i], MAX_NMONTHLY,
756 (tms->tm_year << 16) | ((uint32_t)tms->tm_mon << 8), counter);
759 fclose(f);
762 // cleanup stale entries
763 for (i = 0; i < speed_count; ++i) {
764 sp = &speed[i];
765 if (sp->sync == -1) {
766 sp->sync = 0;
767 continue;
769 if (((uptime - sp->utime) > (10 * SMIN)) || (find_word(exclude, sp->ipaddr))) {
770 // if ((uptime - sp->utime) > (10 * SMIN)) {
771 _dprintf("%s: #%d removing. > time limit or excluded\n", __FUNCTION__, i);
772 --speed_count;
773 memcpy(sp, sp + 1, (speed_count - i) * sizeof(speed[0]));
775 else {
776 _dprintf("%s: %s not found setting sync=1, %d\n", __FUNCTION__, sp->ipaddr, i);
777 sp->sync = 1;
781 for (i = 0; i < history.howmany; ++i) {
782 if ((find_word(exclude, history.ipaddr[i]))) {
783 --history.howmany;
784 _dprintf("%s: #%d removing %s > excluded\n", __FUNCTION__, i, history.ipaddr[i]);
785 memcpy(history.ipaddr[i], history.ipaddr[i+1], (history.howmany - i) * sizeof(history.ipaddr[0]));
786 memcpy(history.daily[i], history.daily[i+1], (history.howmany - i) * sizeof(history.daily[0]));
787 history.dailyp[i] = history.dailyp[i+1];
788 memcpy(history.monthly[i], history.monthly[i+1], (history.howmany - i) * sizeof(history.monthly[0]));
789 history.monthlyp[i] = history.monthlyp[i+1];
793 // todo: total > user
794 if (uptime >= save_utime) {
795 save(0);
796 save_utime = uptime + get_stime();
797 _dprintf("%s: uptime = %lum, save_utime = %lum\n", __FUNCTION__, uptime / 60, save_utime / 60);
801 static void sig_handler(int sig) {
802 switch (sig) {
803 case SIGTERM:
804 case SIGINT:
805 gotterm = 1;
806 break;
807 case SIGHUP:
808 gothup = 1;
809 break;
810 case SIGUSR1:
811 gotuser = 1;
812 break;
813 case SIGUSR2:
814 gotuser = 2;
815 break;
819 int main(int argc, char *argv[]) {
820 struct sigaction sa;
821 long z;
822 int new;
824 printf("cstats - Copyright (C) 2011 Augusto Bott\n");
825 printf("based on rstats - Copyright (C) 2006-2009 Jonathan Zarate\n\n");
827 if (fork() != 0) return 0;
829 openlog("cstats", LOG_PID, LOG_USER);
831 new = 0;
832 if (argc > 1) {
833 if (strcmp(argv[1], "--new") == 0) {
834 new = 1;
835 _dprintf("new=1\n");
839 clear_history();
840 unlink("/var/tmp/cstats-load");
842 sa.sa_handler = sig_handler;
843 sa.sa_flags = 0;
844 sigemptyset(&sa.sa_mask);
845 sigaction(SIGUSR1, &sa, NULL);
846 sigaction(SIGUSR2, &sa, NULL);
847 sigaction(SIGHUP, &sa, NULL);
848 sigaction(SIGTERM, &sa, NULL);
849 sigaction(SIGINT, &sa, NULL);
851 load(new);
853 z = uptime = get_uptime();
854 while (1) {
855 while (uptime < z) {
856 sleep(z - uptime);
857 if (gothup) {
858 if (unlink("/var/tmp/cstats-load") == 0) load_new();
859 else save(0);
860 gothup = 0;
862 if (gotterm) {
863 save(!nvram_match("cstats_sshut", "1"));
864 exit(0);
866 if (gotuser == 1) {
867 save_speedjs(z - get_uptime());
868 gotuser = 0;
870 else if (gotuser == 2) {
871 save_histjs();
872 gotuser = 0;
874 uptime = get_uptime();
876 calc();
877 z += INTERVAL;
880 return 0;