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
));
176 fprintf(stderr
, "out of memory\n");
181 memset(lf
, 0, sizeof(*lf
));
183 /* de->d_name is guaranteed to be <= NAME_MAX */
184 strcpy(lf
->basename
, file
);
185 strcpy(lf
->path
, path
);
186 strcat(lf
->path
, "/");
187 strcat(lf
->path
, lf
->basename
);
189 /* initialize to default */
190 lf
->interval
.tv_sec
= 1;
193 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
);
267 /* fill in field structure */
268 if (lnstat_scan_fields(lf
) < 0) {
273 /* prepend to global list */
274 lf
->next
= lnstat_files
;
282 int lnstat_dump(FILE *outfd
, struct lnstat_file
*lnstat_files
)
284 struct lnstat_file
*lf
;
286 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
289 fprintf(outfd
, "%s:\n", lf
->path
);
291 for (i
= 0; i
< lf
->num_fields
; i
++)
292 fprintf(outfd
, "\t%2u: %s\n", i
+1, lf
->fields
[i
].name
);
298 struct lnstat_field
*lnstat_find_field(struct lnstat_file
*lnstat_files
,
301 struct lnstat_file
*lf
;
302 struct lnstat_field
*ret
= NULL
;
303 const char *colon
= strchr(name
, ':');
308 file
= strndup(name
, colon
-name
);
315 for (lf
= lnstat_files
; lf
; lf
= lf
->next
) {
318 if (file
&& strcmp(file
, lf
->basename
))
321 for (i
= 0; i
< lf
->num_fields
; i
++) {
322 if (!strcmp(field
, lf
->fields
[i
].name
)) {
323 ret
= &lf
->fields
[i
];