1 /* lnstat.c: Unified linux network statistics
3 * Copyright (C) 2004 by Harald Welte <laforge@gnumonks.org>
5 * Development of this code was funded by Astaro AG, http://www.astaro.com/
7 * Based on original concept and ideas from predecessor rtstat.c:
9 * Copyright 2001 by Robert Olsson <robert.olsson@its.uu.se>
10 * Uppsala University, Sweden
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.
28 #include <sys/types.h>
32 /* size of temp buffer used to read lines from procfiles */
33 #define FGETS_BUF_SIZE 1024
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"
38 /* Read (and summarize for SMP) the different stats vars. */
39 static int scan_lines(struct lnstat_file
*lf
, int i
)
41 char buf
[FGETS_BUF_SIZE
];
44 for (j
= 0; j
< lf
->num_fields
; j
++)
45 lf
->fields
[j
].values
[i
] = 0;
49 if (!lf
->compat
&& !fgets(buf
, sizeof(buf
)-1, lf
->fp
))
52 while (!feof(lf
->fp
) && fgets(buf
, sizeof(buf
)-1, lf
->fp
)) {
57 gettimeofday(&lf
->last_read
, NULL
);
59 for (j
= 0; j
< lf
->num_fields
; j
++) {
60 unsigned long f
= strtoul(ptr
, &ptr
, 16);
63 lf
->fields
[j
].values
[i
] = f
;
65 lf
->fields
[j
].values
[i
] += f
;
71 static int time_after(struct timeval
*last
,
75 if (now
->tv_sec
> last
->tv_sec
+ tout
->tv_sec
)
78 if (now
->tv_sec
== last
->tv_sec
+ tout
->tv_sec
) {
79 if (now
->tv_usec
> last
->tv_usec
+ tout
->tv_usec
)
86 int lnstat_update(struct lnstat_file
*lnstat_files
)
88 struct lnstat_file
*lf
;
91 gettimeofday(&tv
, NULL
);
93 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
94 if (time_after(&lf
->last_read
, &lf
->interval
, &tv
)) {
96 struct lnstat_field
*lfi
;
100 for (i
= 0, lfi
= &lf
->fields
[i
];
101 i
< lf
->num_fields
; i
++, lfi
= &lf
->fields
[i
]) {
103 lfi
->result
= lfi
->values
[1];
105 lfi
->result
= (lfi
->values
[1]-lfi
->values
[0])
106 / lf
->interval
.tv_sec
;
116 /* scan first template line and fill in per-field data structures */
117 static int __lnstat_scan_fields(struct lnstat_file
*lf
, char *buf
)
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");
130 lf
->num_fields
= i
+1;
137 static int lnstat_scan_fields(struct lnstat_file
*lf
)
139 char buf
[FGETS_BUF_SIZE
];
142 if (!fgets(buf
, sizeof(buf
)-1, lf
->fp
))
145 return __lnstat_scan_fields(lf
, buf
);
148 /* fake function emulating lnstat_scan_fields() for old kernels */
149 static int lnstat_scan_compat_rtstat_fields(struct lnstat_file
*lf
)
151 char buf
[FGETS_BUF_SIZE
];
153 strncpy(buf
, RTSTAT_COMPAT_LINE
, sizeof(buf
) - 1);
154 buf
[sizeof(buf
) - 1] = '\0';
156 return __lnstat_scan_fields(lf
, buf
);
159 /* find out whether string 'name; is in given string array */
160 static int name_in_array(const int num
, const char **arr
, const char *name
)
164 for (i
= 0; i
< num
; i
++) {
165 if (!strcmp(arr
[i
], name
))
171 /* allocate lnstat_file and open given file */
172 static struct lnstat_file
*alloc_and_open(const char *path
, const char *file
)
174 struct lnstat_file
*lf
;
177 lf
= calloc(1, sizeof(*lf
));
179 fprintf(stderr
, "out of memory\n");
184 snprintf(lf
->basename
, sizeof(lf
->basename
), "%s", file
);
185 snprintf(lf
->path
, sizeof(lf
->path
), "%s/%s", path
, file
);
187 /* initialize to default */
188 lf
->interval
.tv_sec
= 1;
191 lf
->fp
= fopen(lf
->path
, "r");
202 /* lnstat_scan_dir - find and parse all available statistics files/fields */
203 struct lnstat_file
*lnstat_scan_dir(const char *path
, const int num_req_files
,
204 const char **req_files
)
207 struct lnstat_file
*lnstat_files
= NULL
;
211 path
= PROC_NET_STAT
;
215 struct lnstat_file
*lf
;
216 /* Old kernel, before /proc/net/stat was introduced */
217 fprintf(stderr
, "Your kernel doesn't have lnstat support. ");
219 /* we only support rtstat, not multiple files */
220 if (num_req_files
>= 2) {
225 /* we really only accept rt_cache */
226 if (num_req_files
&& !name_in_array(num_req_files
,
227 req_files
, "rt_cache")) {
232 fprintf(stderr
, "Fallback to old rtstat-only operation\n");
234 lf
= alloc_and_open("/proc/net", "rt_cache_stat");
238 strncpy(lf
->basename
, "rt_cache", sizeof(lf
->basename
));
240 /* FIXME: support for old files */
241 if (lnstat_scan_compat_rtstat_fields(lf
) < 0)
244 lf
->next
= lnstat_files
;
249 while ((de
= readdir(dir
))) {
250 struct lnstat_file
*lf
;
252 if (de
->d_type
!= DT_REG
)
255 if (num_req_files
&& !name_in_array(num_req_files
,
256 req_files
, de
->d_name
))
259 lf
= alloc_and_open(path
, de
->d_name
);
265 /* fill in field structure */
266 if (lnstat_scan_fields(lf
) < 0) {
271 /* prepend to global list */
272 lf
->next
= lnstat_files
;
280 int lnstat_dump(FILE *outfd
, struct lnstat_file
*lnstat_files
)
282 struct lnstat_file
*lf
;
284 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
287 fprintf(outfd
, "%s:\n", lf
->path
);
289 for (i
= 0; i
< lf
->num_fields
; i
++)
290 fprintf(outfd
, "\t%2u: %s\n", i
+1, lf
->fields
[i
].name
);
296 struct lnstat_field
*lnstat_find_field(struct lnstat_file
*lnstat_files
,
299 struct lnstat_file
*lf
;
300 struct lnstat_field
*ret
= NULL
;
301 const char *colon
= strchr(name
, ':');
306 file
= strndup(name
, colon
-name
);
313 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
316 if (file
&& strcmp(file
, lf
->basename
))
319 for (i
= 0; i
< lf
->num_fields
; i
++) {
320 if (!strcmp(field
, lf
->fields
[i
].name
)) {
321 ret
= &lf
->fields
[i
];