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
)
43 for (j
= 0; j
< lf
->num_fields
; j
++)
44 lf
->fields
[j
].values
[i
] = 0;
46 while(!feof(lf
->fp
)) {
47 char buf
[FGETS_BUF_SIZE
];
52 fgets(buf
, sizeof(buf
)-1, lf
->fp
);
53 gettimeofday(&lf
->last_read
, NULL
);
55 for (j
= 0; j
< lf
->num_fields
; j
++) {
56 unsigned long f
= strtoul(ptr
, &ptr
, 16);
58 lf
->fields
[j
].values
[i
] = f
;
60 lf
->fields
[j
].values
[i
] += f
;
66 static int time_after(struct timeval
*last
,
70 if (now
->tv_sec
> last
->tv_sec
+ tout
->tv_sec
)
73 if (now
->tv_sec
== last
->tv_sec
+ tout
->tv_sec
) {
74 if (now
->tv_usec
> last
->tv_usec
+ tout
->tv_usec
)
81 int lnstat_update(struct lnstat_file
*lnstat_files
)
83 struct lnstat_file
*lf
;
84 char buf
[FGETS_BUF_SIZE
];
87 gettimeofday(&tv
, NULL
);
89 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
90 if (time_after(&lf
->last_read
, &lf
->interval
, &tv
)) {
92 struct lnstat_field
*lfi
;
97 fgets(buf
, sizeof(buf
)-1, lf
->fp
);
101 for (i
= 0, lfi
= &lf
->fields
[i
];
102 i
< lf
->num_fields
; i
++, lfi
= &lf
->fields
[i
]) {
104 lfi
->result
= lfi
->values
[1];
106 lfi
->result
= (lfi
->values
[1]-lfi
->values
[0])
107 / lf
->interval
.tv_sec
;
111 fgets(buf
, sizeof(buf
)-1, lf
->fp
);
119 /* scan first template line and fill in per-field data structures */
120 static int __lnstat_scan_fields(struct lnstat_file
*lf
, char *buf
)
125 tok
= strtok(buf
, " \t\n");
126 for (i
= 0; i
< LNSTAT_MAX_FIELDS_PER_LINE
; i
++) {
127 lf
->fields
[i
].file
= lf
;
128 strncpy(lf
->fields
[i
].name
, tok
, LNSTAT_MAX_FIELD_NAME_LEN
);
129 /* has to be null-terminate since we initialize to zero
130 * and field size is NAME_LEN + 1 */
131 tok
= strtok(NULL
, " \t\n");
133 lf
->num_fields
= i
+1;
140 static int lnstat_scan_fields(struct lnstat_file
*lf
)
142 char buf
[FGETS_BUF_SIZE
];
145 fgets(buf
, sizeof(buf
)-1, lf
->fp
);
147 return __lnstat_scan_fields(lf
, buf
);
150 /* fake function emulating lnstat_scan_fields() for old kernels */
151 static int lnstat_scan_compat_rtstat_fields(struct lnstat_file
*lf
)
153 char buf
[FGETS_BUF_SIZE
];
155 strncpy(buf
, RTSTAT_COMPAT_LINE
, sizeof(buf
)-1);
157 return __lnstat_scan_fields(lf
, buf
);
160 /* find out whether string 'name; is in given string array */
161 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
= malloc(sizeof(*lf
));
182 memset(lf
, 0, sizeof(*lf
));
184 /* de->d_name is guaranteed to be <= NAME_MAX */
185 strcpy(lf
->basename
, file
);
186 strcpy(lf
->path
, path
);
187 strcat(lf
->path
, "/");
188 strcat(lf
->path
, lf
->basename
);
190 /* initialize to default */
191 lf
->interval
.tv_sec
= 1;
194 lf
->fp
= fopen(lf
->path
, "r");
204 /* lnstat_scan_dir - find and parse all available statistics files/fields */
205 struct lnstat_file
*lnstat_scan_dir(const char *path
, const int num_req_files
,
206 const char **req_files
)
209 struct lnstat_file
*lnstat_files
= NULL
;
213 path
= PROC_NET_STAT
;
217 struct lnstat_file
*lf
;
218 /* Old kernel, before /proc/net/stat was introduced */
219 fprintf(stderr
, "Your kernel doesn't have lnstat support. ");
221 /* we only support rtstat, not multiple files */
222 if (num_req_files
>= 2) {
227 /* we really only accept rt_cache */
228 if (num_req_files
&& !name_in_array(num_req_files
,
229 req_files
, "rt_cache")) {
234 fprintf(stderr
, "Fallback to old rtstat-only operation\n");
236 lf
= alloc_and_open("/proc/net", "rt_cache_stat");
240 strncpy(lf
->basename
, "rt_cache", sizeof(lf
->basename
));
242 /* FIXME: support for old files */
243 if (lnstat_scan_compat_rtstat_fields(lf
) < 0)
246 lf
->next
= lnstat_files
;
251 while ((de
= readdir(dir
))) {
252 struct lnstat_file
*lf
;
254 if (de
->d_type
!= DT_REG
)
257 if (num_req_files
&& !name_in_array(num_req_files
,
258 req_files
, de
->d_name
))
261 lf
= alloc_and_open(path
, de
->d_name
);
265 /* fill in field structure */
266 if (lnstat_scan_fields(lf
) < 0)
269 /* prepend to global list */
270 lf
->next
= lnstat_files
;
278 int lnstat_dump(FILE *outfd
, struct lnstat_file
*lnstat_files
)
280 struct lnstat_file
*lf
;
282 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
285 fprintf(outfd
, "%s:\n", lf
->path
);
287 for (i
= 0; i
< lf
->num_fields
; i
++)
288 fprintf(outfd
, "\t%2u: %s\n", i
+1, lf
->fields
[i
].name
);
294 struct lnstat_field
*lnstat_find_field(struct lnstat_file
*lnstat_files
,
297 struct lnstat_file
*lf
;
298 struct lnstat_field
*ret
= NULL
;
299 const char *colon
= strchr(name
, ':');
304 file
= strndup(name
, colon
-name
);
311 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
314 if (file
&& strcmp(file
, lf
->basename
))
317 for (i
= 0; i
< lf
->num_fields
; i
++) {
318 if (!strcmp(field
, lf
->fields
[i
].name
)) {
319 ret
= &lf
->fields
[i
];