Home | History | Annotate | Download | only in misc
      1 /* lnstat - Unified linux network statistics
      2  *
      3  * Copyright (C) 2004 by Harald Welte <laforge (at) 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 (at) 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		128
     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 
     41 static 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 	{ "keys", 1, NULL, 'k' },
     49 	{ "subject", 1, NULL, 's' },
     50 	{ "width", 1, NULL, 'w' },
     51 };
     52 
     53 static 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 (at) 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 --dump\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 
     78 struct field_param {
     79 	const char *name;
     80 	struct lnstat_field *lf;
     81 	struct {
     82 		unsigned int width;
     83 	} print;
     84 };
     85 
     86 struct field_params {
     87 	unsigned int num;
     88 	struct field_param params[MAX_FIELDS];
     89 };
     90 
     91 static 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 */
    108 static 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 
    125 				if (++j >= MAX_FIELDS - 1) {
    126 					fprintf(stderr,
    127 						"WARN: MAX_FIELDS (%d) reached,"
    128 						" truncating number of keys\n",
    129 						MAX_FIELDS);
    130 					goto full;
    131 				}
    132 			}
    133 		}
    134 	full:
    135 		fps->num = j;
    136 		return 1;
    137 	}
    138 
    139 	for (i = 0; i < fps->num; i++) {
    140 		fps->params[i].lf = lnstat_find_field(lnstat_files,
    141 						      fps->params[i].name);
    142 		if (!fps->params[i].lf) {
    143 			fprintf(stderr, "Field `%s' unknown\n",
    144 				fps->params[i].name);
    145 			return 0;
    146 		}
    147 		fps->params[i].lf->file->interval.tv_sec = interval;
    148 		if (!fps->params[i].print.width)
    149 			fps->params[i].print.width = FIELD_WIDTH_DEFAULT;
    150 	}
    151 	return 1;
    152 }
    153 
    154 struct table_hdr {
    155 	int num_lines;
    156 	char *hdr[HDR_LINES];
    157 };
    158 
    159 static struct table_hdr *build_hdr_string(struct lnstat_file *lnstat_files,
    160 					  struct field_params *fps,
    161 					  int linewidth)
    162 {
    163 	int h,i;
    164 	static struct table_hdr th;
    165 	int ofs = 0;
    166 
    167 	for (i = 0; i < HDR_LINES; i++) {
    168 		th.hdr[i] = malloc(HDR_LINE_LENGTH);
    169 		memset(th.hdr[i], 0, sizeof(th.hdr[i]));
    170 	}
    171 
    172 	for (i = 0; i < fps->num; i++) {
    173 		char *cname, *fname = fps->params[i].lf->name;
    174 		char fmt[12];
    175 		unsigned int width = fps->params[i].print.width;
    176 
    177 		snprintf(fmt, sizeof(fmt)-1, "%%%u.%us|", width, width);
    178 
    179 		snprintf(th.hdr[0]+ofs, width+2, fmt,
    180 			 fps->params[i].lf->file->basename);
    181 
    182 		cname = fname;
    183 		for (h = 1; h < HDR_LINES; h++) {
    184 			if (cname - fname >= strlen(fname))
    185 				snprintf(th.hdr[h]+ofs, width+2, fmt, "");
    186 			else {
    187 				th.num_lines = h+1;
    188 				snprintf(th.hdr[h]+ofs, width+2, fmt, cname);
    189 			}
    190 			cname += width;
    191 		}
    192 		ofs += width+1;
    193 	}
    194 	/* fill in spaces */
    195 	for (h = 1; h <= th.num_lines; h++) {
    196 		for (i = 0; i < ofs; i++) {
    197 			if (th.hdr[h][i] == '\0')
    198 				th.hdr[h][i] = ' ';
    199 		}
    200 	}
    201 
    202 	return &th;
    203 }
    204 
    205 static int print_hdr(FILE *of, struct table_hdr *th)
    206 {
    207 	int i;
    208 
    209 	for (i = 0; i < th->num_lines; i++) {
    210 		fputs(th->hdr[i], of);
    211 		fputc('\n', of);
    212 	}
    213 	return 0;
    214 }
    215 
    216 
    217 int main(int argc, char **argv)
    218 {
    219 	struct lnstat_file *lnstat_files;
    220 	const char *basename;
    221 	int c;
    222 	int interval = DEFAULT_INTERVAL;
    223 	int hdr = 2;
    224 	enum {
    225 		MODE_DUMP,
    226 		MODE_NORMAL,
    227 	} mode = MODE_NORMAL;
    228 
    229 	unsigned long count = 1;
    230 	static struct field_params fp;
    231 	int num_req_files = 0;
    232 	char *req_files[LNSTAT_MAX_FILES];
    233 
    234 	/* backwards compatibility mode for old tools */
    235 	basename = strrchr(argv[0], '/');
    236 	if (basename)
    237 		basename += 1;	  /* name after slash */
    238 	else
    239 		basename = argv[0]; /* no slash */
    240 
    241 	if (!strcmp(basename, "rtstat")) {
    242 		/* rtstat compatibility mode */
    243 		req_files[0] = "rt_cache";
    244 		num_req_files = 1;
    245 	} else if (!strcmp(basename, "ctstat")) {
    246 		/* ctstat compatibility mode */
    247 		req_files[0] = "ip_conntrack";
    248 		num_req_files = 1;
    249 	}
    250 
    251 	while ((c = getopt_long(argc, argv,"Vc:df:h?i:k:s:w:",
    252 				opts, NULL)) != -1) {
    253 		int i, len = 0;
    254 		char *tmp, *tok;
    255 
    256 		switch (c) {
    257 			case 'c':
    258 				count = strtoul(optarg, NULL, 0);
    259 				break;
    260 			case 'd':
    261 				mode = MODE_DUMP;
    262 				break;
    263 			case 'f':
    264 				req_files[num_req_files++] = strdup(optarg);
    265 				break;
    266 			case '?':
    267 			case 'h':
    268 				usage(argv[0], 0);
    269 				break;
    270 			case 'i':
    271 				sscanf(optarg, "%u", &interval);
    272 				break;
    273 			case 'k':
    274 				tmp = strdup(optarg);
    275 				if (!tmp)
    276 					break;
    277 				for (tok = strtok(tmp, ",");
    278 				     tok;
    279 				     tok = strtok(NULL, ",")) {
    280 					if (fp.num >= MAX_FIELDS) {
    281 						fprintf(stderr,
    282 							"WARN: too many keys"
    283 							" requested: (%d max)\n",
    284 							MAX_FIELDS);
    285 						break;
    286 					}
    287 					fp.params[fp.num++].name = tok;
    288 				}
    289 				break;
    290 			case 's':
    291 				sscanf(optarg, "%u", &hdr);
    292 				break;
    293 			case 'w':
    294 				tmp = strdup(optarg);
    295 				if (!tmp)
    296 					break;
    297 				i = 0;
    298 				for (tok = strtok(tmp, ",");
    299 				     tok;
    300 				     tok = strtok(NULL, ",")) {
    301 					len  = strtoul(tok, NULL, 0);
    302 					if (len > FIELD_WIDTH_MAX)
    303 						len = FIELD_WIDTH_MAX;
    304 					fp.params[i].print.width = len;
    305 					i++;
    306 				}
    307 				if (i == 1) {
    308 					for (i = 0; i < MAX_FIELDS; i++)
    309 						fp.params[i].print.width = len;
    310 				}
    311 				break;
    312 			default:
    313 				usage(argv[0], 1);
    314 				break;
    315 		}
    316 	}
    317 
    318 	lnstat_files = lnstat_scan_dir(PROC_NET_STAT, num_req_files,
    319 				       (const char **) req_files);
    320 
    321 	switch (mode) {
    322 		int i;
    323 		struct table_hdr *header;
    324 	case MODE_DUMP:
    325 		lnstat_dump(stderr, lnstat_files);
    326 		break;
    327 	case MODE_NORMAL:
    328 
    329 		if (!map_field_params(lnstat_files, &fp, interval))
    330 			exit(1);
    331 
    332 		header = build_hdr_string(lnstat_files, &fp, 80);
    333 		if (!header)
    334 			exit(1);
    335 
    336 		if (interval < 1 )
    337 			interval=1;
    338 
    339 		for (i = 0; i < count; i++) {
    340 			if  ((hdr > 1 && (! (i % 20))) || (hdr == 1 && i == 0))
    341 				print_hdr(stdout, header);
    342 			lnstat_update(lnstat_files);
    343 			print_line(stdout, lnstat_files, &fp);
    344 			fflush(stdout);
    345 			sleep(interval);
    346 		}
    347 	}
    348 
    349 	return 1;
    350 }
    351 
    352