1/*
2
3	rstats
4	Copyright (C) 2006-2009 Jonathan Zarate
5
6
7	This program is free software; you can redistribute it and/or
8	modify it under the terms of the GNU General Public License
9	as published by the Free Software Foundation; either version 2
10	of the License, or (at your option) any later version.
11
12	This program is distributed in the hope that it will be useful,
13	but WITHOUT ANY WARRANTY; without even the implied warranty of
14	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15	GNU General Public License for more details.
16
17*/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <signal.h>
24#include <time.h>
25#include <sys/types.h>
26#include <sys/sysinfo.h>
27#include <sys/stat.h>
28#include <stdint.h>
29#include <syslog.h>
30#include <ctype.h>
31
32#include <bcmnvram.h>
33#include <shutils.h>
34
35//#define DEBUG_NOISY
36//#define DEBUG_STIME
37
38#include <shared.h>
39
40#ifdef RTCONFIG_ISP_METER
41	#include <rtstate.h>
42#endif
43
44#ifdef RTCONFIG_QTN
45#include "web-qtn.h"
46#endif
47
48//#define DEBUG
49#define K 1024
50#define M (1024 * 1024)
51#define G (1024 * 1024 * 1024)
52
53#define SMIN	60
54#define	SHOUR	(60 * 60)
55#define	SDAY	(60 * 60 * 24)
56#define Y2K		946684800UL
57
58#define INTERVAL		30
59#if defined(RTCONFIG_WANPORT2)
60#define MAX_BW			2000
61#else
62#define MAX_BW			1000
63#endif
64
65#ifdef RTCONFIG_ISP_METER
66	#define MTD_WRITE_INTERVEL	60
67#endif
68
69#define MAX_NSPEED		((24 * SHOUR) / INTERVAL)
70#define MAX_NDAILY		62
71#define MAX_NMONTHLY	25
72/* INTERNET:	2
73 * WIRED:	1
74 * BRIDGE:	1
75 * WIFI_2G:	4
76 * WIFI_5G:	4 x number of 5g bands
77 */
78#define MAX_SPEED_IF	25
79
80#define MAX_ROLLOVER	(MAX_BW * INTERVAL / 8ULL * M)
81
82#define MAX_COUNTER	2
83#define RX 			0
84#define TX 			1
85
86#define DAILY		0
87#define MONTHLY		1
88
89#define ID_V0		0x30305352
90#define ID_V1		0x31305352
91#define CURRENT_ID	ID_V1
92
93#define HI_BACK		5
94
95#define RA_OFFSET_ISP_METER	0x4FF00
96
97enum if_id {
98	IFID_INTERNET = 0,	/* INTERNET */
99	IFID_INTERNET1,		/* INTERNET1 */
100	IFID_WIRED,		/* WIRED */
101	IFID_BRIDGE,		/* BRIDGE */
102	IFID_WIRELESS0,		/* WIRELESS0 */
103	IFID_WIRELESS0_1,	/* WIRELESS0.1 */
104	IFID_WIRELESS0_2,	/* WIRELESS0.2 */
105	IFID_WIRELESS0_3,	/* WIRELESS0.3 */
106	IFID_WIRELESS1,		/* WIRELESS1 */
107	IFID_WIRELESS1_1,	/* WIRELESS1.1 */
108	IFID_WIRELESS1_2,	/* WIRELESS1.2 */
109	IFID_WIRELESS1_3,	/* WIRELESS1.3 */
110	IFID_WIRELESS2,		/* WIRELESS2 */
111	IFID_WIRELESS2_1,	/* WIRELESS2.1 */
112	IFID_WIRELESS2_2,	/* WIRELESS2.2 */
113	IFID_WIRELESS2_3,	/* WIRELESS2.3 */
114
115	IFID_MAX
116};
117
118typedef struct {
119	uint32_t xtime;
120	uint64_t counter[MAX_COUNTER];
121} data_t;
122
123typedef struct {
124	uint32_t id;
125
126	data_t daily[MAX_NDAILY];
127	int dailyp;
128
129	data_t monthly[MAX_NMONTHLY];
130	int monthlyp;
131} history_t;
132
133typedef struct {
134	uint32_t id;
135
136	data_t daily[62];
137	int dailyp;
138
139	data_t monthly[12];
140	int monthlyp;
141} history_v0_t;
142
143typedef struct {
144	char ifname[12];
145	long utime;
146	unsigned long long speed[MAX_NSPEED][MAX_COUNTER];
147	unsigned long long last[MAX_COUNTER];
148	int tail;
149	int sync;
150} speed_t;
151
152history_t history;
153speed_t speed[MAX_SPEED_IF];
154int speed_count;
155long save_utime;
156char save_path[96];
157long current_uptime;
158
159volatile int gothup = 0;
160volatile int gotuser = 0;
161volatile int gotterm = 0;
162
163const char history_fn[] = "/var/lib/misc/rstats-history";
164const char speed_fn[] = "/var/lib/misc/rstats-speed";
165const char uncomp_fn[] = "/var/tmp/rstats-uncomp";
166const char source_fn[] = "/var/lib/misc/rstats-source";
167
168#ifdef RTCONFIG_ISP_METER
169#define ISP_METER_FILE	"/jffs/isp_meter"
170int isp_limit, isp_limit_time;
171unsigned long last_day_rx, last_day_tx, last_month_rx, last_month_tx;
172unsigned long today_rx, today_tx, month_rx, month_tx;
173unsigned long reset_base_day_rx, reset_base_day_tx, reset_base_month_rx, reset_base_month_tx;
174long cur_conn_time, last_connect_time, total_connect_time, reset_base_time;
175
176void reset_traffic_meter_counter(){
177	FILE *fp;
178        last_day_rx = 0;
179        last_day_tx = 0;
180        last_month_rx = 0;
181        last_month_tx = 0;
182        today_rx = 0;
183        today_tx = 0;
184        month_rx = 0;
185        month_tx = 0;
186        reset_base_day_rx = (history.daily[history.dailyp].counter[0]/K);
187        reset_base_day_tx = (history.daily[history.dailyp].counter[1]/K);
188        reset_base_month_rx = (history.monthly[history.monthlyp].counter[0]/K);
189        reset_base_month_tx = (history.monthly[history.monthlyp].counter[1]/K);
190        nvram_set("isp_day_tx", "0");
191        nvram_set("isp_day_rx", "0");
192        nvram_set("isp_month_tx", "0");
193        nvram_set("isp_month_rx", "0");
194        isp_limit = nvram_get_int("isp_limit");
195	isp_limit_time = nvram_get_int("isp_limit_time");
196
197	reset_base_time = cur_conn_time;
198	last_connect_time = 0;
199	total_connect_time = 0;
200
201        set_meter_file("isp_meter:0,0,0,end");
202
203        if(!nvram_match("isp_meter", "disable")
204          && !(nvram_match("wan0_state_t", "2") && nvram_match("wan0_auxstate_t", "0")) ) {
205                notify_rc_and_wait("isp_meter up");
206        }
207}
208
209void get_meter_file(char *meter_buf)
210{
211#ifdef CONFIG_BCMWL5
212	FILE *fp;
213        if (fp=fopen(ISP_METER_FILE, "r")) {
214                fgets(meter_buf, sizeof(meter_buf), fp);
215                fclose(fp);
216	}
217#else
218	FRead(meter_buf, RA_OFFSET_ISP_METER, 64);
219#endif
220	_dprintf("meter_buf: %s\n", meter_buf);
221	return;
222}
223
224int set_meter_file(char *meter_buf)
225{
226	if(meter_buf == NULL)
227		return 0;
228#ifdef CONFIG_BCMWL5
229        FILE *fp;
230        if (fp=fopen(ISP_METER_FILE, "w")) {
231               fprintf(fp, "%s", meter_buf);
232               fclose(fp);
233	}
234#else
235	FWrite(meter_buf, RA_OFFSET_ISP_METER, sizeof(meter_buf));
236#endif
237	return 1;
238}
239#endif
240
241static int get_stime(void)
242{
243#ifdef DEBUG_STIME
244	return 90;
245#else
246	int t;
247	t = nvram_get_int("rstats_stime");
248	if (t < 1) t = 1;
249		else if (t > 8760) t = 8760;
250	return t * SHOUR;
251#endif
252}
253
254static int comp(const char *path, void *buffer, int size)
255{
256	char s[256];
257
258	if (f_write(path, buffer, size, 0, 0) != size) return 0;
259
260	sprintf(s, "%s.gz", path);
261	unlink(s);
262
263	sprintf(s, "gzip %s", path);
264	return system(s) == 0;
265}
266
267static void save(int quick)
268{
269	int i;
270	char *bi, *bo;
271	int n;
272	int b;
273	char hgz[256];
274	char tmp[256];
275	char bak[256];
276	char bkp[256];
277	time_t now;
278	struct tm *tms;
279	static int lastbak = -1;
280
281	//_dprintf("%s: quick=%d\n", __FUNCTION__, quick);
282
283	f_write("/var/lib/misc/rstats-stime", &save_utime, sizeof(save_utime), 0, 0);
284
285	comp(speed_fn, speed, sizeof(speed[0]) * speed_count);
286
287/*
288	if ((now = time(0)) < Y2K) {
289		_dprintf("%s: time not set\n", __FUNCTION__);
290		return;
291	}
292*/
293
294	comp(history_fn, &history, sizeof(history));
295
296	//_dprintf("%s: write source=%s\n", __FUNCTION__, save_path);
297	f_write_string(source_fn, save_path, 0, 0);
298
299	if (quick) {
300		return;
301	}
302
303	sprintf(hgz, "%s.gz", history_fn);
304
305	if (strcmp(save_path, "*nvram") == 0) {
306		if (!wait_action_idle(10)) {
307			_dprintf("%s: busy, not saving\n", __FUNCTION__);
308			return;
309		}
310
311		if ((n = f_read_alloc(hgz, &bi, 20 * 1024)) > 0) {
312			if ((bo = malloc(base64_encoded_len(n) + 1)) != NULL) {
313				n = base64_encode(bi, bo, n);
314				bo[n] = 0;
315				nvram_set("rstats_data", bo);
316				if (!nvram_match("debug_nocommit", "1")) nvram_commit();
317
318				_dprintf("%s: nvram commit\n", __FUNCTION__);
319
320				free(bo);
321			}
322		}
323		free(bi);
324	}
325	else if (save_path[0] != 0) {
326		strcpy(tmp, save_path);
327		strcat(tmp, ".tmp");
328
329		for (i = 15; i > 0; --i) {
330			if (!wait_action_idle(10)) {
331				_dprintf("%s: busy, not saving\n", __FUNCTION__);
332			}
333			else {
334				_dprintf("%s: cp %s %s\n", __FUNCTION__, hgz, tmp);
335				if (eval("cp", hgz, tmp) == 0) {
336					_dprintf("%s: copy ok\n", __FUNCTION__);
337
338					if (!nvram_match("rstats_bak", "0")) {
339						now = time(0);
340						tms = localtime(&now);
341						if (lastbak != tms->tm_yday) {
342							strcpy(bak, save_path);
343							n = strlen(bak);
344							if ((n > 3) && (strcmp(bak + (n - 3), ".gz") == 0)) n -= 3;
345							strcpy(bkp, bak);
346							for (b = HI_BACK-1; b > 0; --b) {
347								sprintf(bkp + n, "_%d.bak", b + 1);
348								sprintf(bak + n, "_%d.bak", b);
349								rename(bak, bkp);
350							}
351							if (eval("cp", "-p", save_path, bak) == 0) lastbak = tms->tm_yday;
352						}
353					}
354
355					_dprintf("%s: rename %s %s\n", __FUNCTION__, tmp, save_path);
356					if (rename(tmp, save_path) == 0) {
357						_dprintf("%s: rename ok\n", __FUNCTION__);
358						break;
359					}
360				}
361			}
362
363			// might not be ready
364			sleep(3);
365			if (gotterm) break;
366		}
367	}
368}
369
370static int decomp(const char *fname, void *buffer, int size, int max)
371{
372	char s[256];
373	int n;
374	FILE *fp;
375	long file_size = 0;
376
377	_dprintf("%s: fname=%s\n", __FUNCTION__, fname);
378
379	unlink(uncomp_fn);
380
381	n = 0;
382	sprintf(s, "gzip -dc %s > %s", fname, uncomp_fn);
383	if (system(s)) {
384		_dprintf("%s: %s != 0\n", __func__, s);
385		goto exit_decomp;
386	}
387	if (!(fp = fopen(uncomp_fn, "r")))
388		goto exit_decomp;
389
390	fseek(fp, 0, SEEK_END);
391	file_size = ftell(fp);
392	fclose(fp);
393	if ((size * max) != file_size) {
394		_dprintf("%s: filesize mismatch! (%ld/%ld)\n", (size * max), file_size);
395		goto exit_decomp;
396	}
397
398	n = f_read(uncomp_fn, buffer, size * max);
399	_dprintf("%s: n=%d\n", __func__, n);
400	if (n <= 0)
401		n = 0;
402	else
403		n = n / size;
404
405exit_decomp:
406	unlink(uncomp_fn);
407	memset((char *)buffer + (size * n), 0, (max - n) * size);
408
409	return n;
410}
411
412static void clear_history(void)
413{
414	memset(&history, 0, sizeof(history));
415	history.id = CURRENT_ID;
416}
417
418static int load_history(const char *fname)
419{
420	history_t hist;
421
422	_dprintf("%s: fname=%s\n", __FUNCTION__, fname);
423
424	if ((decomp(fname, &hist, sizeof(hist), 1) != 1) || (hist.id != CURRENT_ID)) {
425		history_v0_t v0;
426
427		if ((decomp(fname, &v0, sizeof(v0), 1) != 1) || (v0.id != ID_V0)) {
428			_dprintf("%s: load failed\n", __FUNCTION__);
429			return 0;
430		}
431		else {
432			// --- temp conversion ---
433			clear_history();
434
435			// V0 -> V1
436			history.id = CURRENT_ID;
437			memcpy(history.daily, v0.daily, sizeof(history.daily));
438			history.dailyp = v0.dailyp;
439			memcpy(history.monthly, v0.monthly, sizeof(v0.monthly));	// v0 is just shorter
440			history.monthlyp = v0.monthlyp;
441		}
442	}
443	else {
444		memcpy(&history, &hist, sizeof(history));
445	}
446
447	_dprintf("%s: dailyp=%d monthlyp=%d\n", __FUNCTION__, history.dailyp, history.monthlyp);
448	return 1;
449}
450
451/* Try loading from the backup versions.
452 * We'll try from oldest to newest, then
453 * retry the requested one again last.  In case the drive mounts while
454 * we are trying to find a good version.
455 */
456static int try_hardway(const char *fname)
457{
458	char fn[256];
459	int n, b, found = 0;
460
461	strcpy(fn, fname);
462	n = strlen(fn);
463	if ((n > 3) && (strcmp(fn + (n - 3), ".gz") == 0))
464		n -= 3;
465	for (b = HI_BACK; b > 0; --b) {
466		sprintf(fn + n, "_%d.bak", b);
467		found |= load_history(fn);
468	}
469	found |= load_history(fname);
470
471	return found;
472}
473
474static void load_new(void)
475{
476	char hgz[256];
477
478	sprintf(hgz, "%s.gz.new", history_fn);
479	if (load_history(hgz)) save(0);
480	unlink(hgz);
481}
482
483static void load(int new)
484{
485	int i;
486	long t;
487	char *bi, *bo;
488	int n;
489	char hgz[256];
490	char sp[sizeof(save_path)];
491	unsigned char mac[6];
492
493	current_uptime = uptime();
494
495	strlcpy(save_path, nvram_safe_get("rstats_path"), sizeof(save_path) - 32);
496	if (((n = strlen(save_path)) > 0) && (save_path[n - 1] == '/')) {
497#ifdef RTCONFIG_RGMII_BRCM5301X
498		ether_atoe(nvram_safe_get("et1macaddr"), mac);
499#else
500		ether_atoe(nvram_safe_get("et0macaddr"), mac);
501#endif
502#ifdef RTCONFIG_GMAC3
503        	if(nvram_match("gmac3_enable", "1"))
504			ether_atoe(nvram_safe_get("et2macaddr"), mac);
505#endif
506		sprintf(save_path + n, "tomato_rstats_%02x%02x%02x%02x%02x%02x.gz",
507			mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
508	}
509
510	if (f_read("/var/lib/misc/rstats-stime", &save_utime, sizeof(save_utime)) != sizeof(save_utime)) {
511		save_utime = 0;
512	}
513	t = current_uptime + get_stime();
514	if ((save_utime < current_uptime) || (save_utime > t)) save_utime = t;
515	//_dprintf("%s: uptime = %dm, save_utime = %dm\n", __FUNCTION__, current_uptime / 60, save_utime / 60);
516
517	//
518
519	sprintf(hgz, "%s.gz", speed_fn);
520	speed_count = decomp(hgz, speed, sizeof(speed[0]), MAX_SPEED_IF);
521	_dprintf("%s: speed_count = %d\n", __FUNCTION__, speed_count);
522
523	for (i = 0; i < speed_count; ++i) {
524		if (speed[i].utime > current_uptime) {
525			speed[i].utime = current_uptime;
526			speed[i].sync = 1;
527		}
528	}
529
530	//
531
532	sprintf(hgz, "%s.gz", history_fn);
533
534	if (new) {
535		unlink(hgz);
536		save_utime = 0;
537		return;
538	}
539
540	f_read_string(source_fn, sp, sizeof(sp));	// always terminated
541	_dprintf("%s: read source=%s save_path=%s\n", __FUNCTION__, sp, save_path);
542	if ((strcmp(sp, save_path) == 0) && (load_history(hgz))) {
543		_dprintf("%s: using local file\n", __FUNCTION__);
544		return;
545	}
546
547	if (save_path[0] != 0) {
548		if (strcmp(save_path, "*nvram") == 0) {
549			if (!wait_action_idle(60)) exit(0);
550
551			bi = nvram_safe_get("rstats_data");
552			if ((n = strlen(bi)) > 0) {
553				if ((bo = malloc(base64_decoded_len(n))) != NULL) {
554					n = base64_decode(bi, bo, n);
555					_dprintf("%s: nvram n=%d\n", __FUNCTION__, n);
556					f_write(hgz, bo, n, 0, 0);
557					free(bo);
558					load_history(hgz);
559				}
560			}
561		}
562		else {
563			i = 1;
564			while (1) {
565				if (wait_action_idle(10)) {
566
567					// cifs quirk: try forcing refresh
568					eval("ls", save_path);
569
570					/* If we can't access the path, keep trying - maybe it isn't mounted yet.
571					 * If we can, and we can sucessfully load it, oksy.
572					 * If we can, and we cannot load it, then maybe it has been deleted, or
573					 * maybe it's corrupted (like 0 bytes long).
574					 * In these cases, try the backup files.
575					 */
576					if (load_history(save_path) || try_hardway(save_path)) {
577						f_write_string(source_fn, save_path, 0, 0);
578						break;
579					}
580				}
581
582				// not ready...
583				sleep(i);
584				if ((i *= 2) > 900) i = 900;	// 15m
585
586				if (gotterm) {
587					save_path[0] = 0;
588					return;
589				}
590
591				if (i > (3 * 60)) {
592					syslog(LOG_WARNING, "Problem loading %s. Still trying...", save_path);
593				}
594			}
595		}
596	}
597}
598
599static void save_speedjs(long next)
600{
601	int i, j, k;
602	speed_t *sp;
603	int p;
604	FILE *f;
605	uint64_t total;
606	uint64_t tmax;
607	unsigned long n;
608	char c;
609
610	if ((f = fopen("/var/tmp/rstats-speed.js", "w")) == NULL) return;
611
612	_dprintf("%s: speed_count = %d\n", __FUNCTION__, speed_count);
613
614	fprintf(f, "\nspeed_history = {\n");
615
616	for (i = 0; i < speed_count; ++i) {
617		sp = &speed[i];
618		fprintf(f, "%s'%s': {\n", i ? " },\n" : "", sp->ifname);
619		for (j = 0; j < MAX_COUNTER; ++j) {
620			total = tmax = 0;
621			fprintf(f, "%sx: [", j ? ",\n t" : " r");
622			p = sp->tail;
623			for (k = 0; k < MAX_NSPEED; ++k) {
624				p = (p + 1) % MAX_NSPEED;
625				n = sp->speed[p][j];
626				fprintf(f, "%s%lu", k ? "," : "", n);
627				total += n;
628				if (n > tmax) tmax = n;
629			}
630			fprintf(f, "],\n");
631
632			c = j ? 't' : 'r';
633			fprintf(f, " %cx_avg: %llu,\n %cx_max: %llu,\n %cx_total: %llu",
634				c, total / MAX_NSPEED, c, tmax, c, total);
635		}
636	}
637	fprintf(f, "%s_next: %ld};\n", speed_count ? "},\n" : "", ((next >= 1) ? next : 1));
638	fclose(f);
639
640	rename("/var/tmp/rstats-speed.js", "/var/spool/rstats-speed.js");
641}
642
643
644static void save_datajs(FILE *f, int mode)
645{
646	data_t *data;
647	int p;
648	int max;
649	int k, kn;
650
651_dprintf("save_datajs:\n");
652	fprintf(f, "\n%s_history = [\n", (mode == DAILY) ? "daily" : "monthly");
653
654	if (mode == DAILY) {
655		data = history.daily;
656		p = history.dailyp;
657		max = MAX_NDAILY;
658_dprintf("DAILY: p= %d\n", p);
659	}
660	else {
661		data = history.monthly;
662		p = history.monthlyp;
663		max = MAX_NMONTHLY;
664_dprintf("MONTHLY: p= %d\n", p);
665	}
666	kn = 0;
667	for (k = max; k > 0; --k) {
668		p = (p + 1) % max;
669		if (data[p].xtime == 0) continue;
670		fprintf(f, "%s[0x%lx,0x%llx,0x%llx]", kn ? "," : "",
671			(unsigned long)data[p].xtime, data[p].counter[0] / K, data[p].counter[1] / K);
672		++kn;
673_dprintf("%d:: [0x%lx,0x%llx,0x%llx]\n", p,
674	(unsigned long)data[p].xtime, data[p].counter[0] / K, data[p].counter[1] / K);
675	}
676	fprintf(f, "];\n");
677}
678
679static void save_histjs(void)
680{
681	FILE *f;
682
683	if ((f = fopen("/var/tmp/rstats-history.js", "w")) != NULL) {
684		save_datajs(f, DAILY);
685		save_datajs(f, MONTHLY);
686		fclose(f);
687		rename("/var/tmp/rstats-history.js", "/var/spool/rstats-history.js");
688	}
689}
690
691
692static void bump(data_t *data, int *tail, int max, uint32_t xnow, unsigned long long *counter)
693{
694	int t, i;
695	t = *tail;
696	if (data[t].xtime != xnow) {
697		for (i = max - 1; i >= 0; --i) {
698			if (data[i].xtime == xnow) {
699				t = i;
700				break;
701			}
702		}
703		if (i < 0) {
704			*tail = t = (t + 1) % max;
705			data[t].xtime = xnow;
706			memset(data[t].counter, 0, sizeof(data[0].counter));
707		}
708	}
709	for (i = 0; i < MAX_COUNTER; ++i) {
710		data[t].counter[i] += counter[i];
711	}
712}
713
714/**
715 * Convert ifname_desc returned by netdev_calc() to if_id enumeration.
716 * @desc:	Pointer to "INTERNET", "WIRED", "BRIDGE", etc
717 * 		Ref to netdev_calc().
718 * @return:
719 * 	-1:	invalid parameter
720 *  if_id	enumeration
721 */
722static enum if_id desc_to_id(char *desc)
723{
724	enum if_id id = IFID_MAX;
725	char *d = desc + 9, *s = desc + 10;
726
727	if (!desc)
728		return -1;
729
730	if (!strcmp(desc, "INTERNET"))
731		id = IFID_INTERNET;
732	else if (!strcmp(desc, "INTERNET1"))
733		id = IFID_INTERNET1;
734	else if (!strcmp(desc, "WIRED"))
735		id = IFID_WIRED;
736	else if (!strcmp(desc, "BRIDGE"))
737		id = IFID_BRIDGE;
738	else if (!strncmp(desc, "WIRELESS0", 9)) {
739		if (*d == '\0')
740			id = IFID_WIRELESS0;
741		else if (*d == '.' && *s >= '0' && *s <= '2' && *(s + 1) == '\0')
742			id = IFID_WIRELESS0 + *s - '0' + 1;
743	} else if (!strncmp(desc, "WIRELESS1", 9)) {
744		if (*d == '\0')
745			id = IFID_WIRELESS1;
746		else if (*d == '.' && *s >= '0' && *s <= '2' && *(s + 1) == '\0')
747			id = IFID_WIRELESS1 + *s - '0' + 1;
748	} else if (!strncmp(desc, "WIRELESS2", 9)) {
749		if (*d == '\0')
750			id = IFID_WIRELESS2;
751		else if (*d == '.' && *s >= '0' && *s <= '2' && *(s + 1) == '\0')
752			id = IFID_WIRELESS2 + *s - '0' + 1;
753	}
754
755	if (id < 0 || id == IFID_MAX)
756		_dprintf("%s: Unknown desc [%s]\n", __func__, desc);
757
758	return id;
759
760}
761
762static void calc(void)
763{
764	FILE *f;
765	char buf[256];
766	char *ifname;
767	char ifname_desc[12], ifname_desc2[12];
768	char *p;
769	unsigned long long counter[MAX_COUNTER];
770	unsigned long long rx2, tx2;
771	speed_t *sp;
772	int i, j, t;
773	time_t now;
774	time_t mon;
775	struct tm *tms;
776	uint32_t c;
777	uint32_t sc;
778	unsigned long long diff;
779	long tick;
780	int n;
781	char *exclude;
782	enum if_id id;
783	struct tmp_speed_s {
784		char desc[20];
785		unsigned long long counter[MAX_COUNTER];
786	} tmp_speed[IFID_MAX], *tmp;
787#ifdef RTCONFIG_ISP_METER
788        char traffic[64];
789#endif
790
791#ifdef RTCONFIG_QTN
792	qcsapi_unsigned_int l_counter_value;
793#endif
794
795	rx2 = 0;
796	tx2 = 0;
797	now = time(0);
798	exclude = nvram_safe_get("rstats_exclude");
799
800	if ((f = fopen("/proc/net/dev", "r")) == NULL) return;
801	fgets(buf, sizeof(buf), f);	// header
802	fgets(buf, sizeof(buf), f);	// "
803	memset(tmp_speed, 0, sizeof(tmp_speed));
804	while (fgets(buf, sizeof(buf), f)) {
805		if ((p = strchr(buf, ':')) == NULL) continue;
806			//_dprintf("\n=== %s\n", buf);
807		*p = 0;
808		if ((ifname = strrchr(buf, ' ')) == NULL) ifname = buf;
809			else ++ifname;
810		if ((strcmp(ifname, "lo") == 0) || (find_word(exclude, ifname))) continue;
811
812		// <rx bytes, packets, errors, dropped, fifo errors, frame errors, compressed, multicast><tx ...>
813		if (sscanf(p + 1, "%llu%*u%*u%*u%*u%*u%*u%*u%llu", &counter[0], &counter[1]) != 2) continue;
814
815//TODO: like httpd/web.c ej_netdev()
816#ifdef RTCONFIG_BCM5301X_TRAFFIC_MONITOR
817		if(strncmp(ifname, "vlan", 4)==0){
818			traffic_wanlan(ifname, &counter[0], &counter[1]);
819		}
820#endif
821
822		if (!netdev_calc(ifname, ifname_desc, (unsigned long*) &counter[0], (unsigned long*) &counter[1], ifname_desc2, (unsigned long*) &rx2, (unsigned long*) &tx2))
823			continue;
824		//_dprintf(">>> %s, %s, %llu, %llu, %s, %llu, %llu <<<\n",ifname, ifname_desc, counter[0], counter[1], ifname_desc2, rx2, tx2);
825#ifdef RTCONFIG_QTN
826		if (!strcmp(ifname, nvram_safe_get("wl_ifname")))
827			strcpy(ifname_desc2, "WIRELESS1");
828#endif
829loopagain:
830
831		id = desc_to_id(ifname_desc);
832		if (id < 0 || id >= IFID_MAX)
833			continue;
834		tmp = &tmp_speed[id];
835		strcpy(tmp->desc, ifname_desc);
836		for (i = 0; i < ARRAY_SIZE(tmp->counter); ++i)
837			tmp->counter[i] += counter[i];
838
839#ifdef RTCONFIG_QTN  //RT-AC87
840		if(!rpc_qtn_ready())	continue;
841		if (strlen(ifname_desc2))
842		{
843			strcpy(ifname_desc, ifname_desc2);
844			qcsapi_interface_get_counter(WIFINAME, qcsapi_total_bytes_received, &l_counter_value);
845			counter[0] = l_counter_value;
846			qcsapi_interface_get_counter(WIFINAME, qcsapi_total_bytes_sent, &l_counter_value);
847			counter[1] = l_counter_value;
848			strcpy(ifname_desc2, "");
849			goto loopagain;
850		}
851#else
852		if (strlen(ifname_desc2))
853		{
854			strcpy(ifname_desc, ifname_desc2);
855			counter[0] = rx2;
856			counter[1] = tx2;
857                        strcpy(ifname_desc2, "");
858                        goto loopagain;
859		}
860
861#endif
862	}
863	fclose(f);
864
865	for (t = 0, tmp = tmp_speed; t < ARRAY_SIZE(tmp_speed); ++t, ++tmp) {
866		/* skip unused item. */
867		if (tmp->desc[0] == '\0')
868			continue;
869
870		sp = speed;
871		for (i = speed_count; i > 0; --i) {
872			if (strcmp(sp->ifname, tmp->desc) == 0) break;
873			++sp;
874		}
875
876		if (i == 0) {
877			if (speed_count >= MAX_SPEED_IF) continue;
878
879			//_dprintf("%s: add %s as #%d\n", __FUNCTION__, ifname_desc, speed_count);
880
881			i = speed_count++;
882			sp = &speed[i];
883			memset(sp, 0, sizeof(*sp));
884			strcpy(sp->ifname, tmp->desc);
885			sp->sync = 1;
886			sp->utime = current_uptime;
887		}
888		if (sp->sync) {
889			//_dprintf("%s: sync %s\n", __FUNCTION__, ifname_desc);
890			sp->sync = -1;
891
892			memcpy(sp->last, tmp->counter, sizeof(sp->last));
893			memset(tmp->counter, 0, sizeof(tmp->counter));
894		}
895		else {
896
897			sp->sync = -1;
898
899			tick = current_uptime - sp->utime;
900			n = tick / INTERVAL;
901
902			sp->utime += (n * INTERVAL);
903			//_dprintf("%s: %s n=%d tick=%d\n", __FUNCTION__, ifname, n, tick);
904
905			for (i = 0; i < MAX_COUNTER; ++i) {
906				c = tmp->counter[i];
907				sc = sp->last[i];
908				if (c < sc) {
909					diff = (0xFFFFFFFF - sc + 1) + c;
910					if (diff > MAX_ROLLOVER) diff = 0;
911				}
912				else {
913					 diff = c - sc;
914				}
915				sp->last[i] = c;
916				tmp->counter[i] = diff;
917			}
918
919			for (j = 0; j < n; ++j) {
920				sp->tail = (sp->tail + 1) % MAX_NSPEED;
921				for (i = 0; i < MAX_COUNTER; ++i) {
922					sp->speed[sp->tail][i] = tmp->counter[i] / n;
923				}
924			}
925		}
926
927		// todo: split, delay
928
929		if (now > Y2K && strcmp(tmp->desc, "INTERNET")==0) {
930			/* Skip this if the time&date is not set yet */
931			/* Skip non-INTERNET interface only 	     */
932			tms = localtime(&now);
933			bump(history.daily, &history.dailyp, MAX_NDAILY,
934				(tms->tm_year << 16) | ((uint32_t)tms->tm_mon << 8) | tms->tm_mday, tmp->counter);
935			n = nvram_get_int("rstats_offset");
936			if ((n < 1) || (n > 31)) n = 1;
937			mon = now + ((1 - n) * (60 * 60 * 24));
938			tms = localtime(&mon);
939			bump(history.monthly, &history.monthlyp, MAX_NMONTHLY,
940				(tms->tm_year << 16) | ((uint32_t)tms->tm_mon << 8), tmp->counter);
941#ifdef RTCONFIG_ISP_METER
942			today_rx = last_day_rx + (history.daily[history.dailyp].counter[0]/K);
943			today_tx = last_day_tx + (history.daily[history.dailyp].counter[1]/K);
944			memset(traffic, 0, 64);
945			sprintf(traffic, "%lu", today_rx);
946			nvram_set("isp_day_rx", traffic);
947			memset(traffic, 0, 64);
948			sprintf(traffic, "%lu", today_tx);
949			nvram_set("isp_day_tx", traffic);
950			month_rx = last_month_rx + (history.monthly[history.monthlyp].counter[0]/K) - reset_base_month_rx;
951			month_tx = last_month_tx + (history.monthly[history.monthlyp].counter[1]/K) - reset_base_month_tx;
952			memset(traffic, 0, 64);
953			sprintf(traffic, "%lu", month_rx);
954			nvram_set("isp_month_rx", traffic);
955			memset(traffic, 0, 64);
956			sprintf(traffic, "%lu", month_tx);
957			nvram_set("isp_month_tx", traffic);
958#ifdef DEBUG
959_dprintf("CUR MONTH Rx= %lu = %lu + %llu - %lu\n",month_rx,last_month_rx,(history.monthly[history.monthlyp].counter[0]/K), reset_base_month_rx);
960_dprintf("CUR MONTH Tx= %lu = %lu + %llu - %lu\n",month_tx,last_month_tx,(history.monthly[history.monthlyp].counter[0]/K), reset_base_month_tx);
961#endif
962#endif
963		}
964	}
965
966	// cleanup stale entries
967	for (i = 0; i < speed_count; ++i) {
968		sp = &speed[i];
969		if (sp->sync == -1) {
970			sp->sync = 0;
971			continue;
972		}
973		if (((current_uptime - sp->utime) > (10 * SMIN)) || (find_word(exclude, sp->ifname))) {
974			_dprintf("%s: #%d removing. > time limit or excluded\n", __FUNCTION__, i);
975			--speed_count;
976			memcpy(sp, sp + 1, (speed_count - i) * sizeof(speed[0]));
977		}
978		else {
979			_dprintf("%s: %s not found setting sync=1\n", __FUNCTION__, sp->ifname, i);
980			sp->sync = 1;
981		}
982	}
983
984	// todo: total > user
985	if (current_uptime >= save_utime) {
986		save(0);
987		save_utime = current_uptime + get_stime();
988		_dprintf("%s: uptime = %dm, save_utime = %dm\n", __FUNCTION__, current_uptime / 60, save_utime / 60);
989	}
990}
991
992static void sig_handler(int sig)
993{
994	switch (sig) {
995	case SIGTERM:
996	case SIGINT:
997		gotterm = 1;
998		break;
999	case SIGHUP:
1000		gothup = 1;
1001		break;
1002	case SIGUSR1:
1003		gotuser = 1;
1004		break;
1005	case SIGUSR2:
1006		gotuser = 2;
1007		break;
1008	}
1009}
1010
1011int main(int argc, char *argv[])
1012{
1013	struct sigaction sa;
1014	long z;
1015	int new;
1016#ifdef RTCONFIG_ISP_METER
1017	long zzz, pppd_uptime, pppd_conntime, isp_connect_time;
1018	FILE *fp;
1019	char isp_meter_buf[64];
1020	unsigned long isp_rx, isp_tx;
1021	int get_connect_time;
1022	struct timeval timenow;
1023#endif
1024
1025	printf("rstats\nCopyright (C) 2006-2009 Jonathan Zarate\n\n");
1026
1027	if (fork() != 0) return 0;
1028
1029	openlog("rstats", LOG_PID, LOG_USER);
1030
1031	new = 0;
1032	if (argc > 1) {
1033		if (strcmp(argv[1], "--new") == 0) {
1034			new = 1;
1035			_dprintf("new=1\n");
1036		}
1037	}
1038
1039	clear_history();
1040	unlink("/var/tmp/rstats-load");
1041
1042	sa.sa_handler = sig_handler;
1043	sa.sa_flags = 0;
1044	sigemptyset(&sa.sa_mask);
1045	sigaction(SIGUSR1, &sa, NULL);
1046	sigaction(SIGUSR2, &sa, NULL);
1047	sigaction(SIGHUP, &sa, NULL);
1048	sigaction(SIGTERM, &sa, NULL);
1049	sigaction(SIGINT, &sa, NULL);
1050#ifdef RTCONFIG_ISP_METER
1051        signal(SIGTSTP, reset_traffic_meter_counter);
1052
1053	get_connect_time = 0;
1054        reset_base_day_rx = 0;
1055        reset_base_day_tx = 0;
1056        reset_base_month_rx = 0;
1057        reset_base_month_tx = 0;
1058        isp_limit = nvram_get_int("isp_limit");
1059        last_day_rx = nvram_get_int("isp_day_rx");
1060        last_day_tx = nvram_get_int("isp_day_tx");
1061        last_month_rx = nvram_get_int("isp_month_rx");
1062        last_month_tx = nvram_get_int("isp_month_tx");
1063	isp_limit_time = nvram_get_int("isp_limit_time");
1064	last_connect_time = nvram_get_int("isp_connect_time");
1065#ifdef DEBUG
1066_dprintf("rstats get last data from nvram:\n");
1067_dprintf("day rx= %lu\n", last_day_rx);
1068_dprintf("day tx= %lu\n", last_day_tx);
1069_dprintf("mon rx= %lu\n", last_month_rx);
1070_dprintf("mon tx= %lu\n", last_month_tx);
1071_dprintf("limit = %d\n", isp_limit);
1072_dprintf("L_time= %ld\n",isp_limit_time);
1073#endif
1074	memset(isp_meter_buf, 0, sizeof(isp_meter_buf));
1075        get_meter_file(isp_meter_buf);
1076	if(isp_meter_buf!=NULL) {
1077                if (sscanf(isp_meter_buf, "isp_meter:%lu,%lu,%ld,end", &isp_rx, &isp_tx, &isp_connect_time) == 3) {
1078                        _dprintf("isp_rx= %lu, isp_tx= %lu, isp_connent_time= %ld\n",
1079				 isp_rx, isp_tx, isp_connect_time);
1080                        if((isp_rx>last_month_rx)&&(isp_tx>last_month_tx)){
1081                                last_month_rx = isp_rx;
1082				last_month_tx = isp_tx;
1083                        }
1084			if(isp_connect_time > last_connect_time)
1085				last_connect_time = isp_connect_time;
1086                }
1087        }
1088	else {
1089		isp_rx = 0;
1090		isp_tx = 0;
1091		last_connect_time = 0;
1092	}
1093#ifdef DEBUG
1094_dprintf("Update last data from mtd:\n");
1095_dprintf("mon rx= %lu\n", last_month_rx);
1096_dprintf("mon tx= %lu\n", last_month_tx);
1097_dprintf("last_t= %ld\n", last_connect_time);
1098#endif
1099	month_rx = last_month_rx;
1100	month_tx = last_month_tx;
1101
1102	zzz = current_uptime = uptime();
1103#endif
1104	load(new);
1105
1106	z = current_uptime = uptime();
1107
1108	while (1) {
1109		while (current_uptime < z) {
1110			sleep(z - current_uptime);
1111			if (gothup) {
1112				if (unlink("/var/tmp/rstats-load") == 0) load_new();
1113					else save(0);
1114				gothup = 0;
1115			}
1116			if (gotterm) {
1117				save(!nvram_match("rstats_sshut", "1"));
1118				exit(0);
1119			}
1120			if (gotuser == 1) {
1121				save_speedjs(z - uptime());
1122				gotuser = 0;
1123			}
1124			else if (gotuser == 2) {
1125				save_histjs();
1126				gotuser = 0;
1127			}
1128			current_uptime = uptime();
1129		}
1130		calc();
1131		z += INTERVAL;
1132
1133#ifdef RTCONFIG_ISP_METER
1134		gettimeofday(&timenow, NULL);
1135#ifdef DEBUG
1136		_dprintf("********************************\n");
1137		_dprintf(" now time          = %ld\n", timenow.tv_sec);
1138#endif
1139		if((fp=fopen("/var/pppd_time", "r"))!=NULL) {
1140			fgets(isp_meter_buf, sizeof(isp_meter_buf), fp);
1141		        fclose(fp);
1142			if( sscanf(isp_meter_buf, "uptime:%ld", &pppd_uptime) == 1 ) {
1143				cur_conn_time = timenow.tv_sec - pppd_uptime;
1144#ifdef DEBUG
1145				_dprintf(" up time           = %ld\n", pppd_uptime);
1146				_dprintf(" cur_conn time     = %ld\n", cur_conn_time);
1147#endif
1148				total_connect_time = last_connect_time + cur_conn_time - reset_base_time;
1149				get_connect_time = 0;
1150			}
1151			else if( (sscanf(isp_meter_buf, "conntime:%ld", &pppd_conntime)==1) && get_connect_time==0 ) {
1152#ifdef DEBUG
1153                		_dprintf(" last connect time = %ld\n", pppd_conntime);
1154#endif
1155				last_connect_time += pppd_conntime - reset_base_time;
1156				total_connect_time = last_connect_time;
1157				reset_base_time = 0;
1158				get_connect_time = 1;
1159		        }
1160			memset(isp_meter_buf, 0, sizeof(isp_meter_buf));
1161		}
1162#ifdef DEBUG
1163		_dprintf(" reset_base_time   = %ld\n", reset_base_time);
1164		_dprintf(" total_connect_time= %ld\n", total_connect_time);
1165		_dprintf("******************************** %ld\n", z);
1166#endif
1167//_dprintf("isp_meter= %s\n", nvram_get("isp_meter"));
1168                if(!nvram_match("isp_meter", "disable")) {
1169                        isp_limit  = nvram_get_int("isp_limit");
1170#ifdef DEBUG
1171_dprintf("* Month: tx= %lu, rx= %lu \n", month_tx, month_rx);
1172_dprintf("* isplimit = %d\n", isp_limit);
1173_dprintf("* wan_state= %s\n", nvram_get("wan0_state_t"));
1174#endif
1175                        if(nvram_match("wan0_state_t", "2")) { //Connected
1176                                if(nvram_match("isp_meter", "download")) {
1177                                        if(month_rx > (isp_limit*1000))
1178                                                notify_rc_and_wait("isp_meter down");
1179                                }
1180                                else if(nvram_match("isp_meter", "both")) {
1181                                        if((month_tx)+(month_rx) > (isp_limit*1000))
1182                                                notify_rc_and_wait("isp_meter down");
1183                                }
1184				else if(nvram_match("isp_meter", "time")) {
1185					if(total_connect_time > (isp_limit_time*60)) {
1186						notify_rc_and_wait("isp_meter down");
1187						cur_conn_time = 0;
1188					}
1189				}
1190                        }
1191
1192			//Write to Flash
1193			if(current_uptime - zzz >= MTD_WRITE_INTERVEL) {
1194				nvram_commit();
1195				sprintf(isp_meter_buf,"isp_meter:%lu,%lu,%ld,end", month_rx, month_tx, total_connect_time);
1196#ifdef DEBUG
1197_dprintf("*WRITE: %s\n", isp_meter_buf);
1198#endif
1199				set_meter_file(isp_meter_buf);
1200				zzz = current_uptime;
1201         		}
1202		}
1203#endif //RTCONFIG_ISP_METER
1204	}
1205
1206	return 0;
1207}
1208