iproute2/misc/lnstat.c
<<
>>
Prefs
   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 <json_writer.h>
  40#include "lnstat.h"
  41#include "version.h"
  42
  43static struct option opts[] = {
  44        { "version", 0, NULL, 'V' },
  45        { "count", 1, NULL, 'c' },
  46        { "dump", 0, NULL, 'd' },
  47        { "json", 0, NULL, 'j' },
  48        { "file", 1, NULL, 'f' },
  49        { "help", 0, NULL, 'h' },
  50        { "interval", 1, NULL, 'i' },
  51        { "keys", 1, NULL, 'k' },
  52        { "subject", 1, NULL, 's' },
  53        { "width", 1, NULL, 'w' },
  54        { "oneline", 0, NULL, 0 },
  55};
  56
  57static int usage(char *name, int exit_code)
  58{
  59        fprintf(stderr,
  60                "%s Version %s\n"
  61                "Copyright (C) 2004 by Harald Welte <laforge@gnumonks.org>\n"
  62                "This program is free software licensed under GNU GPLv2\nwith ABSOLUTELY NO WARRANTY.\n"
  63                "\n"
  64                "Parameters:\n"
  65                "       -V --version            Print Version of Program\n"
  66                "       -c --count <count>      "
  67                "Print <count> number of intervals\n"
  68                "       -d --dump               "
  69                "Dump list of available files/keys\n"
  70                "       -j --json               "
  71                "Display in JSON format\n"
  72                "       -f --file <file>        Statistics file to use\n"
  73                "       -h --help               This help message\n"
  74                "       -i --interval <intv>    "
  75                "Set interval to 'intv' seconds\n"
  76                "       -k --keys k,k,k,...     Display only keys specified\n"
  77                "       -s --subject [0-2]      Control header printing:\n"
  78                "                               0 = never\n"
  79                "                               1 = once\n"
  80                "                               2 = every 20 lines (default))\n"
  81                "       -w --width n,n,n,...    Width for each field\n"
  82                "\n",
  83                name, version);
  84
  85        exit(exit_code);
  86}
  87
  88struct field_param {
  89        const char *name;
  90        struct lnstat_field *lf;
  91        struct {
  92                unsigned int width;
  93        } print;
  94};
  95
  96struct field_params {
  97        unsigned int num;
  98        struct field_param params[MAX_FIELDS];
  99};
 100
 101static void print_line(FILE *of, const struct lnstat_file *lnstat_files,
 102                       const struct field_params *fp)
 103{
 104        int i;
 105
 106        for (i = 0; i < fp->num; i++) {
 107                const struct lnstat_field *lf = fp->params[i].lf;
 108
 109                fprintf(of, "%*lu|", fp->params[i].print.width, lf->result);
 110        }
 111        fputc('\n', of);
 112}
 113
 114static void print_json(FILE *of, const struct lnstat_file *lnstat_files,
 115                       const struct field_params *fp)
 116{
 117        json_writer_t *jw = jsonw_new(of);
 118        int i;
 119
 120        jsonw_start_object(jw);
 121        for (i = 0; i < fp->num; i++) {
 122                const struct lnstat_field *lf = fp->params[i].lf;
 123
 124                jsonw_uint_field(jw, lf->name, lf->result);
 125        }
 126        jsonw_end_object(jw);
 127        jsonw_destroy(&jw);
 128}
 129
 130/* find lnstat_field according to user specification */
 131static int map_field_params(struct lnstat_file *lnstat_files,
 132                            struct field_params *fps, int interval)
 133{
 134        int i, j = 0;
 135        struct lnstat_file *lf;
 136
 137        /* no field specification on commandline, need to build default */
 138        if (!fps->num) {
 139                for (lf = lnstat_files; lf; lf = lf->next) {
 140                        for (i = 0; i < lf->num_fields; i++) {
 141                                fps->params[j].lf = &lf->fields[i];
 142                                fps->params[j].lf->file->interval.tv_sec =
 143                                                                interval;
 144                                if (!fps->params[j].print.width)
 145                                        fps->params[j].print.width =
 146                                                        FIELD_WIDTH_DEFAULT;
 147
 148                                if (++j >= MAX_FIELDS - 1) {
 149                                        fprintf(stderr,
 150                                                "WARN: MAX_FIELDS (%d) reached, truncating number of keys\n",
 151                                                MAX_FIELDS);
 152                                        goto full;
 153                                }
 154                        }
 155                }
 156full:
 157                fps->num = j;
 158                return 1;
 159        }
 160
 161        for (i = 0; i < fps->num; i++) {
 162                fps->params[i].lf = lnstat_find_field(lnstat_files,
 163                                                      fps->params[i].name);
 164                if (!fps->params[i].lf) {
 165                        fprintf(stderr, "Field `%s' unknown\n",
 166                                fps->params[i].name);
 167                        return 0;
 168                }
 169                fps->params[i].lf->file->interval.tv_sec = interval;
 170                if (!fps->params[i].print.width)
 171                        fps->params[i].print.width = FIELD_WIDTH_DEFAULT;
 172        }
 173        return 1;
 174}
 175
 176struct table_hdr {
 177        int num_lines;
 178        char *hdr[HDR_LINES];
 179};
 180
 181static struct table_hdr *build_hdr_string(struct lnstat_file *lnstat_files,
 182                                          struct field_params *fps,
 183                                          int linewidth)
 184{
 185        int h, i;
 186        static struct table_hdr th;
 187        int ofs = 0;
 188
 189        for (i = 0; i < HDR_LINES; i++)
 190                th.hdr[i] = calloc(1, HDR_LINE_LENGTH);
 191
 192        for (i = 0; i < fps->num; i++) {
 193                char *cname, *fname = fps->params[i].lf->name;
 194                unsigned int width = fps->params[i].print.width;
 195
 196                snprintf(th.hdr[0]+ofs, width+2, "%*.*s|", width, width,
 197                         fps->params[i].lf->file->basename);
 198
 199                cname = fname;
 200                for (h = 1; h < HDR_LINES; h++) {
 201                        if (cname - fname >= strlen(fname))
 202                                snprintf(th.hdr[h]+ofs, width+2,
 203                                         "%*.*s|", width, width, "");
 204                        else {
 205                                th.num_lines = h+1;
 206                                snprintf(th.hdr[h]+ofs, width+2,
 207                                         "%*.*s|", width, width, cname);
 208                        }
 209                        cname += width;
 210                }
 211                ofs += width+1;
 212        }
 213        /* fill in spaces */
 214        for (h = 1; h <= th.num_lines; h++) {
 215                for (i = 0; i < ofs; i++) {
 216                        if (th.hdr[h][i] == '\0')
 217                                th.hdr[h][i] = ' ';
 218                }
 219        }
 220
 221        return &th;
 222}
 223
 224static int print_hdr(FILE *of, struct table_hdr *th)
 225{
 226        int i;
 227
 228        for (i = 0; i < th->num_lines; i++) {
 229                fputs(th->hdr[i], of);
 230                fputc('\n', of);
 231        }
 232        return 0;
 233}
 234
 235
 236int main(int argc, char **argv)
 237{
 238        struct lnstat_file *lnstat_files;
 239        const char *basename;
 240        int i, c;
 241        int interval = DEFAULT_INTERVAL;
 242        int hdr = 2;
 243        enum {
 244                MODE_DUMP,
 245                MODE_JSON,
 246                MODE_NORMAL,
 247        } mode = MODE_NORMAL;
 248        unsigned long count = 0;
 249        struct table_hdr *header;
 250        static struct field_params fp;
 251        int num_req_files = 0;
 252        char *req_files[LNSTAT_MAX_FILES];
 253
 254        /* backwards compatibility mode for old tools */
 255        basename = strrchr(argv[0], '/');
 256        if (basename)
 257                basename += 1;    /* name after slash */
 258        else
 259                basename = argv[0]; /* no slash */
 260
 261        if (!strcmp(basename, "rtstat")) {
 262                /* rtstat compatibility mode */
 263                req_files[0] = "rt_cache";
 264                num_req_files = 1;
 265        } else if (!strcmp(basename, "ctstat")) {
 266                /* ctstat compatibility mode */
 267                req_files[0] = "ip_conntrack";
 268                num_req_files = 1;
 269        }
 270
 271        while ((c = getopt_long(argc, argv, "Vc:djpf:h?i:k:s:w:",
 272                                opts, NULL)) != -1) {
 273                int len = 0;
 274                char *tmp, *tok;
 275
 276                switch (c) {
 277                case 'c':
 278                        count = strtoul(optarg, NULL, 0);
 279                        break;
 280                case 'd':
 281                        mode = MODE_DUMP;
 282                        break;
 283                case 'j':
 284                        mode = MODE_JSON;
 285                        break;
 286                case 'f':
 287                        req_files[num_req_files++] = strdup(optarg);
 288                        break;
 289                case '?':
 290                case 'h':
 291                        usage(argv[0], 0);
 292                        break;
 293                case 'i':
 294                        sscanf(optarg, "%u", &interval);
 295                        break;
 296                case 'k':
 297                        tmp = strdup(optarg);
 298                        if (!tmp)
 299                                break;
 300                        for (tok = strtok(tmp, ",");
 301                             tok;
 302                             tok = strtok(NULL, ",")) {
 303                                if (fp.num >= MAX_FIELDS) {
 304                                        fprintf(stderr,
 305                                                "WARN: too many keys requested: (%d max)\n",
 306                                                MAX_FIELDS);
 307                                        break;
 308                                }
 309                                fp.params[fp.num++].name = tok;
 310                        }
 311                        break;
 312                case 's':
 313                        sscanf(optarg, "%u", &hdr);
 314                        break;
 315                case 'w':
 316                        tmp = strdup(optarg);
 317                        if (!tmp)
 318                                break;
 319                        i = 0;
 320                        for (tok = strtok(tmp, ",");
 321                             tok;
 322                             tok = strtok(NULL, ",")) {
 323                                len  = strtoul(tok, NULL, 0);
 324                                if (len > FIELD_WIDTH_MAX)
 325                                        len = FIELD_WIDTH_MAX;
 326                                fp.params[i].print.width = len;
 327                                i++;
 328                        }
 329                        if (i == 1) {
 330                                for (i = 0; i < MAX_FIELDS; i++)
 331                                        fp.params[i].print.width = len;
 332                        }
 333                        break;
 334                default:
 335                        usage(argv[0], 1);
 336                        break;
 337                }
 338        }
 339
 340        lnstat_files = lnstat_scan_dir(PROC_NET_STAT, num_req_files,
 341                                       (const char **) req_files);
 342
 343        switch (mode) {
 344        case MODE_DUMP:
 345                lnstat_dump(stdout, lnstat_files);
 346                break;
 347
 348        case MODE_NORMAL:
 349        case MODE_JSON:
 350                if (!map_field_params(lnstat_files, &fp, interval))
 351                        exit(1);
 352
 353                header = build_hdr_string(lnstat_files, &fp, 80);
 354                if (!header)
 355                        exit(1);
 356
 357                if (interval < 1)
 358                        interval = 1;
 359
 360                for (i = 0; i < count || !count; i++) {
 361                        lnstat_update(lnstat_files);
 362                        if (mode == MODE_JSON)
 363                                print_json(stdout, lnstat_files, &fp);
 364                        else {
 365                                if  ((hdr > 1 && !(i % 20)) ||
 366                                     (hdr == 1 && i == 0))
 367                                        print_hdr(stdout, header);
 368                                print_line(stdout, lnstat_files, &fp);
 369                        }
 370                        fflush(stdout);
 371                        if (i < count - 1 || !count)
 372                                sleep(interval);
 373                }
 374                break;
 375        }
 376
 377        return 1;
 378}
 379