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 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 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 { "keys", 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 --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 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 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 154struct table_hdr { 155 int num_lines; 156 char *hdr[HDR_LINES]; 157}; 158 159static 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 205static 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 217int 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