1/* lnstat - Unified linux network statistics
2 *
3 * Copyright (C) 2004 by Harald Welte <laforge@gnumonks.org>
4 *
5 * Development of this code was funded by Astaro AG, http://www.astaro.com/
6 *
7 * Based on original concept and ideas from predecessor rtstat.c:
8 *
9 * Copyright 2001 by Robert Olsson <robert.olsson@its.uu.se>
10 *                                 Uppsala University, Sweden
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 */
18
19/* Maximum number of fields that can be displayed */
20#define MAX_FIELDS		64
21
22/* Maximum number of header lines */
23#define HDR_LINES 		10
24
25/* default field width if none specified */
26#define FIELD_WIDTH_DEFAULT	8
27#define FIELD_WIDTH_MAX		20
28
29#define DEFAULT_INTERVAL	2
30
31#define HDR_LINE_LENGTH		(MAX_FIELDS*FIELD_WIDTH_MAX)
32
33#include <unistd.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <getopt.h>
38
39#include "lnstat.h"
40
41static struct option opts[] = {
42	{ "version", 0, NULL, 'V' },
43	{ "count", 1, NULL, 'c' },
44	{ "dump", 1, NULL, 'd' },
45	{ "file", 1, NULL, 'f' },
46	{ "help", 0, NULL, 'h' },
47	{ "interval", 1, NULL, 'i' },
48	{ "key", 1, NULL, 'k' },
49	{ "subject", 1, NULL, 's' },
50	{ "width", 1, NULL, 'w' },
51};
52
53static int usage(char *name, int exit_code)
54{
55	fprintf(stderr, "%s Version %s\n", name, LNSTAT_VERSION);
56	fprintf(stderr, "Copyright (C) 2004 by Harald Welte "
57			"<laforge@gnumonks.org>\n");
58	fprintf(stderr, "This program is free software licensed under GNU GPLv2"
59			"\nwith ABSOLUTELY NO WARRANTY.\n\n");
60	fprintf(stderr, "Parameters:\n");
61	fprintf(stderr, "\t-V --version\t\tPrint Version of Program\n");
62	fprintf(stderr, "\t-c --count <count>\t"
63			"Print <count> number of intervals\n");
64	fprintf(stderr, "\t-d --dumpt\t\t"
65			"Dump list of available files/keys\n");
66	fprintf(stderr, "\t-f --file <file>\tStatistics file to use\n");
67	fprintf(stderr, "\t-h --help\t\tThis help message\n");
68	fprintf(stderr, "\t-i --interval <intv>\t"
69			"Set interval to 'intv' seconds\n");
70	fprintf(stderr, "\t-k --keys k,k,k,...\tDisplay only keys specified\n");
71	fprintf(stderr, "\t-s --subject [0-2]\t?\n");
72	fprintf(stderr, "\t-w --width n,n,n,...\tWidth for each field\n");
73	fprintf(stderr, "\n");
74
75	exit(exit_code);
76}
77
78struct field_param {
79	const char *name;
80	struct lnstat_field *lf;
81	struct {
82		unsigned int width;
83	} print;
84};
85
86struct field_params {
87	unsigned int num;
88	struct field_param params[MAX_FIELDS];
89};
90
91static void print_line(FILE *of, const struct lnstat_file *lnstat_files,
92		       const struct field_params *fp)
93{
94	int i;
95
96	for (i = 0; i < fp->num; i++) {
97		struct lnstat_field *lf = fp->params[i].lf;
98		char formatbuf[255];
99
100		snprintf(formatbuf, sizeof(formatbuf)-1, "%%%ulu|",
101			 fp->params[i].print.width);
102		fprintf(of, formatbuf, lf->result);
103	}
104	fputc('\n', of);
105}
106
107/* find lnstat_field according to user specification */
108static int map_field_params(struct lnstat_file *lnstat_files,
109			    struct field_params *fps, int interval)
110{
111	int i, j = 0;
112	struct lnstat_file *lf;
113
114	/* no field specification on commandline, need to build default */
115	if (!fps->num) {
116		for (lf = lnstat_files; lf; lf = lf->next) {
117			for (i = 0; i < lf->num_fields; i++) {
118				fps->params[j].lf = &lf->fields[i];
119				fps->params[j].lf->file->interval.tv_sec =
120								interval;
121				if (!fps->params[j].print.width)
122					fps->params[j].print.width =
123							FIELD_WIDTH_DEFAULT;
124				j++;
125			}
126		}
127		fps->num = j;
128		return 1;
129	}
130
131	for (i = 0; i < fps->num; i++) {
132		fps->params[i].lf = lnstat_find_field(lnstat_files,
133						      fps->params[i].name);
134		if (!fps->params[i].lf) {
135			fprintf(stderr, "Field `%s' unknown\n",
136				fps->params[i].name);
137			return 0;
138		}
139		fps->params[i].lf->file->interval.tv_sec = interval;
140		if (!fps->params[i].print.width)
141			fps->params[i].print.width = FIELD_WIDTH_DEFAULT;
142	}
143	return 1;
144}
145
146struct table_hdr {
147	int num_lines;
148	char *hdr[HDR_LINES];
149};
150
151static struct table_hdr *build_hdr_string(struct lnstat_file *lnstat_files,
152					  struct field_params *fps,
153					  int linewidth)
154{
155	int h,i;
156	static struct table_hdr th;
157	int ofs = 0;
158
159	for (i = 0; i < HDR_LINES; i++) {
160		th.hdr[i] = malloc(HDR_LINE_LENGTH);
161		memset(th.hdr[i], 0, sizeof(th.hdr[i]));
162	}
163
164	for (i = 0; i < fps->num; i++) {
165		char *cname, *fname = fps->params[i].lf->name;
166		char fmt[12];
167		unsigned int width = fps->params[i].print.width;
168
169		snprintf(fmt, sizeof(fmt)-1, "%%%u.%us|", width, width);
170
171		snprintf(th.hdr[0]+ofs, width+2, fmt,
172			 fps->params[i].lf->file->basename);
173
174		cname = fname;
175		for (h = 1; h < HDR_LINES; h++) {
176			if (cname - fname >= strlen(fname))
177				snprintf(th.hdr[h]+ofs, width+2, fmt, "");
178			else {
179				th.num_lines = h+1;
180				snprintf(th.hdr[h]+ofs, width+2, fmt, cname);
181			}
182			cname += width;
183		}
184		ofs += width+1;
185	}
186	/* fill in spaces */
187	for (h = 1; h <= th.num_lines; h++) {
188		for (i = 0; i < ofs; i++) {
189			if (th.hdr[h][i] == '\0')
190				th.hdr[h][i] = ' ';
191		}
192	}
193
194	return &th;
195}
196
197static int print_hdr(FILE *of, struct table_hdr *th)
198{
199	int i;
200
201	for (i = 0; i < th->num_lines; i++) {
202		fputs(th->hdr[i], of);
203		fputc('\n', of);
204	}
205	return 0;
206}
207
208
209int main(int argc, char **argv)
210{
211	struct lnstat_file *lnstat_files;
212	const char *basename;
213	int c;
214	int interval = DEFAULT_INTERVAL;
215	int hdr = 2;
216	enum {
217		MODE_DUMP,
218		MODE_NORMAL,
219	} mode = MODE_NORMAL;
220
221	unsigned long count = 1;
222	static struct field_params fp;
223	int num_req_files = 0;
224	char *req_files[LNSTAT_MAX_FILES];
225
226	/* backwards compatibility mode for old tools */
227	basename = strrchr(argv[0], '/');
228	if (basename)
229		basename += 1;	  /* name after slash */
230	else
231		basename = argv[0]; /* no slash */
232
233	if (!strcmp(basename, "rtstat")) {
234		/* rtstat compatibility mode */
235		req_files[0] = "rt_cache";
236		num_req_files = 1;
237	} else if (!strcmp(basename, "ctstat")) {
238		/* ctstat compatibility mode */
239		req_files[0] = "ip_conntrack";
240		num_req_files = 1;
241	}
242
243	while ((c = getopt_long(argc, argv,"Vc:df:h?i:k:s:w:",
244				opts, NULL)) != -1) {
245		int i, len = 0;
246		char *tmp, *tok;
247
248		switch (c) {
249			case 'c':
250				count = strtoul(optarg, NULL, 0);
251				break;
252			case 'd':
253				mode = MODE_DUMP;
254				break;
255			case 'f':
256				req_files[num_req_files++] = strdup(optarg);
257				break;
258			case '?':
259			case 'h':
260				usage(argv[0], 0);
261				break;
262			case 'i':
263				sscanf(optarg, "%u", &interval);
264				break;
265			case 'k':
266				tmp = strdup(optarg);
267				if (!tmp)
268					break;
269				for (tok = strtok(tmp, ",");
270				     tok;
271				     tok = strtok(NULL, ",")) {
272					if (fp.num >= MAX_FIELDS)
273						break;
274					fp.params[fp.num++].name = tok;
275				}
276				break;
277			case 's':
278				sscanf(optarg, "%u", &hdr);
279				break;
280			case 'w':
281				tmp = strdup(optarg);
282				if (!tmp)
283					break;
284				i = 0;
285				for (tok = strtok(tmp, ",");
286				     tok;
287				     tok = strtok(NULL, ",")) {
288					len  = strtoul(tok, NULL, 0);
289					if (len > FIELD_WIDTH_MAX)
290						len = FIELD_WIDTH_MAX;
291					fp.params[i].print.width = len;
292					i++;
293				}
294				if (i == 1) {
295					for (i = 0; i < MAX_FIELDS; i++)
296						fp.params[i].print.width = len;
297				}
298				break;
299			default:
300				usage(argv[0], 1);
301				break;
302		}
303	}
304
305	lnstat_files = lnstat_scan_dir(PROC_NET_STAT, num_req_files,
306				       (const char **) req_files);
307
308	switch (mode) {
309		int i;
310		struct table_hdr *header;
311	case MODE_DUMP:
312		lnstat_dump(stderr, lnstat_files);
313		break;
314	case MODE_NORMAL:
315
316		if (!map_field_params(lnstat_files, &fp, interval))
317			exit(1);
318
319		header = build_hdr_string(lnstat_files, &fp, 80);
320		if (!header)
321			exit(1);
322
323		if (interval < 1 )
324			interval=1;
325
326		for (i = 0; i < count; i++) {
327			if  ((hdr > 1 && (! (i % 20))) || (hdr == 1 && i == 0))
328				print_hdr(stdout, header);
329			lnstat_update(lnstat_files);
330			print_line(stdout, lnstat_files, &fp);
331			sleep(interval);
332		}
333	}
334
335	return 1;
336}
337
338