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);
62 lf
->fields
[j
].values
[i
] = f
;
64 lf
->fields
[j
].values
[i
] += f
;
70 static int time_after(struct timeval
*last
,
74 if (now
->tv_sec
> last
->tv_sec
+ tout
->tv_sec
)
77 if (now
->tv_sec
== last
->tv_sec
+ tout
->tv_sec
) {
78 if (now
->tv_usec
> last
->tv_usec
+ tout
->tv_usec
)
85 int lnstat_update(struct lnstat_file
*lnstat_files
)
87 struct lnstat_file
*lf
;
90 gettimeofday(&tv
, NULL
);
92 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
93 if (time_after(&lf
->last_read
, &lf
->interval
, &tv
)) {
95 struct lnstat_field
*lfi
;
99 for (i
= 0, lfi
= &lf
->fields
[i
];
100 i
< lf
->num_fields
; i
++, lfi
= &lf
->fields
[i
]) {
102 lfi
->result
= lfi
->values
[1];
104 lfi
->result
= (lfi
->values
[1]-lfi
->values
[0])
105 / lf
->interval
.tv_sec
;
115 /* scan first template line and fill in per-field data structures */
116 static int __lnstat_scan_fields(struct lnstat_file
*lf
, char *buf
)
121 tok
= strtok(buf
, " \t\n");
122 for (i
= 0; i
< LNSTAT_MAX_FIELDS_PER_LINE
; i
++) {
123 lf
->fields
[i
].file
= lf
;
124 strncpy(lf
->fields
[i
].name
, tok
, LNSTAT_MAX_FIELD_NAME_LEN
);
125 /* has to be null-terminate since we initialize to zero
126 * and field size is NAME_LEN + 1 */
127 tok
= strtok(NULL
, " \t\n");
129 lf
->num_fields
= i
+1;
136 static int lnstat_scan_fields(struct lnstat_file
*lf
)
138 char buf
[FGETS_BUF_SIZE
];
141 fgets(buf
, sizeof(buf
)-1, lf
->fp
);
143 return __lnstat_scan_fields(lf
, buf
);
146 /* fake function emulating lnstat_scan_fields() for old kernels */
147 static int lnstat_scan_compat_rtstat_fields(struct lnstat_file
*lf
)
149 char buf
[FGETS_BUF_SIZE
];
151 strncpy(buf
, RTSTAT_COMPAT_LINE
, sizeof(buf
)-1);
153 return __lnstat_scan_fields(lf
, buf
);
156 /* find out whether string 'name; is in given string array */
157 static int name_in_array(const int num
, const char **arr
, const char *name
)
160 for (i
= 0; i
< num
; i
++) {
161 if (!strcmp(arr
[i
], name
))
167 /* allocate lnstat_file and open given file */
168 static struct lnstat_file
*alloc_and_open(const char *path
, const char *file
)
170 struct lnstat_file
*lf
;
173 lf
= malloc(sizeof(*lf
));
178 memset(lf
, 0, sizeof(*lf
));
180 /* de->d_name is guaranteed to be <= NAME_MAX */
181 strcpy(lf
->basename
, file
);
182 strcpy(lf
->path
, path
);
183 strcat(lf
->path
, "/");
184 strcat(lf
->path
, lf
->basename
);
186 /* initialize to default */
187 lf
->interval
.tv_sec
= 1;
190 lf
->fp
= fopen(lf
->path
, "r");
200 /* lnstat_scan_dir - find and parse all available statistics files/fields */
201 struct lnstat_file
*lnstat_scan_dir(const char *path
, const int num_req_files
,
202 const char **req_files
)
205 struct lnstat_file
*lnstat_files
= NULL
;
209 path
= PROC_NET_STAT
;
213 struct lnstat_file
*lf
;
214 /* Old kernel, before /proc/net/stat was introduced */
215 fprintf(stderr
, "Your kernel doesn't have lnstat support. ");
217 /* we only support rtstat, not multiple files */
218 if (num_req_files
>= 2) {
223 /* we really only accept rt_cache */
224 if (num_req_files
&& !name_in_array(num_req_files
,
225 req_files
, "rt_cache")) {
230 fprintf(stderr
, "Fallback to old rtstat-only operation\n");
232 lf
= alloc_and_open("/proc/net", "rt_cache_stat");
236 strncpy(lf
->basename
, "rt_cache", sizeof(lf
->basename
));
238 /* FIXME: support for old files */
239 if (lnstat_scan_compat_rtstat_fields(lf
) < 0)
242 lf
->next
= lnstat_files
;
247 while ((de
= readdir(dir
))) {
248 struct lnstat_file
*lf
;
250 if (de
->d_type
!= DT_REG
)
253 if (num_req_files
&& !name_in_array(num_req_files
,
254 req_files
, de
->d_name
))
257 lf
= alloc_and_open(path
, de
->d_name
);
261 /* fill in field structure */
262 if (lnstat_scan_fields(lf
) < 0)
265 /* prepend to global list */
266 lf
->next
= lnstat_files
;
274 int lnstat_dump(FILE *outfd
, struct lnstat_file
*lnstat_files
)
276 struct lnstat_file
*lf
;
278 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
281 fprintf(outfd
, "%s:\n", lf
->path
);
283 for (i
= 0; i
< lf
->num_fields
; i
++)
284 fprintf(outfd
, "\t%2u: %s\n", i
+1, lf
->fields
[i
].name
);
290 struct lnstat_field
*lnstat_find_field(struct lnstat_file
*lnstat_files
,
293 struct lnstat_file
*lf
;
294 struct lnstat_field
*ret
= NULL
;
295 const char *colon
= strchr(name
, ':');
300 file
= strndup(name
, colon
-name
);
307 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
310 if (file
&& strcmp(file
, lf
->basename
))
313 for (i
= 0; i
< lf
->num_fields
; i
++) {
314 if (!strcmp(field
, lf
->fields
[i
].name
)) {
315 ret
= &lf
->fields
[i
];