ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/network_stats.c
Revision: 1.37
Committed: Sat Feb 14 00:08:51 2004 UTC (20 years, 3 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.36: +8 -5 lines
Log Message:
Rename BSD "int s" to "int sock" to match Linux code and fix compile error.
Make BSD code set the interface name before calling ioctl so it gets the
stats for the right interface.

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 i-scream
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 * 02111-1307 USA
20 *
21 * $Id: network_stats.c,v 1.36 2004/02/13 18:54:29 pajs Exp $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include "statgrab.h"
31 #include <time.h>
32 #ifdef SOLARIS
33 #include <kstat.h>
34 #include <sys/sysinfo.h>
35 #endif
36 #ifdef LINUX
37 #include <stdio.h>
38 #include <sys/types.h>
39 #include <regex.h>
40 #include <sys/ioctl.h>
41 #include <sys/socket.h>
42 #include <net/if.h>
43 #include <ctype.h>
44 #include "tools.h"
45 /* Stuff which could be defined by defining KERNEL, but
46 * that would be a bad idea, so we'll just declare it here
47 */
48 typedef __uint8_t u8;
49 typedef __uint16_t u16;
50 typedef __uint32_t u32;
51 #include <linux/ethtool.h>
52 #include <linux/sockios.h>
53 #include <unistd.h>
54 #endif
55 #ifdef ALLBSD
56 #include <sys/types.h>
57 #include <sys/socket.h>
58 #include <ifaddrs.h>
59 #include <net/if.h>
60 #include <net/if_media.h>
61 #include <sys/ioctl.h>
62 #endif
63
64 static network_stat_t *network_stats=NULL;
65 static int interfaces=0;
66
67 void network_stat_init(int start, int end, network_stat_t *net_stats){
68
69 for(net_stats+=start; start<end; start++){
70 net_stats->interface_name=NULL;
71 net_stats->tx=0;
72 net_stats->rx=0;
73 net_stats++;
74 }
75 }
76
77 network_stat_t *network_stat_malloc(int needed_entries, int *cur_entries, network_stat_t *net_stats){
78
79 if(net_stats==NULL){
80
81 if((net_stats=malloc(needed_entries * sizeof(network_stat_t)))==NULL){
82 return NULL;
83 }
84 network_stat_init(0, needed_entries, net_stats);
85 *cur_entries=needed_entries;
86
87 return net_stats;
88 }
89
90
91 if(*cur_entries<needed_entries){
92 net_stats=realloc(net_stats, (sizeof(network_stat_t)*needed_entries));
93 if(net_stats==NULL){
94 return NULL;
95 }
96 network_stat_init(*cur_entries, needed_entries, net_stats);
97 *cur_entries=needed_entries;
98 }
99
100 return net_stats;
101 }
102
103
104 network_stat_t *get_network_stats(int *entries){
105
106 static int sizeof_network_stats=0;
107 network_stat_t *network_stat_ptr;
108
109 #ifdef SOLARIS
110 kstat_ctl_t *kc;
111 kstat_t *ksp;
112 kstat_named_t *knp;
113 #endif
114
115 #ifdef LINUX
116 FILE *f;
117 /* Horrible big enough, but it should be easily big enough */
118 char line[8096];
119 regex_t regex;
120 regmatch_t line_match[4];
121 #endif
122 #ifdef ALLBSD
123 struct ifaddrs *net, *net_ptr;
124 struct if_data *net_data;
125 #endif
126
127 #ifdef ALLBSD
128 if(getifaddrs(&net) != 0){
129 return NULL;
130 }
131
132 interfaces=0;
133
134 for(net_ptr=net;net_ptr!=NULL;net_ptr=net_ptr->ifa_next){
135 if(net_ptr->ifa_addr->sa_family != AF_LINK) continue;
136 network_stats=network_stat_malloc((interfaces+1), &sizeof_network_stats, network_stats);
137 if(network_stats==NULL){
138 return NULL;
139 }
140 network_stat_ptr=network_stats+interfaces;
141
142 if(network_stat_ptr->interface_name!=NULL) free(network_stat_ptr->interface_name);
143 network_stat_ptr->interface_name=strdup(net_ptr->ifa_name);
144 if(network_stat_ptr->interface_name==NULL) return NULL;
145 net_data=(struct if_data *)net_ptr->ifa_data;
146 network_stat_ptr->rx=net_data->ifi_ibytes;
147 network_stat_ptr->tx=net_data->ifi_obytes;
148 network_stat_ptr->systime=time(NULL);
149 interfaces++;
150 }
151 freeifaddrs(net);
152 #endif
153
154 #ifdef SOLARIS
155 if ((kc = kstat_open()) == NULL) {
156 return NULL;
157 }
158
159 interfaces=0;
160
161 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
162 if (!strcmp(ksp->ks_class, "net")) {
163 kstat_read(kc, ksp, NULL);
164
165 #ifdef SOL7
166 #define RLOOKUP "rbytes"
167 #define WLOOKUP "obytes"
168 #define VALTYPE value.ui32
169 #else
170 #define RLOOKUP "rbytes64"
171 #define WLOOKUP "obytes64"
172 #define VALTYPE value.ui64
173 #endif
174
175 if((knp=kstat_data_lookup(ksp, RLOOKUP))==NULL){
176 /* Not a network interface, so skip to the next entry */
177 continue;
178 }
179
180 network_stats=network_stat_malloc((interfaces+1), &sizeof_network_stats, network_stats);
181 if(network_stats==NULL){
182 return NULL;
183 }
184 network_stat_ptr=network_stats+interfaces;
185 network_stat_ptr->rx=knp->VALTYPE;
186
187 if((knp=kstat_data_lookup(ksp, WLOOKUP))==NULL){
188 /* Not a network interface, so skip to the next entry */
189 continue;
190 }
191 network_stat_ptr->tx=knp->VALTYPE;
192 if(network_stat_ptr->interface_name!=NULL){
193 free(network_stat_ptr->interface_name);
194 }
195 network_stat_ptr->interface_name=strdup(ksp->ks_name);
196
197 network_stat_ptr->systime=time(NULL);
198 interfaces++;
199 }
200 }
201
202 kstat_close(kc);
203 #endif
204 #ifdef LINUX
205 f=fopen("/proc/net/dev", "r");
206 if(f==NULL){
207 return NULL;
208 }
209 /* read the 2 lines.. Its the title, so we dont care :) */
210 fgets(line, sizeof(line), f);
211 fgets(line, sizeof(line), f);
212
213
214 if((regcomp(&regex, "^[[:space:]]*([^:]+):[[:space:]]*([[:digit:]]+)[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+([[:digit:]]+)", REG_EXTENDED))!=0){
215 return NULL;
216 }
217
218 interfaces=0;
219
220 while((fgets(line, sizeof(line), f)) != NULL){
221 if((regexec(&regex, line, 4, line_match, 0))!=0){
222 continue;
223 }
224 network_stats=network_stat_malloc((interfaces+1), &sizeof_network_stats, network_stats);
225 if(network_stats==NULL){
226 return NULL;
227 }
228 network_stat_ptr=network_stats+interfaces;
229
230 if(network_stat_ptr->interface_name!=NULL){
231 free(network_stat_ptr->interface_name);
232 }
233
234 network_stat_ptr->interface_name=get_string_match(line, &line_match[1]);
235 network_stat_ptr->rx=get_ll_match(line, &line_match[2]);
236 network_stat_ptr->tx=get_ll_match(line, &line_match[3]);
237 network_stat_ptr->systime=time(NULL);
238
239 interfaces++;
240 }
241 fclose(f);
242 regfree(&regex);
243
244 #endif
245
246 #ifdef CYGWIN
247 return NULL;
248 #endif
249
250 *entries=interfaces;
251
252 return network_stats;
253 }
254
255 long long transfer_diff(long long new, long long old){
256 #if defined(SOL7) || defined(LINUX) || defined(FREEBSD)
257 #define MAXVAL 4294967296LL
258 #else
259 #define MAXVAL 18446744073709551616LL
260 #endif
261 long long result;
262 if(new>=old){
263 result = (new-old);
264 }else{
265 result = (MAXVAL+(new-old));
266 }
267
268 return result;
269
270 }
271
272 network_stat_t *get_network_stats_diff(int *entries) {
273 static network_stat_t *diff = NULL;
274 static int diff_count = 0;
275 network_stat_t *src, *dest;
276 int i, j, new_count;
277
278 if (network_stats == NULL) {
279 /* No previous stats, so we can't calculate a difference. */
280 return get_network_stats(entries);
281 }
282
283 /* Resize the results array to match the previous stats. */
284 diff = network_stat_malloc(interfaces, &diff_count, diff);
285 if (diff == NULL) {
286 return NULL;
287 }
288
289 /* Copy the previous stats into the result. */
290 for (i = 0; i < diff_count; i++) {
291 src = &network_stats[i];
292 dest = &diff[i];
293
294 if (dest->interface_name != NULL) {
295 free(dest->interface_name);
296 }
297 dest->interface_name = strdup(src->interface_name);
298 dest->rx = src->rx;
299 dest->tx = src->tx;
300 dest->systime = src->systime;
301 }
302
303 /* Get a new set of stats. */
304 if (get_network_stats(&new_count) == NULL) {
305 return NULL;
306 }
307
308 /* For each previous stat... */
309 for (i = 0; i < diff_count; i++) {
310 dest = &diff[i];
311
312 /* ... find the corresponding new stat ... */
313 for (j = 0; j < new_count; j++) {
314 /* Try the new stat in the same position first,
315 since that's most likely to be it. */
316 src = &network_stats[(i + j) % new_count];
317 if (strcmp(src->interface_name, dest->interface_name) == 0) {
318 break;
319 }
320 }
321 if (j == new_count) {
322 /* No match found. */
323 continue;
324 }
325
326 /* ... and subtract the previous stat from it to get the
327 difference. */
328 dest->rx = transfer_diff(src->rx, dest->rx);
329 dest->tx = transfer_diff(src->tx, dest->tx);
330 dest->systime = src->systime - dest->systime;
331 }
332
333 *entries = diff_count;
334 return diff;
335 }
336 /* NETWORK INTERFACE STATS */
337
338 void network_iface_stat_init(int start, int end, network_iface_stat_t *net_stats){
339
340 for(net_stats+=start; start<end; start++){
341 net_stats->interface_name=NULL;
342 net_stats->speed=0;
343 net_stats->dup=UNKNOWN_DUPLEX;
344 net_stats++;
345 }
346 }
347
348 network_iface_stat_t *network_iface_stat_malloc(int needed_entries, int *cur_entries, network_iface_stat_t *net_stats){
349
350 if(net_stats==NULL){
351
352 if((net_stats=malloc(needed_entries * sizeof(network_iface_stat_t)))==NULL){
353 return NULL;
354 }
355 network_iface_stat_init(0, needed_entries, net_stats);
356 *cur_entries=needed_entries;
357
358 return net_stats;
359 }
360
361
362 if(*cur_entries<needed_entries){
363 net_stats=realloc(net_stats, (sizeof(network_iface_stat_t)*needed_entries));
364 if(net_stats==NULL){
365 return NULL;
366 }
367 network_iface_stat_init(*cur_entries, needed_entries, net_stats);
368 *cur_entries=needed_entries;
369 }
370
371 return net_stats;
372 }
373
374 network_iface_stat_t *get_network_iface_stats(int *entries){
375 static network_iface_stat_t *network_iface_stats;
376 network_iface_stat_t *network_iface_stat_ptr;
377 static int sizeof_network_iface_stats=0;
378 static int ifaces;
379
380 #ifdef SOLARIS
381 kstat_ctl_t *kc;
382 kstat_t *ksp;
383 kstat_named_t *knp;
384 #endif
385 #ifdef ALLBSD
386 struct ifaddrs *net, *net_ptr;
387 struct ifmediareq ifmed;
388 struct ifreq ifr;
389 int sock;
390 int x;
391 #endif
392 #ifdef LINUX
393 FILE *f;
394 /* Horrible big enough, but it should be easily big enough */
395 char line[8096];
396 void *eth_tool_cmd_buf;
397 int buf_size;
398 int sock;
399 #endif
400 ifaces = 0;
401 #ifdef ALLBSD
402 if(getifaddrs(&net) != 0){
403 return NULL;
404 }
405
406 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == 0) return NULL;
407
408 for(net_ptr=net; net_ptr!=NULL; net_ptr=net_ptr->ifa_next){
409 if(net_ptr->ifa_addr->sa_family != AF_LINK) continue;
410 network_iface_stats=network_iface_stat_malloc((ifaces+1), &sizeof_network_iface_stats, network_iface_stats);
411 if(network_iface_stats==NULL){
412 return NULL;
413 }
414 network_iface_stat_ptr = network_iface_stats + ifaces;
415
416 memset(&ifmed, 0, sizeof(struct ifmediareq));
417 strlcpy(ifmed.ifm_name, net_ptr->ifa_name, sizeof(ifmed.ifm_name));
418 if(ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed) == -1){
419 continue;
420 }
421
422 /* We may need to change this if we start doing wireless devices too */
423 if( (ifmed.ifm_active | IFM_ETHER) != ifmed.ifm_active ){
424 /* Not a ETHER device */
425 continue;
426 }
427
428 if(network_iface_stat_ptr->interface_name != NULL) free(network_iface_stat_ptr->interface_name);
429 network_iface_stat_ptr->interface_name = strdup(net_ptr->ifa_name);
430 if(network_iface_stat_ptr->interface_name == NULL) return NULL;
431
432 /* Only intrested in the first 4 bits) - Assuming only ETHER devices */
433 x = ifmed.ifm_active & 0x0f;
434 switch(x){
435 /* 10 Mbit connections. Speedy :) */
436 case(IFM_10_T):
437 case(IFM_10_2):
438 case(IFM_10_5):
439 case(IFM_10_STP):
440 case(IFM_10_FL):
441 network_iface_stat_ptr->speed = 10;
442 break;
443 /* 100 Mbit conneections */
444 case(IFM_100_TX):
445 case(IFM_100_FX):
446 case(IFM_100_T4):
447 case(IFM_100_VG):
448 case(IFM_100_T2):
449 network_iface_stat_ptr->speed = 100;
450 break;
451 /* 1000 Mbit connections */
452 case(IFM_1000_SX):
453 case(IFM_1000_LX):
454 case(IFM_1000_CX):
455 #if defined(FREEBSD) && !defined(FREEBSD5)
456 case(IFM_1000_TX):
457 case(IFM_1000_FX):
458 #else
459 case(IFM_1000_T):
460 #endif
461 network_iface_stat_ptr->speed = 1000;
462 break;
463 /* We don't know what it is */
464 default:
465 network_iface_stat_ptr->speed = 0;
466 break;
467 }
468
469 if( (ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active ){
470 network_iface_stat_ptr->dup = FULL_DUPLEX;
471 }else if( (ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active ){
472 network_iface_stat_ptr->dup = HALF_DUPLEX;
473 }else{
474 network_iface_stat_ptr->dup = UNKNOWN_DUPLEX;
475 }
476
477 memset(&ifr, 0, sizeof(ifr));
478 strncpy(ifr.ifr_name, net_ptr->ifa_name, sizeof(ifr.ifr_name));
479
480 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0){
481 continue;
482 }
483 if((ifr.ifr_flags & IFF_UP) != 0){
484 network_iface_stat_ptr->up = 1;
485 }else{
486 network_iface_stat_ptr->up = 0;
487 }
488
489 ifaces++;
490 }
491 freeifaddrs(net);
492 close(sock);
493 #endif
494
495 #ifdef SOLARIS
496 if ((kc = kstat_open()) == NULL) {
497 return NULL;
498 }
499
500 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
501 if (!strcmp(ksp->ks_class, "net")) {
502 kstat_read(kc, ksp, NULL);
503 if((knp=kstat_data_lookup(ksp, "ifspeed"))==NULL){
504 /* Not a network interface, so skip to the next entry */
505 continue;
506 }
507 network_iface_stats=network_iface_stat_malloc((ifaces+1), &sizeof_network_iface_stats, network_iface_stats);
508 if(network_iface_stats==NULL){
509 return NULL;
510 }
511 network_iface_stat_ptr = network_iface_stats + ifaces;
512 network_iface_stat_ptr->speed = knp->value.ui64 / (1000*1000);
513
514 if((knp=kstat_data_lookup(ksp, "link_up"))==NULL){
515 /* Not a network interface, so skip to the next entry */
516 continue;
517 }
518 /* Solaris has 1 for up, and 0 for not. As we do too */
519 network_iface_stat_ptr->up = value.ui32;
520
521 if((knp=kstat_data_lookup(ksp, "link_duplex"))==NULL){
522 /* Not a network interface, so skip to the next entry */
523 continue;
524 }
525
526 network_iface_stat_ptr->dup = UNKNOWN_DUPLEX;
527 if(knp->value.ui32 == 2){
528 network_iface_stat_ptr->dup = FULL_DUPLEX;
529 }
530 if(knp->value.ui32 == 1){
531 network_iface_stat_ptr->dup = HALF_DUPLEX;
532 }
533
534 if(network_iface_stat_ptr->interface_name != NULL) free(network_iface_stat_ptr->interface_name);
535 network_iface_stat_ptr->interface_name = strdup(ksp->ks_name);
536 if(network_iface_stat_ptr->interface_name == NULL) return NULL;
537 ifaces++;
538 }
539 }
540 kstat_close(kc);
541
542 #endif
543 #ifdef LINUX
544
545 f = fopen("/proc/net/dev", "r");
546 if(f == NULL){
547 return NULL;
548 }
549
550 /* Setup stuff so we can do the ioctl to get the info */
551 if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
552 return NULL;
553 }
554
555 buf_size = sizeof(struct ethtool_cmd);
556 eth_tool_cmd_buf = malloc(buf_size);
557 if(eth_tool_cmd_buf == NULL) return NULL;
558
559 /* Ignore first 2 lines.. Just headings */
560 if((fgets(line, sizeof(line), f)) == NULL) return NULL;
561 if((fgets(line, sizeof(line), f)) == NULL) return NULL;
562
563 while((fgets(line, sizeof(line), f)) != NULL){
564 char *name, *ptr;
565 struct ifreq ifr;
566 struct ethtool_cmd *ethcmd;
567 int err;
568
569 /* Get the interface name */
570 ptr = strchr(line, ':');
571 if (ptr == NULL) continue;
572 *ptr='\0';
573 name = line;
574 while(isspace(*(name))){
575 name++;
576 }
577
578 memset(&ifr, 0, sizeof(ifr));
579 memset(eth_tool_cmd_buf, 0, buf_size);
580 ifr.ifr_data = (caddr_t) eth_tool_cmd_buf;
581 strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
582
583 ethcmd = (struct ethtool_cmd *) ifr.ifr_data;
584 ethcmd->cmd = ETHTOOL_GSET;
585
586 err = ioctl(sock, SIOCETHTOOL, &ifr);
587 if(err < 0){
588 /* This could fail if the interface doesn't support the command. Carry
589 * on to the next :)
590 */
591 continue;
592 }
593
594 /* We have a good interface to add */
595 network_iface_stats=network_iface_stat_malloc((ifaces+1), &sizeof_network_iface_stats, network_iface_stats);
596 if(network_iface_stats==NULL){
597 return NULL;
598 }
599 network_iface_stat_ptr = network_iface_stats + ifaces;
600 network_iface_stat_ptr->interface_name = strdup(name);
601 network_iface_stat_ptr->speed = ethcmd->speed;
602 if((ifr.ifr_flags & IFF_UP) != 0){
603 network_iface_stat_ptr->up = 1;
604 }else{
605 network_iface_stat_ptr->up = 0;
606 }
607
608 network_iface_stat_ptr->dup = UNKNOWN_DUPLEX;
609 if(ethcmd->duplex == 0x00){
610 network_iface_stat_ptr->dup = FULL_DUPLEX;
611 }
612 if(ethcmd->duplex == 0x01){
613 network_iface_stat_ptr->dup = HALF_DUPLEX;
614 }
615 ifaces++;
616 }
617 close(sock);
618 free(eth_tool_cmd_buf);
619 #endif
620 *entries = ifaces;
621 return network_iface_stats;
622 }
623