ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/network_stats.c
Revision: 1.79
Committed: Mon Oct 9 17:23:07 2006 UTC (17 years, 7 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.78: +8 -7 lines
Log Message:
Move reading the interface name to earlier in the function. This fixes
problems for us on a host with slightly peculiar interfaces.

File Contents

# User Rev Content
1 tdb 1.22 /*
2 tdb 1.64 * i-scream libstatgrab
3 tdb 1.15 * http://www.i-scream.org
4 tdb 1.22 * Copyright (C) 2000-2004 i-scream
5 pajs 1.1 *
6 tdb 1.22 * 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 pajs 1.1 *
11 tdb 1.22 * This library is distributed in the hope that it will be useful,
12 pajs 1.1 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 tdb 1.22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14     * Lesser General Public License for more details.
15 pajs 1.1 *
16 tdb 1.22 * 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 tdb 1.23 *
21 tdb 1.79 * $Id: network_stats.c,v 1.78 2006/10/09 14:09:38 tdb Exp $
22 pajs 1.1 */
23    
24     #ifdef HAVE_CONFIG_H
25     #include "config.h"
26     #endif
27    
28 tdb 1.4 #include <stdlib.h>
29 pajs 1.6 #include <string.h>
30 pajs 1.1 #include "statgrab.h"
31 ats 1.59 #include "vector.h"
32 ats 1.60 #include "tools.h"
33 tdb 1.11 #include <time.h>
34 pajs 1.1 #ifdef SOLARIS
35     #include <kstat.h>
36     #include <sys/sysinfo.h>
37 ats 1.44 #include <sys/types.h>
38     #include <sys/socket.h>
39     #include <sys/ioctl.h>
40     #include <net/if.h>
41     #include <netinet/in.h>
42     #include <sys/sockio.h>
43 tdb 1.61 #include <unistd.h>
44 pajs 1.6 #endif
45     #ifdef LINUX
46     #include <stdio.h>
47     #include <sys/types.h>
48 pajs 1.27 #include <sys/ioctl.h>
49     #include <sys/socket.h>
50     #include <net/if.h>
51     #include <ctype.h>
52 tdb 1.73 #include <linux/version.h>
53 ats 1.75 #include <asm/types.h>
54     /* These aren't defined by asm/types.h unless the kernel is being
55     compiled, but some versions of ethtool.h need them. */
56 pajs 1.27 typedef __uint8_t u8;
57     typedef __uint16_t u16;
58     typedef __uint32_t u32;
59 ats 1.42 typedef __uint64_t u64;
60 pajs 1.27 #include <linux/ethtool.h>
61     #include <linux/sockios.h>
62 ats 1.32 #include <unistd.h>
63 pajs 1.1 #endif
64 ats 1.18 #ifdef ALLBSD
65 pajs 1.14 #include <sys/types.h>
66     #include <sys/socket.h>
67     #include <ifaddrs.h>
68     #include <net/if.h>
69 pajs 1.26 #include <net/if_media.h>
70     #include <sys/ioctl.h>
71 tdb 1.58 #include <unistd.h>
72 pajs 1.14 #endif
73 tdb 1.76 #ifdef WIN32
74     #include <windows.h>
75     #include <Iphlpapi.h>
76     #include "win32.h"
77     #endif
78 pajs 1.1
79 ats 1.62 static void network_stat_init(sg_network_io_stats *s) {
80 ats 1.59 s->interface_name = NULL;
81     s->tx = 0;
82     s->rx = 0;
83     s->ipackets = 0;
84     s->opackets = 0;
85     s->ierrors = 0;
86     s->oerrors = 0;
87     s->collisions = 0;
88 pajs 1.3 }
89    
90 ats 1.62 static void network_stat_destroy(sg_network_io_stats *s) {
91 ats 1.59 free(s->interface_name);
92 pajs 1.1 }
93    
94 ats 1.62 VECTOR_DECLARE_STATIC(network_stats, sg_network_io_stats, 5,
95 tdb 1.65 network_stat_init, network_stat_destroy);
96 pajs 1.3
97 tdb 1.76 #ifdef WIN32
98     static PMIB_IFTABLE win32_get_devices()
99     {
100     PMIB_IFTABLE if_table;
101     PMIB_IFTABLE tmp;
102     unsigned long dwSize = 0;
103    
104     // Allocate memory for pointers
105     if_table = sg_malloc(sizeof(MIB_IFTABLE));
106     if(if_table == NULL) {
107     return NULL;
108     }
109    
110     // Get necessary size for the buffer
111     if(GetIfTable(if_table, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
112     tmp = sg_realloc(if_table, dwSize);
113     if(tmp == NULL) {
114     free(if_table);
115     return NULL;
116     }
117     if_table = tmp;
118     }
119    
120     // Get the data
121     if(GetIfTable(if_table, &dwSize, 0) != NO_ERROR) {
122     free(if_table);
123     return NULL;
124     }
125     return if_table;
126     }
127     #endif /* WIN32 */
128    
129 ats 1.62 sg_network_io_stats *sg_get_network_io_stats(int *entries){
130 ats 1.59 int interfaces;
131 ats 1.62 sg_network_io_stats *network_stat_ptr;
132 pajs 1.6
133     #ifdef SOLARIS
134 tdb 1.65 kstat_ctl_t *kc;
135     kstat_t *ksp;
136 pajs 1.1 kstat_named_t *knp;
137 pajs 1.6 #endif
138 pajs 1.3
139 pajs 1.6 #ifdef LINUX
140     FILE *f;
141 pajs 1.12 /* Horrible big enough, but it should be easily big enough */
142 pajs 1.6 char line[8096];
143     regex_t regex;
144 ats 1.49 regmatch_t line_match[9];
145 pajs 1.14 #endif
146 ats 1.18 #ifdef ALLBSD
147 pajs 1.14 struct ifaddrs *net, *net_ptr;
148     struct if_data *net_data;
149     #endif
150 tdb 1.76 #ifdef WIN32
151     PMIB_IFTABLE if_table;
152     MIB_IFROW if_row;
153     int i, no, j;
154    
155     /* used for duplicate interface names. 5 for space, hash, up to two
156     * numbers and terminating slash */
157     char buf[5];
158     #endif
159 pajs 1.14
160 ats 1.18 #ifdef ALLBSD
161 pajs 1.14 if(getifaddrs(&net) != 0){
162 ats 1.69 sg_set_error_with_errno(SG_ERROR_GETIFADDRS, NULL);
163 pajs 1.14 return NULL;
164     }
165    
166     interfaces=0;
167    
168     for(net_ptr=net;net_ptr!=NULL;net_ptr=net_ptr->ifa_next){
169     if(net_ptr->ifa_addr->sa_family != AF_LINK) continue;
170 ats 1.59
171     if (VECTOR_RESIZE(network_stats, interfaces + 1) < 0) {
172 pajs 1.14 return NULL;
173     }
174     network_stat_ptr=network_stats+interfaces;
175    
176 ats 1.62 if (sg_update_string(&network_stat_ptr->interface_name,
177 tdb 1.65 net_ptr->ifa_name) < 0) {
178 ats 1.60 return NULL;
179     }
180 pajs 1.14 net_data=(struct if_data *)net_ptr->ifa_data;
181     network_stat_ptr->rx=net_data->ifi_ibytes;
182 tdb 1.46 network_stat_ptr->tx=net_data->ifi_obytes;
183     network_stat_ptr->ipackets=net_data->ifi_ipackets;
184     network_stat_ptr->opackets=net_data->ifi_opackets;
185     network_stat_ptr->ierrors=net_data->ifi_ierrors;
186     network_stat_ptr->oerrors=net_data->ifi_oerrors;
187     network_stat_ptr->collisions=net_data->ifi_collisions;
188 pajs 1.14 network_stat_ptr->systime=time(NULL);
189     interfaces++;
190     }
191     freeifaddrs(net);
192 pajs 1.6 #endif
193 pajs 1.1
194 pajs 1.6 #ifdef SOLARIS
195 tdb 1.65 if ((kc = kstat_open()) == NULL) {
196 tdb 1.66 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
197 tdb 1.65 return NULL;
198     }
199 pajs 1.1
200 pajs 1.6 interfaces=0;
201 pajs 1.3
202 tdb 1.66 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
203 tdb 1.76 if (strcmp(ksp->ks_class, "net") == 0) {
204 tdb 1.65 kstat_read(kc, ksp, NULL);
205 pajs 1.1
206 pajs 1.7 #ifdef SOL7
207 tdb 1.53 #define LRX "rbytes"
208     #define LTX "obytes"
209     #define LIPACKETS "ipackets"
210     #define LOPACKETS "opackets"
211 pajs 1.7 #define VALTYPE value.ui32
212     #else
213 tdb 1.53 #define LRX "rbytes64"
214     #define LTX "obytes64"
215     #define LIPACKETS "ipackets64"
216     #define LOPACKETS "opackets64"
217 pajs 1.7 #define VALTYPE value.ui64
218     #endif
219    
220 tdb 1.53 /* Read rx */
221     if((knp=kstat_data_lookup(ksp, LRX))==NULL){
222 ats 1.44 /* This is a network interface, but it doesn't
223     * have the rbytes/obytes values; for instance,
224     * the loopback devices have this behaviour
225     * (although they do track packets in/out). */
226 ats 1.55 /* FIXME: Show packet counts when byte counts
227     * not available. */
228 pajs 1.1 continue;
229     }
230 pajs 1.3
231 tdb 1.53 /* Create new network_stats */
232 ats 1.59 if (VECTOR_RESIZE(network_stats, interfaces + 1) < 0) {
233 tdb 1.78 kstat_close(kc);
234 pajs 1.1 return NULL;
235     }
236     network_stat_ptr=network_stats+interfaces;
237 tdb 1.53
238 tdb 1.79 /* Read interface name */
239     if (sg_update_string(&network_stat_ptr->interface_name,
240     ksp->ks_name) < 0) {
241     kstat_close(kc);
242     return NULL;
243     }
244    
245 tdb 1.53 /* Finish reading rx */
246 pajs 1.7 network_stat_ptr->rx=knp->VALTYPE;
247 pajs 1.1
248 tdb 1.53 /* Read tx */
249     if((knp=kstat_data_lookup(ksp, LTX))==NULL){
250 pajs 1.1 continue;
251     }
252 pajs 1.7 network_stat_ptr->tx=knp->VALTYPE;
253 tdb 1.53
254     /* Read ipackets */
255     if((knp=kstat_data_lookup(ksp, LIPACKETS))==NULL){
256     continue;
257     }
258     network_stat_ptr->ipackets=knp->VALTYPE;
259    
260     /* Read opackets */
261     if((knp=kstat_data_lookup(ksp, LOPACKETS))==NULL){
262     continue;
263     }
264     network_stat_ptr->opackets=knp->VALTYPE;
265    
266     /* Read ierrors */
267     if((knp=kstat_data_lookup(ksp, "ierrors"))==NULL){
268     continue;
269     }
270     network_stat_ptr->ierrors=knp->value.ui32;
271    
272     /* Read oerrors */
273     if((knp=kstat_data_lookup(ksp, "oerrors"))==NULL){
274     continue;
275     }
276     network_stat_ptr->oerrors=knp->value.ui32;
277    
278     /* Read collisions */
279     if((knp=kstat_data_lookup(ksp, "collisions"))==NULL){
280     continue;
281     }
282     network_stat_ptr->collisions=knp->value.ui32;
283    
284 pajs 1.3
285 tdb 1.53 /* Store systime */
286 pajs 1.3 network_stat_ptr->systime=time(NULL);
287 tdb 1.53
288 pajs 1.1 interfaces++;
289     }
290     }
291 pajs 1.2
292     kstat_close(kc);
293 pajs 1.6 #endif
294     #ifdef LINUX
295     f=fopen("/proc/net/dev", "r");
296     if(f==NULL){
297 ats 1.69 sg_set_error_with_errno(SG_ERROR_OPEN, "/proc/net/dev");
298 pajs 1.6 return NULL;
299     }
300     /* read the 2 lines.. Its the title, so we dont care :) */
301     fgets(line, sizeof(line), f);
302     fgets(line, sizeof(line), f);
303    
304    
305 ats 1.50 if((regcomp(&regex, "^ *([^:]+): *([0-9]+) +([0-9]+) +([0-9]+) +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+ +([0-9]+) +([0-9]+) +([0-9]+) +[0-9]+ +[0-9]+ +([0-9]+)", REG_EXTENDED))!=0){
306 tdb 1.66 sg_set_error(SG_ERROR_PARSE, NULL);
307 pajs 1.6 return NULL;
308     }
309    
310     interfaces=0;
311 pajs 1.1
312 pajs 1.6 while((fgets(line, sizeof(line), f)) != NULL){
313 ats 1.51 if((regexec(&regex, line, 9, line_match, 0))!=0){
314 pajs 1.6 continue;
315     }
316 ats 1.59
317     if (VECTOR_RESIZE(network_stats, interfaces + 1) < 0) {
318     return NULL;
319 pajs 1.6 }
320 tdb 1.65 network_stat_ptr=network_stats+interfaces;
321 pajs 1.6
322     if(network_stat_ptr->interface_name!=NULL){
323     free(network_stat_ptr->interface_name);
324     }
325    
326 ats 1.62 network_stat_ptr->interface_name=sg_get_string_match(line, &line_match[1]);
327     network_stat_ptr->rx=sg_get_ll_match(line, &line_match[2]);
328     network_stat_ptr->tx=sg_get_ll_match(line, &line_match[5]);
329     network_stat_ptr->ipackets=sg_get_ll_match(line, &line_match[3]);
330     network_stat_ptr->opackets=sg_get_ll_match(line, &line_match[6]);
331     network_stat_ptr->ierrors=sg_get_ll_match(line, &line_match[4]);
332     network_stat_ptr->oerrors=sg_get_ll_match(line, &line_match[7]);
333     network_stat_ptr->collisions=sg_get_ll_match(line, &line_match[8]);
334 pajs 1.6 network_stat_ptr->systime=time(NULL);
335    
336     interfaces++;
337     }
338 pajs 1.12 fclose(f);
339 pajs 1.13 regfree(&regex);
340 pajs 1.6
341     #endif
342 ats 1.20
343     #ifdef CYGWIN
344 tdb 1.66 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
345 ats 1.70 return NULL;
346     #endif
347     #ifdef HPUX
348     sg_set_error(SG_ERROR_UNSUPPORTED, "HP-UX");
349 ats 1.20 return NULL;
350     #endif
351    
352 tdb 1.76 #ifdef WIN32
353     interfaces = 0;
354    
355     if((if_table = win32_get_devices()) == NULL) {
356     sg_set_error(SG_ERROR_DEVICES, "network");
357     return NULL;
358     }
359    
360     if(VECTOR_RESIZE(network_stats, if_table->dwNumEntries) < 0) {
361     free(if_table);
362     return NULL;
363     }
364    
365     for (i=0; i<if_table->dwNumEntries; i++) {
366     network_stat_ptr=network_stats+i;
367     if_row = if_table->table[i];
368    
369     if(sg_update_string(&network_stat_ptr->interface_name,
370     if_row.bDescr) < 0) {
371     free(if_table);
372     return NULL;
373     }
374     network_stat_ptr->tx = if_row.dwOutOctets;
375     network_stat_ptr->rx = if_row.dwInOctets;
376     network_stat_ptr->ipackets = if_row.dwInUcastPkts + if_row.dwInNUcastPkts;
377     network_stat_ptr->opackets = if_row.dwOutUcastPkts + if_row.dwOutNUcastPkts;
378     network_stat_ptr->ierrors = if_row.dwInErrors;
379     network_stat_ptr->oerrors = if_row.dwOutErrors;
380     network_stat_ptr->collisions = 0; /* can't do that */
381     network_stat_ptr->systime = time(NULL);
382    
383     interfaces++;
384     }
385     free(if_table);
386    
387     /* Please say there's a nicer way to do this... If windows has two (or
388     * more) identical network cards, GetIfTable returns them with the same
389     * name, not like in Device Manager where the other has a #2 etc after
390     * it. So, add the #number here. Should we be doing this? Or should the
391     * end programs be dealing with duplicate names? Currently breaks
392     * watch.pl in rrdgraphing. But Unix does not have the issue of
393     * duplicate net device names.
394     */
395     for (i=0; i<interfaces; i++) {
396     no = 2;
397     for(j=i+1; j<interfaces; j++) {
398     network_stat_ptr=network_stats+j;
399     if(strcmp(network_stats[i].interface_name,
400     network_stat_ptr->interface_name) == 0) {
401     if(snprintf(buf, sizeof(buf), " #%d", no) < 0) {
402     break;
403     }
404     if(sg_concat_string(&network_stat_ptr->interface_name, buf) != 0) {
405     return NULL;
406     }
407    
408     no++;
409     }
410     }
411     }
412     #endif
413    
414 pajs 1.1 *entries=interfaces;
415    
416     return network_stats;
417 pajs 1.3 }
418    
419 ats 1.62 static long long transfer_diff(long long new, long long old){
420 tdb 1.76 #if defined(SOL7) || defined(LINUX) || defined(FREEBSD) || defined(DFBSD) || defined(OPENBSD) || defined(WIN32)
421 ats 1.56 /* 32-bit quantities, so we must explicitly deal with wraparound. */
422     #define MAXVAL 0x100000000LL
423     if (new >= old) {
424     return new - old;
425     } else {
426     return MAXVAL + new - old;
427     }
428 pajs 1.8 #else
429 ats 1.56 /* 64-bit quantities, so plain subtraction works. */
430     return new - old;
431 pajs 1.8 #endif
432     }
433    
434 ats 1.62 sg_network_io_stats *sg_get_network_io_stats_diff(int *entries) {
435     VECTOR_DECLARE_STATIC(diff, sg_network_io_stats, 1,
436 tdb 1.65 network_stat_init, network_stat_destroy);
437 ats 1.62 sg_network_io_stats *src = NULL, *dest;
438 ats 1.59 int i, j, diff_count, new_count;
439 ats 1.24
440     if (network_stats == NULL) {
441     /* No previous stats, so we can't calculate a difference. */
442 ats 1.62 return sg_get_network_io_stats(entries);
443 pajs 1.3 }
444    
445 ats 1.24 /* Resize the results array to match the previous stats. */
446 ats 1.59 diff_count = VECTOR_SIZE(network_stats);
447     if (VECTOR_RESIZE(diff, diff_count) < 0) {
448 pajs 1.3 return NULL;
449     }
450    
451 ats 1.24 /* Copy the previous stats into the result. */
452     for (i = 0; i < diff_count; i++) {
453     src = &network_stats[i];
454     dest = &diff[i];
455 pajs 1.3
456 ats 1.62 if (sg_update_string(&dest->interface_name,
457 tdb 1.65 src->interface_name) < 0) {
458 ats 1.60 return NULL;
459 pajs 1.3 }
460 ats 1.24 dest->rx = src->rx;
461     dest->tx = src->tx;
462 tdb 1.47 dest->ipackets = src->ipackets;
463     dest->opackets = src->opackets;
464     dest->ierrors = src->ierrors;
465     dest->oerrors = src->oerrors;
466     dest->collisions = src->collisions;
467 ats 1.24 dest->systime = src->systime;
468     }
469 pajs 1.3
470 ats 1.24 /* Get a new set of stats. */
471 ats 1.62 if (sg_get_network_io_stats(&new_count) == NULL) {
472 ats 1.21 return NULL;
473     }
474 pajs 1.3
475 ats 1.24 /* For each previous stat... */
476     for (i = 0; i < diff_count; i++) {
477     dest = &diff[i];
478    
479     /* ... find the corresponding new stat ... */
480     for (j = 0; j < new_count; j++) {
481     /* Try the new stat in the same position first,
482     since that's most likely to be it. */
483     src = &network_stats[(i + j) % new_count];
484     if (strcmp(src->interface_name, dest->interface_name) == 0) {
485     break;
486     }
487     }
488     if (j == new_count) {
489     /* No match found. */
490     continue;
491 pajs 1.3 }
492    
493 ats 1.24 /* ... and subtract the previous stat from it to get the
494     difference. */
495     dest->rx = transfer_diff(src->rx, dest->rx);
496     dest->tx = transfer_diff(src->tx, dest->tx);
497 tdb 1.47 dest->ipackets = transfer_diff(src->ipackets, dest->ipackets);
498     dest->opackets = transfer_diff(src->opackets, dest->opackets);
499     dest->ierrors = transfer_diff(src->ierrors, dest->ierrors);
500     dest->oerrors = transfer_diff(src->oerrors, dest->oerrors);
501     dest->collisions = transfer_diff(src->collisions, dest->collisions);
502 ats 1.24 dest->systime = src->systime - dest->systime;
503 pajs 1.3 }
504    
505 ats 1.24 *entries = diff_count;
506     return diff;
507     }
508 ats 1.59
509 ats 1.68 int sg_network_io_compare_name(const void *va, const void *vb) {
510     const sg_network_io_stats *a = (const sg_network_io_stats *)va;
511     const sg_network_io_stats *b = (const sg_network_io_stats *)vb;
512    
513     return strcmp(a->interface_name, b->interface_name);
514     }
515    
516 pajs 1.25 /* NETWORK INTERFACE STATS */
517    
518 ats 1.62 static void network_iface_stat_init(sg_network_iface_stats *s) {
519 ats 1.59 s->interface_name = NULL;
520     s->speed = 0;
521 tdb 1.74 s->duplex = SG_IFACE_DUPLEX_UNKNOWN;
522 pajs 1.25 }
523    
524 ats 1.62 static void network_iface_stat_destroy(sg_network_iface_stats *s) {
525 ats 1.59 free(s->interface_name);
526 pajs 1.25 }
527    
528 ats 1.62 sg_network_iface_stats *sg_get_network_iface_stats(int *entries){
529     VECTOR_DECLARE_STATIC(network_iface_stats, sg_network_iface_stats, 5,
530 tdb 1.65 network_iface_stat_init, network_iface_stat_destroy);
531 ats 1.62 sg_network_iface_stats *network_iface_stat_ptr;
532 ats 1.43 int ifaces = 0;
533 pajs 1.25
534     #ifdef SOLARIS
535 tdb 1.65 kstat_ctl_t *kc;
536     kstat_t *ksp;
537 pajs 1.25 kstat_named_t *knp;
538 ats 1.44 int sock;
539 pajs 1.25 #endif
540 pajs 1.26 #ifdef ALLBSD
541 tdb 1.65 struct ifaddrs *net, *net_ptr;
542 pajs 1.26 struct ifmediareq ifmed;
543 pajs 1.36 struct ifreq ifr;
544 ats 1.37 int sock;
545 pajs 1.26 int x;
546     #endif
547 pajs 1.27 #ifdef LINUX
548 tdb 1.65 FILE *f;
549     /* Horrible big enough, but it should be easily big enough */
550     char line[8096];
551 pajs 1.27 int sock;
552     #endif
553 tdb 1.76 #ifdef WIN32
554     PMIB_IFTABLE if_table;
555     MIB_IFROW if_row;
556     int i,j,no;
557     char buf[5];
558     #endif
559 ats 1.43
560 pajs 1.26 #ifdef ALLBSD
561 tdb 1.65 if(getifaddrs(&net) != 0){
562 ats 1.69 sg_set_error_with_errno(SG_ERROR_GETIFADDRS, NULL);
563 tdb 1.65 return NULL;
564     }
565 pajs 1.26
566 ats 1.37 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == 0) return NULL;
567 pajs 1.26
568     for(net_ptr=net; net_ptr!=NULL; net_ptr=net_ptr->ifa_next){
569 tdb 1.65 if(net_ptr->ifa_addr->sa_family != AF_LINK) continue;
570 ats 1.59
571     if (VECTOR_RESIZE(network_iface_stats, ifaces + 1) < 0) {
572 tdb 1.65 return NULL;
573     }
574     network_iface_stat_ptr = network_iface_stats + ifaces;
575 pajs 1.26
576 ats 1.40 memset(&ifr, 0, sizeof(ifr));
577     strncpy(ifr.ifr_name, net_ptr->ifa_name, sizeof(ifr.ifr_name));
578    
579     if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0){
580     continue;
581     }
582     if((ifr.ifr_flags & IFF_UP) != 0){
583     network_iface_stat_ptr->up = 1;
584     }else{
585     network_iface_stat_ptr->up = 0;
586     }
587    
588 ats 1.62 if (sg_update_string(&network_iface_stat_ptr->interface_name,
589 tdb 1.65 net_ptr->ifa_name) < 0) {
590 ats 1.60 return NULL;
591     }
592 ats 1.40
593     network_iface_stat_ptr->speed = 0;
594 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_UNKNOWN;
595 ats 1.40 ifaces++;
596    
597 pajs 1.26 memset(&ifmed, 0, sizeof(struct ifmediareq));
598 ats 1.62 sg_strlcpy(ifmed.ifm_name, net_ptr->ifa_name, sizeof(ifmed.ifm_name));
599 ats 1.37 if(ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed) == -1){
600 ats 1.40 /* Not all interfaces support the media ioctls. */
601 pajs 1.26 continue;
602     }
603    
604     /* We may need to change this if we start doing wireless devices too */
605     if( (ifmed.ifm_active | IFM_ETHER) != ifmed.ifm_active ){
606     /* Not a ETHER device */
607     continue;
608     }
609    
610     /* Only intrested in the first 4 bits) - Assuming only ETHER devices */
611     x = ifmed.ifm_active & 0x0f;
612     switch(x){
613     /* 10 Mbit connections. Speedy :) */
614     case(IFM_10_T):
615     case(IFM_10_2):
616     case(IFM_10_5):
617     case(IFM_10_STP):
618     case(IFM_10_FL):
619     network_iface_stat_ptr->speed = 10;
620     break;
621     /* 100 Mbit conneections */
622     case(IFM_100_TX):
623     case(IFM_100_FX):
624     case(IFM_100_T4):
625     case(IFM_100_VG):
626     case(IFM_100_T2):
627     network_iface_stat_ptr->speed = 100;
628     break;
629     /* 1000 Mbit connections */
630     case(IFM_1000_SX):
631     case(IFM_1000_LX):
632     case(IFM_1000_CX):
633 tdb 1.54 #if defined(IFM_1000_TX) && !defined(OPENBSD)
634     case(IFM_1000_TX): /* FreeBSD 4 and others (but NOT OpenBSD)? */
635 tdb 1.45 #endif
636     #ifdef IFM_1000_FX
637     case(IFM_1000_FX): /* FreeBSD 4 */
638     #endif
639     #ifdef IFM_1000_T
640     case(IFM_1000_T): /* FreeBSD 5 */
641 tdb 1.28 #endif
642 pajs 1.26 network_iface_stat_ptr->speed = 1000;
643     break;
644     /* We don't know what it is */
645     default:
646     network_iface_stat_ptr->speed = 0;
647     break;
648     }
649    
650     if( (ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active ){
651 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_FULL;
652 pajs 1.26 }else if( (ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active ){
653 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_HALF;
654 pajs 1.26 }else{
655 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_UNKNOWN;
656 pajs 1.26 }
657 pajs 1.36
658 pajs 1.26 }
659     freeifaddrs(net);
660 ats 1.37 close(sock);
661 pajs 1.26 #endif
662 pajs 1.25
663     #ifdef SOLARIS
664 ats 1.44 if ((kc = kstat_open()) == NULL) {
665 tdb 1.66 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
666 ats 1.44 return NULL;
667     }
668    
669     if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
670 ats 1.69 sg_set_error_with_errno(SG_ERROR_SOCKET, NULL);
671 tdb 1.78 kstat_close(kc);
672 ats 1.44 return NULL;
673     }
674 pajs 1.25
675 ats 1.44 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
676 tdb 1.76 if (strcmp(ksp->ks_class, "net") == 0) {
677 ats 1.44 struct ifreq ifr;
678    
679     kstat_read(kc, ksp, NULL);
680    
681     strncpy(ifr.ifr_name, ksp->ks_name, sizeof ifr.ifr_name);
682     if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
683     /* Not a network interface. */
684 pajs 1.25 continue;
685     }
686 ats 1.44
687 ats 1.59 if (VECTOR_RESIZE(network_iface_stats, ifaces + 1) < 0) {
688 tdb 1.78 kstat_close(kc);
689 pajs 1.25 return NULL;
690     }
691 pajs 1.26 network_iface_stat_ptr = network_iface_stats + ifaces;
692 ats 1.44 ifaces++;
693    
694 ats 1.62 if (sg_update_string(&network_iface_stat_ptr->interface_name,
695 tdb 1.65 ksp->ks_name) < 0) {
696 tdb 1.78 kstat_close(kc);
697 ats 1.60 return NULL;
698     }
699 pajs 1.25
700 ats 1.44 if ((ifr.ifr_flags & IFF_UP) != 0) {
701     network_iface_stat_ptr->up = 1;
702     } else {
703 tdb 1.77 network_iface_stat_ptr->up = 0;
704 ats 1.44 }
705 pajs 1.35
706 ats 1.44 if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL) {
707     network_iface_stat_ptr->speed = knp->value.ui64 / (1000 * 1000);
708     } else {
709     network_iface_stat_ptr->speed = 0;
710 pajs 1.25 }
711    
712 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_UNKNOWN;
713 ats 1.44 if ((knp = kstat_data_lookup(ksp, "link_duplex")) != NULL) {
714     switch (knp->value.ui32) {
715     case 1:
716 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_HALF;
717 ats 1.44 break;
718     case 2:
719 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_FULL;
720 ats 1.44 break;
721     }
722 pajs 1.33 }
723 pajs 1.25 }
724     }
725 ats 1.44
726     close(sock);
727 pajs 1.25 kstat_close(kc);
728 tdb 1.76 #endif
729 pajs 1.27 #ifdef LINUX
730     f = fopen("/proc/net/dev", "r");
731 tdb 1.65 if(f == NULL){
732 ats 1.69 sg_set_error_with_errno(SG_ERROR_OPEN, "/proc/net/dev");
733 tdb 1.65 return NULL;
734     }
735 pajs 1.27
736     /* Setup stuff so we can do the ioctl to get the info */
737     if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
738 ats 1.69 sg_set_error_with_errno(SG_ERROR_SOCKET, NULL);
739 pajs 1.27 return NULL;
740     }
741    
742     /* Ignore first 2 lines.. Just headings */
743 tdb 1.66 if((fgets(line, sizeof(line), f)) == NULL) {
744     sg_set_error(SG_ERROR_PARSE, NULL);
745     return NULL;
746     }
747     if((fgets(line, sizeof(line), f)) == NULL) {
748     sg_set_error(SG_ERROR_PARSE, NULL);
749     return NULL;
750     }
751 pajs 1.27
752 tdb 1.65 while((fgets(line, sizeof(line), f)) != NULL){
753     char *name, *ptr;
754     struct ifreq ifr;
755     struct ethtool_cmd ethcmd;
756     int err;
757 pajs 1.27
758     /* Get the interface name */
759 tdb 1.65 ptr = strchr(line, ':');
760     if (ptr == NULL) continue;
761     *ptr='\0';
762     name = line;
763     while(isspace(*(name))){
764     name++;
765     }
766 pajs 1.27
767 tdb 1.65 memset(&ifr, 0, sizeof ifr);
768     strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
769 pajs 1.27
770 ats 1.38 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
771     continue;
772     }
773 pajs 1.27
774     /* We have a good interface to add */
775 ats 1.59 if (VECTOR_RESIZE(network_iface_stats, ifaces + 1) < 0) {
776 pajs 1.27 return NULL;
777     }
778     network_iface_stat_ptr = network_iface_stats + ifaces;
779 ats 1.60
780 ats 1.62 if (sg_update_string(&network_iface_stat_ptr->interface_name,
781 tdb 1.65 name) < 0) {
782 ats 1.60 return NULL;
783     }
784 ats 1.38 if ((ifr.ifr_flags & IFF_UP) != 0) {
785 pajs 1.35 network_iface_stat_ptr->up = 1;
786 ats 1.38 } else {
787 pajs 1.35 network_iface_stat_ptr->up = 0;
788     }
789    
790 tdb 1.65 memset(&ethcmd, 0, sizeof ethcmd);
791     ethcmd.cmd = ETHTOOL_GSET;
792     ifr.ifr_data = (caddr_t) &ethcmd;
793 ats 1.38
794 tdb 1.65 err = ioctl(sock, SIOCETHTOOL, &ifr);
795     if (err == 0) {
796 ats 1.38 network_iface_stat_ptr->speed = ethcmd.speed;
797    
798     switch (ethcmd.duplex) {
799 tdb 1.72 case DUPLEX_FULL:
800 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_FULL;
801 ats 1.38 break;
802 tdb 1.72 case DUPLEX_HALF:
803 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_HALF;
804 ats 1.38 break;
805     default:
806 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_UNKNOWN;
807 ats 1.38 }
808     } else {
809     /* Not all interfaces support the ethtool ioctl. */
810 ats 1.39 network_iface_stat_ptr->speed = 0;
811 tdb 1.74 network_iface_stat_ptr->duplex = SG_IFACE_DUPLEX_UNKNOWN;
812 pajs 1.27 }
813 ats 1.38
814 pajs 1.27 ifaces++;
815     }
816 pajs 1.30 close(sock);
817 ats 1.38 fclose(f);
818 pajs 1.27 #endif
819 ats 1.71 #ifdef CYGWIN
820     sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
821     return NULL;
822     #endif
823     #ifdef HPUX
824     sg_set_error(SG_ERROR_UNSUPPORTED, "HP-UX");
825     return NULL;
826 tdb 1.76 #endif
827     #ifdef WIN32
828     ifaces = 0;
829    
830     if((if_table = win32_get_devices()) == NULL) {
831     sg_set_error(SG_ERROR_DEVICES, "network interfaces");
832     return NULL;
833     }
834    
835     if(VECTOR_RESIZE(network_iface_stats, if_table->dwNumEntries) < 0) {
836     free(if_table);
837     return NULL;
838     }
839    
840     for(i=0; i<if_table->dwNumEntries; i++) {
841     network_iface_stat_ptr=network_iface_stats+i;
842     if_row = if_table->table[i];
843    
844     if(sg_update_string(&network_iface_stat_ptr->interface_name,
845     if_row.bDescr) < 0) {
846     free(if_table);
847     return NULL;
848     }
849     network_iface_stat_ptr->speed = if_row.dwSpeed /1000000;
850    
851     if((if_row.dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED ||
852     if_row.dwOperStatus ==
853     MIB_IF_OPER_STATUS_OPERATIONAL) &&
854     if_row.dwAdminStatus == 1) {
855     network_iface_stat_ptr->up = 1;
856     } else {
857     network_iface_stat_ptr->up = 0;
858     }
859    
860     ifaces++;
861     }
862     free(if_table);
863    
864     /* again with the renumbering */
865     for (i=0; i<ifaces; i++) {
866     no = 2;
867     for(j=i+1; j<ifaces; j++) {
868     network_iface_stat_ptr=network_iface_stats+j;
869     if(strcmp(network_iface_stats[i].interface_name,
870     network_iface_stat_ptr->interface_name) == 0) {
871     if(snprintf(buf, sizeof(buf), " #%d", no) < 0) {
872     break;
873     }
874     if(sg_concat_string(&network_iface_stat_ptr->interface_name, buf) != 0) {
875     return NULL;
876     }
877    
878     no++;
879     }
880     }
881     }
882 tdb 1.74 #endif
883    
884     #ifdef SG_ENABLE_DEPRECATED
885     network_iface_stat_ptr->dup = network_iface_stat_ptr->duplex;
886 ats 1.71 #endif
887    
888 pajs 1.26 *entries = ifaces;
889 pajs 1.25 return network_iface_stats;
890 ats 1.68 }
891    
892     int sg_network_iface_compare_name(const void *va, const void *vb) {
893     const sg_network_iface_stats *a = (const sg_network_iface_stats *)va;
894     const sg_network_iface_stats *b = (const sg_network_iface_stats *)vb;
895    
896     return strcmp(a->interface_name, b->interface_name);
897 pajs 1.25 }
898