dnscrypto-proxy: Update to release 1.3.0
[tomato.git] / release / src / router / cstats / cstats.c
bloba2c5e16f66466159f31bd93fd40ec9eebd106ec9
1 /*
3 cstats
4 Copyright (C) 2011-2012 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 #include "cstats.h"
42 long save_utime;
43 char save_path[96];
44 long uptime;
46 volatile int gothup = 0;
47 volatile int gotuser = 0;
48 volatile int gotterm = 0;
50 #ifdef DEBUG_CSTATS
51 void Node_print(Node *self, FILE *stream) {
52 fprintf(stream, "%s", self->ipaddr);
55 void Node_printer(Node *self, void *stream) {
56 Node_print(self, (FILE *)stream);
57 fprintf((FILE *)stream, " ");
60 void Tree_info(void) {
61 _dprintf("Tree = ");
62 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_printer, stdout);
63 _dprintf("\n");
64 _dprintf("Tree depth = %d\n", TREE_DEPTH(&tree, linkage));
66 #endif
68 Node *Node_new(char *ipaddr) {
69 Node *self;
70 if ((self = malloc(sizeof(Node))) != NULL) {
71 memset(self, 0, sizeof(Node));
72 self->id = CURRENT_ID;
73 strncpy(self->ipaddr, ipaddr, INET_ADDRSTRLEN);
74 _dprintf("%s: new node ip=%s, version=%d, sizeof(Node)=%d (bytes)\n", __FUNCTION__, self->ipaddr, self->id, sizeof(Node));
76 return self;
79 int Node_compare(Node *lhs, Node *rhs) {
80 return strncmp(lhs->ipaddr, rhs->ipaddr, INET_ADDRSTRLEN);
83 Tree tree = TREE_INITIALIZER(Node_compare);
85 static int get_stime(void) {
86 #ifdef DEBUG_STIME
87 return 90;
88 #else
89 int t;
90 t = nvram_get_int("cstats_stime");
91 if (t < 1) t = 1;
92 else if (t > 8760) t = 8760;
93 return t * SHOUR;
94 #endif
97 void Node_save(Node *self, void *t) {
98 node_print_mode_t *info = (node_print_mode_t *)t;
99 if(fwrite(self, sizeof(Node), 1, info->stream) > 0) {
100 info->kn++;
104 static int save_history_from_tree(const char *fname) {
105 FILE *f;
106 node_print_mode_t info;
107 char s[256];
109 info.kn=0;
110 _dprintf("%s: fname=%s\n", __FUNCTION__, fname);
112 unlink(uncomp_fn);
113 if ((f = fopen(uncomp_fn, "wb")) != NULL) {
114 info.mode=0;
115 info.stream=f;
116 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_save, &info);
117 fclose(f);
119 sprintf(s, "%s.gz", fname);
120 unlink(s);
122 if (rename(uncomp_fn, fname) == 0) {
123 sprintf(s, "gzip %s", fname);
124 system(s);
127 return info.kn;
130 static void save(int quick) {
131 int i;
132 int n;
133 int b;
134 char hgz[256];
135 char tmp[256];
136 char bak[256];
137 char bkp[256];
138 time_t now;
139 struct tm *tms;
140 static int lastbak = -1;
142 _dprintf("%s: quick=%d\n", __FUNCTION__, quick);
144 f_write("/var/lib/misc/cstats-stime", &save_utime, sizeof(save_utime), 0, 0);
146 n = save_history_from_tree(history_fn);
147 _dprintf("%s: saved %d records from tree on file %s\n", __FUNCTION__, n, history_fn);
149 _dprintf("%s: write source=%s\n", __FUNCTION__, save_path);
150 f_write_string(source_fn, save_path, 0, 0);
152 if (quick) {
153 return;
156 sprintf(hgz, "%s.gz", history_fn);
158 if (save_path[0] != 0) {
159 strcpy(tmp, save_path);
160 strcat(tmp, ".tmp");
162 for (i = 15; i > 0; --i) {
163 if (!wait_action_idle(10)) {
164 _dprintf("%s: busy, not saving\n", __FUNCTION__);
166 else {
167 _dprintf("%s: cp %s %s\n", __FUNCTION__, hgz, tmp);
168 if (eval("cp", hgz, tmp) == 0) {
169 _dprintf("%s: copy ok\n", __FUNCTION__);
171 if (!nvram_match("cstats_bak", "0")) {
172 now = time(0);
173 tms = localtime(&now);
174 if (lastbak != tms->tm_yday) {
175 strcpy(bak, save_path);
176 n = strlen(bak);
177 if ((n > 3) && (strcmp(bak + (n - 3), ".gz") == 0)) n -= 3;
178 // sprintf(bak + n, "_%d.bak", ((tms->tm_yday / 7) % 3) + 1);
179 // if (eval("cp", save_path, bak) == 0) lastbak = tms->tm_yday;
180 strcpy(bkp, bak);
181 for (b = HI_BACK-1; b > 0; --b) {
182 sprintf(bkp + n, "_%d.bak", b + 1);
183 sprintf(bak + n, "_%d.bak", b);
184 rename(bak, bkp);
186 if (eval("cp", "-p", save_path, bak) == 0) lastbak = tms->tm_yday;
190 _dprintf("%s: rename %s %s\n", __FUNCTION__, tmp, save_path);
191 if (rename(tmp, save_path) == 0) {
192 _dprintf("%s: rename ok\n", __FUNCTION__);
193 break;
198 // might not be ready
199 sleep(3);
200 if (gotterm) break;
205 static int load_history_to_tree(const char *fname) {
206 int n;
207 FILE *f;
208 char s[256];
209 Node tmp;
210 Node *ptr;
211 char *exclude;
213 exclude = nvram_safe_get("cstats_exclude");
214 _dprintf("%s: cstats_exclude='%s'\n", __FUNCTION__, exclude);
215 _dprintf("%s: fname=%s\n", __FUNCTION__, fname);
216 unlink(uncomp_fn);
218 n = -1; // Initial value, will be returned if we failed to parse a data file
219 sprintf(s, "gzip -dc %s > %s", fname, uncomp_fn);
220 if (system(s) == 0) {
221 if ((f = fopen(uncomp_fn, "rb")) != NULL) {
222 n = 0; // Initial counter
223 while (fread(&tmp, sizeof(Node), 1, f) > 0) {
224 if ((find_word(exclude, tmp.ipaddr))) {
225 _dprintf("%s: not loading excluded ip '%s'\n", __FUNCTION__, tmp.ipaddr);
226 continue;
229 if (tmp.id == CURRENT_ID) {
230 _dprintf("%s: found data for ip %s\n", __FUNCTION__, tmp.ipaddr);
232 ptr = TREE_FIND(&tree, _Node, linkage, &tmp);
233 if (ptr) {
234 _dprintf("%s: removing/reloading new data for ip %s\n", __FUNCTION__, ptr->ipaddr);
235 TREE_REMOVE(&tree, _Node, linkage, ptr);
236 free(ptr);
237 ptr = NULL;
240 TREE_INSERT(&tree, _Node, linkage, Node_new(tmp.ipaddr));
242 ptr = TREE_FIND(&tree, _Node, linkage, &tmp);
244 memcpy(ptr->daily, &tmp.daily, sizeof(data_t) * MAX_NDAILY);
245 ptr->dailyp = tmp.dailyp;
246 memcpy(ptr->monthly, &tmp.monthly, sizeof(data_t) * MAX_NMONTHLY);
247 ptr->monthlyp = tmp.monthlyp;
249 ptr->utime = tmp.utime;
250 memcpy(ptr->speed, &tmp.speed, sizeof(uint64_t) * MAX_NSPEED * MAX_COUNTER);
251 memcpy(ptr->last, &tmp.last, sizeof(uint64_t) * MAX_COUNTER);
252 ptr->tail = tmp.tail;
253 // ptr->sync = tmp.sync;
254 ptr->sync = -1;
256 if (ptr->utime > uptime) {
257 ptr->utime = uptime;
258 ptr->sync = 1;
261 ++n;
262 } else {
263 _dprintf("%s: data for ip '%s' version %d not loaded (current version is %d)\n", __FUNCTION__, tmp.ipaddr, tmp.id, CURRENT_ID);
267 fclose(f);
270 else {
271 _dprintf("%s: %s != 0\n", __FUNCTION__, s);
273 unlink(uncomp_fn);
275 if (n == -1)
276 _dprintf("%s: Failed to parse the data file!\n", __FUNCTION__);
277 else
278 _dprintf("%s: Loaded %d records\n", __FUNCTION__, n);
280 return n;
283 static int load_history(const char *fname) {
284 _dprintf("%s: fname=%s\n", __FUNCTION__, fname);
285 return load_history_to_tree(fname);
288 /* Try loading from the backup versions.
289 * We'll try from oldest to newest, then
290 * retry the requested one again last. In case the drive mounts while
291 * we are trying to find a good version.
293 static int try_hardway(const char *fname) {
294 char fn[256];
295 int n, b, found = 0;
297 strcpy(fn, fname);
298 n = strlen(fn);
299 if ((n > 3) && (strcmp(fn + (n - 3), ".gz") == 0))
300 n -= 3;
301 for (b = HI_BACK; b > 0; --b) {
302 sprintf(fn + n, "_%d.bak", b);
303 found |= load_history(fn);
305 found |= load_history(fname);
307 return found;
310 static void load_new(void) {
311 char hgz[256];
313 sprintf(hgz, "%s.gz.new", history_fn);
314 if (load_history(hgz) >= 0) save(0);
315 unlink(hgz);
318 static void load(int new) {
319 int i;
320 long t;
321 int n;
322 char hgz[256];
323 unsigned char mac[6];
325 uptime = get_uptime();
327 _dprintf("%s: new=%d, uptime=%lu\n", __FUNCTION__, new, uptime);
329 strlcpy(save_path, nvram_safe_get("cstats_path"), sizeof(save_path) - 32);
330 if (((n = strlen(save_path)) > 0) && (save_path[n - 1] == '/')) {
331 ether_atoe(nvram_safe_get("et0macaddr"), mac);
332 sprintf(save_path + n, "tomato_cstats_%02x%02x%02x%02x%02x%02x.gz",
333 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
336 if (f_read("/var/lib/misc/cstats-stime", &save_utime, sizeof(save_utime)) != sizeof(save_utime)) {
337 save_utime = 0;
339 t = uptime + get_stime();
340 if ((save_utime < uptime) || (save_utime > t)) save_utime = t;
341 _dprintf("%s: uptime = %lum, save_utime = %lum\n", __FUNCTION__, uptime / 60, save_utime / 60);
343 sprintf(hgz, "%s.gz", history_fn);
345 if (new) {
346 unlink(hgz);
347 save_utime = 0;
348 return;
351 if (save_path[0] != 0) {
352 i = 1;
353 while (1) {
354 if (wait_action_idle(10)) {
356 // cifs quirk: try forcing refresh
357 eval("ls", save_path);
359 /* If we can't access the path, keep trying - maybe it isn't mounted yet.
360 * If we can, and we can sucessfully load it, oksy.
361 * If we can, and we cannot load it, then maybe it has been deleted, or
362 * maybe it's corrupted (like 0 bytes long).
363 * In these cases, try the backup files.
365 // if (load_history(save_path)) {
366 if ((load_history(save_path) >= 0) || (try_hardway(save_path) >= 0)) {
367 f_write_string(source_fn, save_path, 0, 0);
368 break;
372 // not ready...
373 sleep(i);
374 if ((i *= 2) > 900) i = 900; // 15m
376 if (gotterm) {
377 save_path[0] = 0;
378 return;
381 if (i > (3 * 60)) {
382 syslog(LOG_WARNING, "Problem loading %s. Still trying...", save_path);
388 void Node_print_speedjs(Node *self, void *t) {
389 int j, k, p;
390 uint64_t total, tmax;
391 uint64_t n;
392 char c;
394 node_print_mode_t *info = (node_print_mode_t *)t;
396 fprintf(info->stream, "%s'%s': {\n", info->kn ? " },\n" : "", self->ipaddr);
397 for (j = 0; j < MAX_COUNTER; ++j) {
398 total = tmax = 0;
399 fprintf(info->stream, "%sx: [", j ? ",\n t" : " r");
400 p = self->tail;
401 for (k = 0; k < MAX_NSPEED; ++k) {
402 p = (p + 1) % MAX_NSPEED;
403 n = self->speed[p][j];
404 fprintf(info->stream, "%s%llu", k ? "," : "", n);
405 total += n;
406 if (n > tmax) tmax = n;
408 fprintf(info->stream, "],\n");
410 c = j ? 't' : 'r';
411 fprintf(info->stream, " %cx_avg: %llu,\n %cx_max: %llu,\n %cx_total: %llu",
412 c, total / MAX_NSPEED, c, tmax, c, total);
414 info->kn++;
417 static void save_speedjs(long next) {
418 FILE *f;
420 if ((f = fopen("/var/tmp/cstats-speed.js", "w")) == NULL) return;
422 node_print_mode_t info;
423 info.mode=0;
424 info.stream=f;
425 info.kn=0;
427 fprintf(f, "\nspeed_history = {\n");
428 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_print_speedjs, &info);
429 fprintf(f, "%s_next: %ld};\n", info.kn ? "},\n" : "", ((next >= 1) ? next : 1));
431 fclose(f);
433 rename("/var/tmp/cstats-speed.js", "/var/spool/cstats-speed.js");
436 void Node_print_datajs(Node *self, void *t) {
437 data_t *data;
438 int p, max, k;
440 node_print_mode_t *info = (node_print_mode_t *)t;
442 if (info->mode == DAILY) {
443 data = self->daily;
444 p = self->dailyp;
445 max = MAX_NDAILY;
448 else {
449 data = self->monthly;
450 p = self->monthlyp;
451 max = MAX_NMONTHLY;
454 for (k = max; k > 0; --k) {
455 p = (p + 1) % max;
456 if (data[p].xtime == 0) continue;
457 fprintf(info->stream, "%s[0x%lx,'%s',%llu,%llu]", info->kn ? "," : "",
458 (unsigned long)data[p].xtime, self->ipaddr, data[p].counter[0] / K, data[p].counter[1] / K);
459 info->kn++;
463 static void save_datajs(FILE *f, int mode) {
464 node_print_mode_t info;
465 info.mode=mode;
466 info.stream=f;
467 info.kn=0;
468 fprintf(f, "\n%s_history = [\n", (mode == DAILY) ? "daily" : "monthly");
469 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_print_datajs, &info);
470 fprintf(f, "\n];\n");
473 static void save_histjs(void) {
474 FILE *f;
476 if ((f = fopen("/var/tmp/cstats-history.js", "w")) != NULL) {
477 save_datajs(f, DAILY);
478 save_datajs(f, MONTHLY);
479 fclose(f);
480 rename("/var/tmp/cstats-history.js", "/var/spool/cstats-history.js");
484 static void bump(data_t *data, int *tail, int max, uint32_t xnow, uint64_t *counter) {
485 int t, i;
487 t = *tail;
488 if (data[t].xtime != xnow) {
489 for (i = max - 1; i >= 0; --i) {
490 if (data[i].xtime == xnow) {
491 t = i;
492 break;
495 if (i < 0) {
496 *tail = t = (t + 1) % max;
497 data[t].xtime = xnow;
498 memset(data[t].counter, 0, sizeof(data[0].counter));
501 for (i = 0; i < MAX_COUNTER; ++i) {
502 data[t].counter[i] += counter[i];
506 void Node_housekeeping(Node *self, void *info) {
507 if (self) {
508 if (self->sync == -1) {
509 self->sync = 0;
510 } else {
511 self->sync = 1;
516 static void calc(void) {
517 FILE *f;
518 char buf[512];
519 char *ipaddr = NULL;
520 uint64_t counter[MAX_COUNTER];
521 int i, j;
522 time_t now;
523 time_t mon;
524 struct tm *tms;
525 uint64_t c;
526 uint64_t sc;
527 uint64_t diff;
528 long tick;
529 int n;
530 char *exclude = NULL;
531 char *include = NULL;
533 Node *ptr = NULL;
534 Node test;
536 now = time(0);
538 exclude = strdup(nvram_safe_get("cstats_exclude"));
539 _dprintf("%s: cstats_exclude='%s'\n", __FUNCTION__, exclude);
541 include = strdup(nvram_safe_get("cstats_include"));
542 _dprintf("%s: cstats_include='%s'\n", __FUNCTION__, include);
545 uint64_t tx;
546 uint64_t rx;
547 char ip[INET_ADDRSTRLEN];
548 char br;
550 char name[] = "/proc/net/ipt_account/lanX";
552 for(br=0 ; br<=3 ; br++) {
554 char bridge[2] = "0";
555 if (br!=0)
556 bridge[0]+=br;
557 else
558 strcpy(bridge, "");
560 sprintf(name, "/proc/net/ipt_account/lan%s", bridge);
562 if ((f = fopen(name, "r")) == NULL) continue;
564 while (fgets(buf, sizeof(buf), f)) {
565 if(sscanf(buf,
566 "ip = %s bytes_src = %llu %*u %*u %*u %*u packets_src = %*u %*u %*u %*u %*u bytes_dst = %llu %*u %*u %*u %*u packets_dst = %*u %*u %*u %*u %*u time = %*u",
567 ip, &rx, &tx) != 3 ) continue;
568 #ifdef DEBUG_CSTATS
569 _dprintf("%s: %s tx=%llu rx=%llu\n", __FUNCTION__, ip, tx, rx);
570 #endif
572 if (find_word(exclude, ip)) continue;
573 if ((tx < 1) && (rx < 1)) continue;
575 counter[0] = tx;
576 counter[1] = rx;
577 ipaddr=ip;
579 strncpy(test.ipaddr, ipaddr, INET_ADDRSTRLEN);
580 ptr = TREE_FIND(&tree, _Node, linkage, &test);
582 if ( (ptr) || (nvram_get_int("cstats_all")) || (find_word(include, ipaddr)) ) {
584 if (!ptr) {
585 _dprintf("%s: new ip: %s\n", __FUNCTION__, ipaddr);
586 TREE_INSERT(&tree, _Node, linkage, Node_new(ipaddr));
587 ptr = TREE_FIND(&tree, _Node, linkage, &test);
588 ptr->sync = 1;
589 ptr->utime = uptime;
591 #ifdef DEBUG_CSTATS
592 Tree_info();
593 #endif
594 _dprintf("%s: sync[%s]=%d\n", __FUNCTION__, ptr->ipaddr, ptr->sync);
595 if (ptr->sync) {
596 _dprintf("%s: sync[%s] changed to -1\n", __FUNCTION__, ptr->ipaddr);
597 ptr->sync = -1;
598 #ifdef DEBUG_CSTATS
599 for (i = 0; i < MAX_COUNTER; ++i) {
600 _dprintf("%s: counter[%d]=%llu ptr->last[%d]=%llu\n", __FUNCTION__, i, counter[i], i, ptr->last[i]);
602 #endif
603 memcpy(ptr->last, counter, sizeof(ptr->last));
604 memset(counter, 0, sizeof(counter));
605 for (i = 0; i < MAX_COUNTER; ++i) {
606 _dprintf("%s: counter[%d]=%llu ptr->last[%d]=%llu\n", __FUNCTION__, i, counter[i], i, ptr->last[i]);
609 else {
610 #ifdef DEBUG_CSTATS
611 _dprintf("%s: sync[%s] = %d \n", __FUNCTION__, ptr->ipaddr, ptr->sync);
612 #endif
613 ptr->sync = -1;
614 _dprintf("%s: sync[%s] = %d \n", __FUNCTION__, ptr->ipaddr, ptr->sync);
615 tick = uptime - ptr->utime;
616 n = tick / INTERVAL;
617 if (n < 1) {
618 _dprintf("%s: %s is a little early... %lu < %d\n", __FUNCTION__, ipaddr, tick, INTERVAL);
619 continue; // Don't update the tree this time
620 } else {
621 ptr->utime += (n * INTERVAL);
622 _dprintf("%s: %s n=%d tick=%lu utime=%lu ptr->utime=%lu\n", __FUNCTION__, ipaddr, n, tick, uptime, ptr->utime);
623 for (i = 0; i < MAX_COUNTER; ++i) {
624 c = counter[i];
625 sc = ptr->last[i];
626 #ifdef DEBUG_CSTATS
627 _dprintf("%s: counter[%d]=%llu ptr->last[%d]=%llu c=%llu sc=%llu\n", __FUNCTION__, i, counter[i], i, ptr->last[i], c, sc);
628 #endif
629 if (c < sc) {
630 diff = (0xFFFFFFFF - sc) + c;
631 if (diff > MAX_ROLLOVER) diff = 0;
633 else {
634 diff = c - sc;
636 ptr->last[i] = c;
637 counter[i] = diff;
638 _dprintf("%s: counter[%d]=%llu ptr->last[%d]=%llu c=%llu sc=%llu diff=%llu\n", __FUNCTION__, i, counter[i], i, ptr->last[i], c, sc, diff);
640 _dprintf("%s: ip=%s n=%d ptr->tail=%d\n", __FUNCTION__, ptr->ipaddr, n, ptr->tail);
641 for (j = 0; j < n; ++j) {
642 ptr->tail = (ptr->tail + 1) % MAX_NSPEED;
643 #ifdef DEBUG_CSTATS
644 _dprintf("%s: ip=%s j=%d n=%d ptr->tail=%d\n", __FUNCTION__, ptr->ipaddr, j, n, ptr->tail);
645 #endif
646 for (i = 0; i < MAX_COUNTER; ++i) {
647 ptr->speed[ptr->tail][i] = counter[i] / n;
650 _dprintf("%s: ip=%s j=%d n=%d ptr->tail=%d\n", __FUNCTION__, ptr->ipaddr, j, n, ptr->tail);
654 if (now > Y2K) { /* Skip this if the time&date is not set yet */
655 #ifdef DEBUG_CSTATS
656 _dprintf("%s: calling bump %s ptr->dailyp=%d\n", __FUNCTION__, ptr->ipaddr, ptr->dailyp);
657 #endif
658 tms = localtime(&now);
659 bump(ptr->daily, &ptr->dailyp, MAX_NDAILY,
660 (tms->tm_year << 16) | ((uint32_t)tms->tm_mon << 8) | tms->tm_mday, counter);
662 #ifdef DEBUG_CSTATS
663 _dprintf("%s: calling bump %s ptr->monthlyp=%d\n", __FUNCTION__, ptr->ipaddr, ptr->monthlyp);
664 #endif
665 n = nvram_get_int("cstats_offset");
666 if ((n < 1) || (n > 31)) n = 1;
667 mon = now + ((1 - n) * (60 * 60 * 24));
668 tms = localtime(&mon);
669 bump(ptr->monthly, &ptr->monthlyp, MAX_NMONTHLY,
670 (tms->tm_year << 16) | ((uint32_t)tms->tm_mon << 8), counter);
675 fclose(f);
678 // remove/exclude history (if we still have any data previously stored)
679 char *nvp, *b;
680 nvp = exclude;
681 if (nvp) {
682 while ((b = strsep(&nvp, ",")) != NULL) {
683 _dprintf("%s: check exclude='%s'\n", __FUNCTION__, b);
684 strncpy(test.ipaddr, b, INET_ADDRSTRLEN);
685 ptr = TREE_FIND(&tree, _Node, linkage, &test);
686 if (ptr) {
687 _dprintf("%s: excluding '%s'\n", __FUNCTION__, ptr->ipaddr);
688 TREE_REMOVE(&tree, _Node, linkage, ptr);
689 free(ptr);
690 ptr = NULL;
695 // cleanup remaining entries for next time
696 TREE_FORWARD_APPLY(&tree, _Node, linkage, Node_housekeeping, NULL);
698 // todo: total > user ???
699 if (uptime >= save_utime) {
700 save(0);
701 save_utime = uptime + get_stime();
702 _dprintf("%s: uptime = %lum, save_utime = %lum\n", __FUNCTION__, uptime / 60, save_utime / 60);
705 _dprintf("%s: ====================================\n", __FUNCTION__);
707 //QUIT:
708 if (exclude) {
709 free(exclude);
710 exclude = NULL;
712 if (include) {
713 free(include);
714 include = NULL;
718 static void sig_handler(int sig) {
719 switch (sig) {
720 case SIGTERM:
721 case SIGINT:
722 gotterm = 1;
723 break;
724 case SIGHUP:
725 gothup = 1;
726 break;
727 case SIGUSR1:
728 gotuser = 1;
729 break;
730 case SIGUSR2:
731 gotuser = 2;
732 break;
736 int main(int argc, char *argv[]) {
738 struct sigaction sa;
739 long z;
740 int new;
742 printf("cstats - Copyright (C) 2011-2012 Augusto Bott\n");
743 printf("based on rstats - Copyright (C) 2006-2009 Jonathan Zarate\n\n");
745 if (fork() != 0) return 0;
747 openlog("cstats", LOG_PID, LOG_USER);
749 new = 0;
750 if (argc > 1) {
751 if (strcmp(argv[1], "--new") == 0) {
752 new = 1;
753 _dprintf("new=1\n");
757 unlink("/var/tmp/cstats-load");
759 sa.sa_handler = sig_handler;
760 sa.sa_flags = 0;
761 sigemptyset(&sa.sa_mask);
762 sigaction(SIGUSR1, &sa, NULL);
763 sigaction(SIGUSR2, &sa, NULL);
764 sigaction(SIGHUP, &sa, NULL);
765 sigaction(SIGTERM, &sa, NULL);
766 sigaction(SIGINT, &sa, NULL);
768 load(new);
770 z = uptime = get_uptime();
771 while (1) {
772 while (uptime < z) {
773 sleep(z - uptime);
774 if (gothup) {
775 if (unlink("/var/tmp/cstats-load") == 0) load_new();
776 else save(0);
777 gothup = 0;
779 if (gotterm) {
780 save(!nvram_match("cstats_sshut", "1"));
781 exit(0);
783 if (gotuser == 1) {
784 save_speedjs(z - get_uptime());
785 gotuser = 0;
787 else if (gotuser == 2) {
788 save_histjs();
789 gotuser = 0;
791 uptime = get_uptime();
793 calc();
794 z += INTERVAL;
797 return 0;