ifstat: add json output format
diff --git a/misc/ifstat.c b/misc/ifstat.c
index 6d0ad8c..a47c046 100644
--- a/misc/ifstat.c
+++ b/misc/ifstat.c
@@ -38,6 +38,7 @@
 int reset_history = 0;
 int ignore_history = 0;
 int no_output = 0;
+int json_output = 0;
 int no_update = 0;
 int scan_interval = 0;
 int time_constant = 0;
@@ -61,6 +62,32 @@
 	__u32			ival[MAXS];
 };
 
+static const char *stats[MAXS] = {
+	"rx_packets",
+	"tx_packets",
+	"rx_bytes",
+	"tx_bytes",
+	"rx_errors",
+	"tx_errors",
+	"rx_dropped",
+	"tx_dropped",
+	"multicast",
+	"collisions",
+	"rx_length_errors",
+	"rx_over_errors",
+	"rx_crc_errors",
+	"rx_frame_errors",
+	"rx_fifo_errors",
+	"rx_missed_errors",
+	"tx_aborted_errors",
+	"tx_carrier_errors",
+	"tx_fifo_errors",
+	"tx_heartbeat_errors",
+	"tx_window_errors",
+	"rx_compressed",
+	"tx_compressed"
+};
+
 struct ifstat_ent *kern_db;
 struct ifstat_ent *hist_db;
 
@@ -212,8 +239,13 @@
 static void dump_raw_db(FILE *fp, int to_hist)
 {
 	struct ifstat_ent *n, *h;
+	const char *eol = "\n";
+
 	h = hist_db;
-	fprintf(fp, "#%s\n", info_source);
+	if (json_output)
+		fprintf(fp, "{ \"%s\":{", info_source);
+	else
+		fprintf(fp, "#%s\n", info_source);
 
 	for (n=kern_db; n; n=n->next) {
 		int i;
@@ -232,10 +264,22 @@
 				}
 			}
 		}
-		fprintf(fp, "%d %s ", n->ifindex, n->name);
-		for (i=0; i<MAXS; i++)
-			fprintf(fp, "%llu %u ", vals[i], (unsigned)rates[i]);
-		fprintf(fp, "\n");
+
+		if (json_output) {
+			fprintf(fp, "%s   \"%s\":{",
+				eol, n->name);
+			eol = ",\n";
+			for (i=0; i<MAXS && stats[i]; i++)
+				fprintf(fp, " \"%s\":%llu", 
+					stats[i], vals[i]);
+			fprintf(fp, "}");
+		} else {
+			fprintf(fp, "%d %s ", n->ifindex, n->name);
+			for (i=0; i<MAXS; i++)
+				fprintf(fp, "%llu %u ", vals[i], 
+					(unsigned)rates[i]);
+			fprintf(fp, "\n");
+		}
 	}
 }
 
@@ -244,10 +288,11 @@
 static const unsigned long long mega = 1000000;
 static const unsigned long long kilo = 1000;
 
-static void format_rate(FILE *fp, unsigned long long *vals,
-			double *rates, int i)
+static void format_rate(FILE *fp, const unsigned long long *vals,
+			const double *rates, int i)
 {
 	char temp[64];
+
 	if (vals[i] > giga)
 		fprintf(fp, "%7lluM ", vals[i]/mega);
 	else if (vals[i] > mega)
@@ -265,7 +310,7 @@
 		fprintf(fp, "%-6u ", (unsigned)rates[i]);
 }
 
-static void format_pair(FILE *fp, unsigned long long *vals, int i, int k)
+static void format_pair(FILE *fp, const unsigned long long *vals, int i, int k)
 {
 	char temp[64];
 	if (vals[i] > giga)
@@ -328,10 +373,27 @@
 	}
 }
 
