iproute2/misc/lnstat_util.c
<<
>>
Prefs
   1/* lnstat.c:  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#include <unistd.h>
  20#include <stdio.h>
  21#include <stdlib.h>
  22#include <string.h>
  23#include <dirent.h>
  24#include <limits.h>
  25#include <time.h>
  26
  27#include <sys/time.h>
  28#include <sys/types.h>
  29
  30#include "lnstat.h"
  31
  32/* size of temp buffer used to read lines from procfiles */
  33#define FGETS_BUF_SIZE 1024
  34
  35
  36#define RTSTAT_COMPAT_LINE "entries  in_hit in_slow_tot in_no_route in_brd in_martian_dst in_martian_src  out_hit out_slow_tot out_slow_mc  gc_total gc_ignored gc_goal_miss gc_dst_overflow in_hlist_search out_hlist_search\n"
  37
  38/* Read (and summarize for SMP) the different stats vars. */
  39static int scan_lines(struct lnstat_file *lf, int i)
  40{
  41        char buf[FGETS_BUF_SIZE];
  42        int j, num_lines = 0;
  43
  44        for (j = 0; j < lf->num_fields; j++)
  45                lf->fields[j].values[i] = 0;
  46
  47        rewind(lf->fp);
  48        /* skip first line */
  49        if (!lf->compat && !fgets(buf, sizeof(buf)-1, lf->fp))
  50                return -1;
  51
  52        while (!feof(lf->fp) && fgets(buf, sizeof(buf)-1, lf->fp)) {
  53                char *ptr = buf;
  54
  55                num_lines++;
  56
  57                gettimeofday(&lf->last_read, NULL);
  58
  59                for (j = 0; j < lf->num_fields; j++) {
  60                        unsigned long f = strtoul(ptr, &ptr, 16);
  61
  62                        if (j == 0)
  63                                lf->fields[j].values[i] = f;
  64                        else
  65                                lf->fields[j].values[i] += f;
  66                }
  67        }
  68        return num_lines;
  69}
  70
  71static int time_after(struct timeval *last,
  72                      struct timeval *tout,
  73                      struct timeval *now)
  74{
  75        if (now->tv_sec > last->tv_sec + tout->tv_sec)
  76                return 1;
  77
  78        if (now->tv_sec == last->tv_sec + tout->tv_sec) {
  79                if (now->tv_usec > last->tv_usec + tout->tv_usec)
  80                        return 1;
  81        }
  82
  83        return 0;
  84}
  85
  86int lnstat_update(struct lnstat_file *lnstat_files)
  87{
  88        struct lnstat_file *lf;
  89        struct timeval tv;
  90
  91        gettimeofday(&tv, NULL);
  92
  93        for (lf = lnstat_files; lf; lf = lf->next) {
  94                if (time_after(&lf->last_read, &lf->interval, &tv)) {
  95                        int i;
  96                        struct lnstat_field *lfi;
  97
  98                        scan_lines(lf, 1);
  99
 100                        for (i = 0, lfi = &lf->fields[i];
 101                             i < lf->num_fields; i++, lfi = &lf->fields[i]) {
 102                                if (i == 0)
 103                                        lfi->result = lfi->values[1];
 104                                else
 105                                        lfi->result = (lfi->values[1]-lfi->values[0])
 106                                                        / lf->interval.tv_sec;
 107                        }
 108
 109                        scan_lines(lf, 0);
 110                }
 111        }
 112
 113        return 0;
 114}
 115
 116/* scan first template line and fill in per-field data structures */
 117static int __lnstat_scan_fields(struct lnstat_file *lf, char *buf)
 118{
 119        char *tok;
 120        int i;
 121
 122        tok = strtok(buf, " \t\n");
 123        for (i = 0; i < LNSTAT_MAX_FIELDS_PER_LINE; i++) {
 124                lf->fields[i].file = lf;
 125                strncpy(lf->fields[i].name, tok, LNSTAT_MAX_FIELD_NAME_LEN);
 126                /* has to be null-terminate since we initialize to zero
 127                 * and field size is NAME_LEN + 1 */
 128                tok = strtok(NULL, " \t\n");
 129                if (!tok) {
 130                        lf->num_fields = i+1;
 131                        return 0;
 132                }
 133        }
 134        return 0;
 135}
 136
 137static int lnstat_scan_fields(struct lnstat_file *lf)
 138{
 139        char buf[FGETS_BUF_SIZE];
 140
 141        rewind(lf->fp);
 142        if (!fgets(buf, sizeof(buf)-1, lf->fp))
 143                return -1;
 144
 145        return __lnstat_scan_fields(lf, buf);
 146}
 147
 148/* fake function emulating lnstat_scan_fields() for old kernels */
 149static int lnstat_scan_compat_rtstat_fields(struct lnstat_file *lf)
 150{
 151        char buf[FGETS_BUF_SIZE];
 152
 153        strncpy(buf, RTSTAT_COMPAT_LINE, sizeof(buf) - 1);
 154        buf[sizeof(buf) - 1] = '\0';
 155
 156        return __lnstat_scan_fields(lf, buf);
 157}
 158
 159/* find out whether string 'name; is in given string array */
 160static int name_in_array(const int num, const char **arr, const char *name)
 161{
 162        int i;
 163
 164        for (i = 0; i < num; i++) {
 165                if (!strcmp(arr[i], name))
 166                        return 1;
 167        }
 168        return 0;
 169}
 170
 171/* allocate lnstat_file and open given file */
 172static struct lnstat_file *alloc_and_open(const char *path, const char *file)
 173{
 174        struct lnstat_file *lf;
 175
 176        /* allocate */
 177        lf = calloc(1, sizeof(*lf));
 178        if (!lf) {
 179                fprintf(stderr, "out of memory\n");
 180                return NULL;
 181        }
 182
 183        /* initialize */
 184        snprintf(lf->basename, sizeof(lf->basename), "%s", file);
 185        snprintf(lf->path, sizeof(lf->path), "%s/%s", path, file);
 186
 187        /* initialize to default */
 188        lf->interval.tv_sec = 1;
 189
 190        /* open */
 191        lf->fp = fopen(lf->path, "r");
 192        if (!lf->fp) {
 193                perror(lf->path);
 194                free(lf);
 195                return NULL;
 196        }
 197
 198        return lf;
 199}
 200
 201
 202/* lnstat_scan_dir - find and parse all available statistics files/fields */
 203struct lnstat_file *lnstat_scan_dir(const char *path, const int num_req_files,
 204                                    const char **req_files)
 205{
 206        DIR *dir;
 207        struct lnstat_file *lnstat_files = NULL;
 208        struct dirent *de;
 209
 210        if (!path)
 211                path = PROC_NET_STAT;
 212
 213        dir = opendir(path);
 214        if (!dir) {
 215                struct lnstat_file *lf;
 216                /* Old kernel, before /proc/net/stat was introduced */
 217                fprintf(stderr, "Your kernel doesn't have lnstat support. ");
 218
 219                /* we only support rtstat, not multiple files */
 220                if (num_req_files >= 2) {
 221                        fputc('\n', stderr);
 222                        return NULL;
 223                }
 224
 225                /* we really only accept rt_cache */
 226                if (num_req_files && !name_in_array(num_req_files,
 227                                                    req_files, "rt_cache")) {
 228                        fputc('\n', stderr);
 229                        return NULL;
 230                }
 231
 232                fprintf(stderr, "Fallback to old rtstat-only operation\n");
 233
 234                lf = alloc_and_open("/proc/net", "rt_cache_stat");
 235                if (!lf)
 236                        return NULL;
 237                lf->compat = 1;
 238                strncpy(lf->basename, "rt_cache", sizeof(lf->basename));
 239
 240                /* FIXME: support for old files */
 241                if (lnstat_scan_compat_rtstat_fields(lf) < 0)
 242                        return NULL;
 243
 244                lf->next = lnstat_files;
 245                lnstat_files = lf;
 246                return lnstat_files;
 247        }
 248
 249        while ((de = readdir(dir))) {
 250                struct lnstat_file *lf;
 251
 252                if (de->d_type != DT_REG)
 253                        continue;
 254
 255                if (num_req_files && !name_in_array(num_req_files,
 256                                                    req_files, de->d_name))
 257                        continue;
 258
 259                lf = alloc_and_open(path, de->d_name);
 260                if (!lf) {
 261                        closedir(dir);
 262                        return NULL;
 263                }
 264
 265                /* fill in field structure */
 266                if (lnstat_scan_fields(lf) < 0) {
 267                        closedir(dir);
 268                        return NULL;
 269                }
 270
 271                /* prepend to global list */
 272                lf->next = lnstat_files;
 273                lnstat_files = lf;
 274        }
 275        closedir(dir);
 276
 277        return lnstat_files;
 278}
 279
 280int lnstat_dump(FILE *outfd, struct lnstat_file *lnstat_files)
 281{
 282        struct lnstat_file *lf;
 283
 284        for (lf = lnstat_files; lf; lf = lf->next) {
 285                int i;
 286
 287                fprintf(outfd, "%s:\n", lf->path);
 288
 289                for (i = 0; i < lf->num_fields; i++)
 290                        fprintf(outfd, "\t%2u: %s\n", i+1, lf->fields[i].name);
 291
 292        }
 293        return 0;
 294}
 295
 296struct lnstat_field *lnstat_find_field(struct lnstat_file *lnstat_files,
 297                                       const char *name)
 298{
 299        struct lnstat_file *lf;
 300        struct lnstat_field *ret = NULL;
 301        const char *colon = strchr(name, ':');
 302        char *file;
 303        const char *field;
 304
 305        if (colon) {
 306                file = strndup(name, colon-name);
 307                field = colon+1;
 308        } else {
 309                file = NULL;
 310                field = name;
 311        }
 312
 313        for (lf = lnstat_files; lf; lf = lf->next) {
 314                int i;
 315
 316                if (file && strcmp(file, lf->basename))
 317                        continue;
 318
 319                for (i = 0; i < lf->num_fields; i++) {
 320                        if (!strcmp(field, lf->fields[i].name)) {
 321                                ret = &lf->fields[i];
 322                                goto out;
 323                        }
 324                }
 325        }
 326out:
 327        free(file);
 328
 329        return ret;
 330}
 331