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 if (!fgets(buf
, sizeof(buf
)-1, lf
->fp
))
144 return __lnstat_scan_fields(lf
, buf
);
147 /* fake function emulating lnstat_scan_fields() for old kernels */
148 static int lnstat_scan_compat_rtstat_fields(struct lnstat_file
*lf
)
150 char buf
[FGETS_BUF_SIZE
];
152 strncpy(buf
, RTSTAT_COMPAT_LINE
, sizeof(buf
)-1);
154 return __lnstat_scan_fields(lf
, buf
);
157 /* find out whether string 'name; is in given string array */
158 static int name_in_array(const int num
, const char **arr
, const char *name
)
161 for (i
= 0; i
< num
; i
++) {
162 if (!strcmp(arr
[i
], name
))
168 /* allocate lnstat_file and open given file */
169 static struct lnstat_file
*alloc_and_open(const char *path
, const char *file
)
171 struct lnstat_file
*lf
;
174 lf
= malloc(sizeof(*lf
));
179 memset(lf
, 0, sizeof(*lf
));
181 /* de->d_name is guaranteed to be <= NAME_MAX */
182 strcpy(lf
->basename
, file
);
183 strcpy(lf
->path
, path
);
184 strcat(lf
->path
, "/");
185 strcat(lf
->path
, lf
->basename
);
187 /* initialize to default */
188 lf
->interval
.tv_sec
= 1;
191 lf
->fp
= fopen(lf
->path
, "r");
201 /* lnstat_scan_dir - find and parse all available statistics files/fields */
202 struct lnstat_file
*lnstat_scan_dir(const char *path
, const int num_req_files
,
203 const char **req_files
)
206 struct lnstat_file
*lnstat_files
= NULL
;
210 path
= PROC_NET_STAT
;
214 struct lnstat_file
*lf
;
215 /* Old kernel, before /proc/net/stat was introduced */
216 fprintf(stderr
, "Your kernel doesn't have lnstat support. ");
218 /* we only support rtstat, not multiple files */
219 if (num_req_files
>= 2) {
224 /* we really only accept rt_cache */
225 if (num_req_files
&& !name_in_array(num_req_files
,
226 req_files
, "rt_cache")) {
231 fprintf(stderr
, "Fallback to old rtstat-only operation\n");
233 lf
= alloc_and_open("/proc/net", "rt_cache_stat");
237 strncpy(lf
->basename
, "rt_cache", sizeof(lf
->basename
));
239 /* FIXME: support for old files */
240 if (lnstat_scan_compat_rtstat_fields(lf
) < 0)
243 lf
->next
= lnstat_files
;
248 while ((de
= readdir(dir
))) {
249 struct lnstat_file
*lf
;
251 if (de
->d_type
!= DT_REG
)
254 if (num_req_files
&& !name_in_array(num_req_files
,
255 req_files
, de
->d_name
))
258 lf
= alloc_and_open(path
, de
->d_name
);
262 /* fill in field structure */
263 if (lnstat_scan_fields(lf
) < 0)
266 /* prepend to global list */
267 lf
->next
= lnstat_files
;
275 int lnstat_dump(FILE *outfd
, struct lnstat_file
*lnstat_files
)
277 struct lnstat_file
*lf
;
279 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
282 fprintf(outfd
, "%s:\n", lf
->path
);
284 for (i
= 0; i
< lf
->num_fields
; i
++)
285 fprintf(outfd
, "\t%2u: %s\n", i
+1, lf
->fields
[i
].name
);
291 struct lnstat_field
*lnstat_find_field(struct lnstat_file
*lnstat_files
,
294 struct lnstat_file
*lf
;
295 struct lnstat_field
*ret
= NULL
;
296 const char *colon
= strchr(name
, ':');
301 file
= strndup(name
, colon
-name
);
308 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
311 if (file
&& strcmp(file
, lf
->basename
))
314 for (i
= 0; i
< lf
->num_fields
; i
++) {
315 if (!strcmp(field
, lf
->fields
[i
].name
)) {
316 ret
= &lf
->fields
[i
];