-static void print_one_if(FILE *fp, struct ifstat_ent *n,
-			 unsigned long long *vals)
+static void print_one_json(FILE *fp, const struct ifstat_ent *n,
+			   const unsigned long long *vals)
+{
+	int i, m;
+	const char *sep = " ";
+	
+	m = show_errors ? 20 : 10;
+	fprintf(fp, "    \"%s\":{", n->name);
+	for (i=0; i < m && stats[i]; i++) {
+		fprintf(fp, "%s\"%s\":%llu", 
+			sep, stats[i], vals[i]);
+		sep = ", ";
+	}
+	fprintf(fp, " }");
+}
+
+static void print_one_if(FILE *fp, const struct ifstat_ent *n,
+			 const unsigned long long *vals)
 {
 	int i;
+
 	fprintf(fp, "%-15s ", n->name);
 	for (i=0; i<4; i++)
 		format_rate(fp, vals, n->rate, i);
@@ -375,27 +437,42 @@
 	}
 }
 
-
 static void dump_kern_db(FILE *fp)
 {
 	struct ifstat_ent *n;
+	const char *eol = "\n";
 
-	print_head(fp);
+	if (json_output)
+		fprintf(fp, "{ \"%s\": {", info_source);
+	else
+		print_head(fp);
 
 	for (n=kern_db; n; n=n->next) {
 		if (!match(n->name))
 			continue;
-		print_one_if(fp, n, n->val);
+
+		if (json_output) {
+			fprintf(fp, "%s", eol);
+			eol = ",\n";
+			print_one_json(fp, n, n->val);
+		} else
+			print_one_if(fp, n, n->val);
 	}
+	if (json_output)
+		fprintf(fp, "\n} }\n");
 }
 
 
 static void dump_incr_db(FILE *fp)
 {
 	struct ifstat_ent *n, *h;
-	h = hist_db;
+	const char *eol = "\n";
 
-	print_head(fp);
+	h = hist_db;
+	if (json_output)
+		fprintf(fp, "{ \"%s\":{", info_source);
+	else
+		print_head(fp);
 
 	for (n=kern_db; n; n=n->next) {
 		int i;
@@ -414,8 +491,16 @@
 		}
 		if (!match(n->name))
 			continue;
-		print_one_if(fp, n, vals);
+
+		if (json_output) {
+			fprintf(fp, "%s", eol);
+			eol = ",\n";
+			print_one_json(fp, n, n->val);
+		} else
+			print_one_if(fp, n, vals);
 	}
+	if (json_output)
+		fprintf(fp, "\n} }\n");
 }
 
 
@@ -559,9 +644,10 @@
 "   -a, --ignore	ignore history\n"
 "   -d, --scan=SECS	sample every statistics every SECS\n"
 "   -e, --errors	show errors\n"
+"   -j, --json          format output in JSON\n"
 "   -n, --nooutput	do history only\n"
 "   -r, --reset		reset history\n"
-"   -s, --noupdate	don;t update history\n"
+"   -s, --noupdate	don\'t update history\n"
 "   -t, --interval=SECS	report average over the last SECS\n"
 "   -V, --version	output version information\n"
 "   -z, --zeros		show entries with zero activity\n");
@@ -575,6 +661,7 @@
 	{ "scan", 1, 0, 'd'},
 	{ "errors", 0, 0, 'e' },
 	{ "nooutput", 0, 0, 'n' },
+	{ "json", 0, 0, 'j' },
 	{ "reset", 0, 0, 'r' },
 	{ "noupdate", 0, 0, 's' },
 	{ "interval", 1, 0, 't' },
@@ -591,7 +678,7 @@
 	int ch;
 	int fd;
 
-	while ((ch = getopt_long(argc, argv, "hvVzrnasd:t:eK",
+	while ((ch = getopt_long(argc, argv, "hjvVzrnasd:t:e",
 			longopts, NULL)) != EOF) {
 		switch(ch) {
 		case 'z':
@@ -612,6 +699,9 @@
 		case 'e':
 			show_errors = 1;
 			break;
+		case 'j':
+			json_output = 1;
+			break;
 		case 'd':
 			scan_interval = atoi(optarg) * 1000;
 			if (scan_interval <= 0) {
@@ -759,11 +849,14 @@
 		else
 			dump_incr_db(stdout);
 	}
+
 	if (!no_update) {
 		ftruncate(fileno(hist_fp), 0);
 		rewind(hist_fp);
+
+		json_output = 0;
 		dump_raw_db(hist_fp, 1);
-		fflush(hist_fp);
+		fclose(hist_fp);
 	}
 	exit(0);
 }