ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/network_stats.c
Revision: 1.66
Committed: Wed Apr 7 21:08:40 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.65: +20 -4 lines
Log Message:
The rest of the error handling stuff (except the vector code).

I've been extremely unimaginative with the string names in error.c, but
they're all in one place so much easier to tidy up. I'm also beginning to
wonder if we actually needed an SG_ERROR_SYSTEM_CALL to indicate some call
into the system failed - because the majority of our errors are those :-)

Still to do, then:
 - vector code
 - better string names in error.c
 - deal with arg string in some way
 - make use of the error status in statgrab/saidar/examples

File Contents

# Content
1 /*
2 * i-scream libstatgrab
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.65 2004/04/07 14:53:40 tdb 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 "vector.h"
32 #include "tools.h"
33 #include <time.h>
34 #ifdef SOLARIS
35 #include <kstat.h>
36 #include <sys/sysinfo.h>
37 #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 #include <unistd.h>
44 #endif
45 #ifdef LINUX
46 #include <stdio.h>
47 #include <sys/types.h>
48 #include <regex.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51 #include <net/if.h>
52 #include <ctype.h>
53 /* Stuff which could be defined by defining KERNEL, but
54 * that would be a bad idea, so we'll just declare it here
55 */
56 typedef __uint8_t u8;
57 typedef __uint16_t u16;
58 typedef __uint32_t u32;
59 typedef __uint64_t u64;
60 #include <linux/ethtool.h>
61 #include <linux/sockios.h>
62 #include <unistd.h>
63 #endif
64 #ifdef ALLBSD
65 #include <sys/types.h>
66 #include <sys/socket.h>
67 #include <ifaddrs.h>
68 #include <net/if.h>
69 #include <net/if_media.h>
70 #include <sys/ioctl.h>
71 #include <unistd.h>
72 #endif
73
74 static void network_stat_init(sg_network_io_stats *s) {
75 s->interface_name = NULL;
76 s->tx = 0;
77 s->rx = 0;
78 s->ipackets = 0;
79 s->opackets = 0;
80 s->ierrors = 0;
81 s->oerrors = 0;
82 s->collisions = 0;
83 }
84
85 static void network_stat_destroy(sg_network_io_stats *s) {
86 free(s->interface_name);
87 }
88
89 VECTOR_DECLARE_STATIC(network_stats, sg_network_io_stats, 5,
90 network_stat_init, network_stat_destroy);
91
92 sg_network_io_stats *sg_get_network_io_stats(int *entries){
93 int interfaces;
94 sg_network_io_stats *network_stat_ptr;
95
96 #ifdef SOLARIS
97 kstat_ctl_t *kc;
98 kstat_t *ksp;
99 kstat_named_t *knp;
100 #endif
101
102 #ifdef LINUX
103 FILE *f;
104 /* Horrible big enough, but it should be easily big enough */
105 char line[8096];
106 regex_t regex;
107 regmatch_t line_match[9];
108 #endif
109 #ifdef ALLBSD
110 struct ifaddrs *net, *net_ptr;
111 struct if_data *net_data;
112 #endif
113
114 #ifdef ALLBSD
115 if(getifaddrs(&net) != 0){
116 sg_set_error(SG_ERROR_GETIFADDRS, NULL);
117 return NULL;
118 }
119
120 interfaces=0;
121
122 for(net_ptr=net;net_ptr!=NULL;net_ptr=net_ptr->ifa_next){
123 if(net_ptr->ifa_addr->sa_family != AF_LINK) continue;
124
125 if (VECTOR_RESIZE(network_stats, interfaces + 1) < 0) {
126 return NULL;
127 }
128 network_stat_ptr=network_stats+interfaces;
129
130 if (sg_update_string(&network_stat_ptr->interface_name,
131 net_ptr->ifa_name) < 0) {
132 return NULL;
133 }
134 net_data=(struct if_data *)net_ptr->ifa_data;
135 network_stat_ptr->rx=net_data->ifi_ibytes;
136 network_stat_ptr->tx=net_data->ifi_obytes;
137 network_stat_ptr->ipackets=net_data->ifi_ipackets;
138 network_stat_ptr->opackets=net_data->ifi_opackets;
139 network_stat_ptr->ierrors=net_data->ifi_ierrors;
140 network_stat_ptr->oerrors=net_data->ifi_oerrors;
141 network_stat_ptr->collisions=net_data->ifi_collisions;
142 network_stat_ptr->systime=time(NULL);
143 interfaces++;
144 }
145 freeifaddrs(net);
146 #endif
147
148 #ifdef SOLARIS
149 if ((kc = kstat_open()) == NULL) {
150 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
151 return NULL;
152 }
153
154 interfaces=0;
155
156 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
157 if (!strcmp(ksp->ks_class, "net")) {
158 kstat_read(kc, ksp, NULL);
159
160 #ifdef SOL7
161 #define LRX "rbytes"
162 #define LTX "obytes"
163 #define LIPACKETS "ipackets"
164 #define LOPACKETS "opackets"
165 #define VALTYPE value.ui32
166 #else
167 #define LRX "rbytes64"
168 #define LTX "obytes64"
169 #define LIPACKETS "ipackets64"
170 #define LOPACKETS "opackets64"
171 #define VALTYPE value.ui64
172 #endif
173
174 /* Read rx */
175 if((knp=kstat_data_lookup(ksp, LRX))==NULL){
176 /* This is a network interface, but it doesn't
177 * have the rbytes/obytes values; for instance,
178 * the loopback devices have this behaviour
179 * (although they do track packets in/out). */
180 /* FIXME: Show packet counts when byte counts
181 * not available. */
182 continue;
183 }
184
185 /* Create new network_stats */
186 if (VECTOR_RESIZE(network_stats, interfaces + 1) < 0) {
187 return NULL;
188 }
189 network_stat_ptr=network_stats+interfaces;
190
191 /* Finish reading rx */
192 network_stat_ptr->rx=knp->VALTYPE;
193
194 /* Read tx */
195 if((knp=kstat_data_lookup(ksp, LTX))==NULL){
196 continue;
197 }
198 network_stat_ptr->tx=knp->VALTYPE;
199
200 /* Read ipackets */
201 if((knp=kstat_data_lookup(ksp, LIPACKETS))==NULL){
202 continue;
203 }
204 network_stat_ptr->ipackets=knp->VALTYPE;
205
206 /* Read opackets */
207 if((knp=kstat_data_lookup(ksp, LOPACKETS))==NULL){
208 continue;
209 }
210 network_stat_ptr->opackets=knp->VALTYPE;
211
212 /* Read ierrors */
213 if((knp=kstat_data_lookup(ksp, "ierrors"))==NULL){
214 continue;
215 }
216 network_stat_ptr->ierrors=knp->value.ui32;
217
218 /* Read oerrors */
219 if((knp=kstat_data_lookup(ksp, "oerrors"))==NULL){
220 continue;
221 }
222 network_stat_ptr->oerrors=knp->value.ui32;
223
224 /* Read collisions */
225 if((knp=kstat_data_lookup(ksp, "collisions"))==NULL){
226 continue;
227 }
228 network_stat_ptr->collisions=knp->value.ui32;
229
230 /* Read interface name */
231 if (sg_update_string(&network_stat_ptr->interface_name,
232 ksp->ks_name) < 0) {
233 return NULL;
234 }
235
236 /* Store systime */
237 network_stat_ptr->systime=time(NULL);
238
239 interfaces++;
240 }
241 }
242
243 kstat_close(kc);
244 #endif
245 #ifdef LINUX
246 f=fopen("/proc/net/dev", "r");
247 if(f==NULL){
248 sg_set_error(SG_ERROR_OPEN, "/proc/net/dev");
249 return NULL;
250 }
251 /* read the 2 lines.. Its the title, so we dont care :) */
252 fgets(line, sizeof(line), f);
253 fgets(line, sizeof(line), f);
254
255
256 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){
257 sg_set_error(SG_ERROR_PARSE, NULL);
258 return NULL;
259 }
260
261 interfaces=0;
262
263 while((fgets(line, sizeof(line), f)) != NULL){
264 if((regexec(&regex, line, 9, line_match, 0))!=0){
265 continue;
266 }
267
268 if (VECTOR_RESIZE(network_stats, interfaces + 1) < 0) {
269 return NULL;
270 }
271 network_stat_ptr=network_stats+interfaces;
272
273 if(network_stat_ptr->interface_name!=NULL){
274 free(network_stat_ptr->interface_name);
275 }
276
277 network_stat_ptr->interface_name=sg_get_string_match(line, &line_match[1]);
278 network_stat_ptr->rx=sg_get_ll_match(line, &line_match[2]);
279 network_stat_ptr->tx=sg_get_ll_match(line, &line_match[5]);
280 network_stat_ptr->ipackets=sg_get_ll_match(line, &line_match[3]);
281 network_stat_ptr->opackets=sg_get_ll_match(line, &line_match[6]);
282 network_stat_ptr->ierrors=sg_get_ll_match(line, &line_match[4]);
283 network_stat_ptr->oerrors=sg_get_ll_match(line, &line_match[7]);
284 network_stat_ptr->collisions=sg_get_ll_match(line, &line_match[8]);
285 network_stat_ptr->systime=time(NULL);
286
287 interfaces++;
288 }
289 fclose(f);
290 regfree(&regex);
291
292 #endif
293
294 #ifdef CYGWIN
295 sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin");
296 return NULL;
297 #endif
298
299 *entries=interfaces;
300
301 return network_stats;
302 }
303
304 static long long transfer_diff(long long new, long long old){
305 #if defined(SOL7) || defined(LINUX) || defined(FREEBSD) || defined(DFBSD) || defined(OPENBSD)
306 /* 32-bit quantities, so we must explicitly deal with wraparound. */
307 #define MAXVAL 0x100000000LL
308 if (new >= old) {
309 return new - old;
310 } else {
311 return MAXVAL + new - old;
312 }
313 #else
314 /* 64-bit quantities, so plain subtraction works. */
315 return new - old;
316 #endif
317 }
318
319 sg_network_io_stats *sg_get_network_io_stats_diff(int *entries) {
320 VECTOR_DECLARE_STATIC(diff, sg_network_io_stats, 1,
321 network_stat_init, network_stat_destroy);
322 sg_network_io_stats *src = NULL, *dest;
323 int i, j, diff_count, new_count;
324
325 if (network_stats == NULL) {
326 /* No previous stats, so we can't calculate a difference. */
327 return sg_get_network_io_stats(entries);
328 }
329
330 /* Resize the results array to match the previous stats. */
331 diff_count = VECTOR_SIZE(network_stats);
332 if (VECTOR_RESIZE(diff, diff_count) < 0) {
333 return NULL;
334 }
335
336 /* Copy the previous stats into the result. */
337 for (i = 0; i < diff_count; i++) {
338 src = &network_stats[i];
339 dest = &diff[i];
340
341 if (sg_update_string(&dest->interface_name,
342 src->interface_name) < 0) {
343 return NULL;
344 }
345 dest->rx = src->rx;
346 dest->tx = src->tx;
347 dest->ipackets = src->ipackets;
348 dest->opackets = src->opackets;
349 dest->ierrors = src->ierrors;
350 dest->oerrors = src->oerrors;
351 dest->collisions = src->collisions;
352 dest->systime = src->systime;
353 }
354
355 /* Get a new set of stats. */
356 if (sg_get_network_io_stats(&new_count) == NULL) {
357 return NULL;
358 }
359
360 /* For each previous stat... */
361 for (i = 0; i < diff_count; i++) {
362 dest = &diff[i];
363
364 /* ... find the corresponding new stat ... */
365 for (j = 0; j < new_count; j++) {
366 /* Try the new stat in the same position first,
367 since that's most likely to be it. */
368 src = &network_stats[(i + j) % new_count];
369 if (strcmp(src->interface_name, dest->interface_name) == 0) {
370 break;
371 }
372 }
373 if (j == new_count) {
374 /* No match found. */
375 continue;
376 }
377
378 /* ... and subtract the previous stat from it to get the
379 difference. */
380 dest->rx = transfer_diff(src->rx, dest->rx);
381 dest->tx = transfer_diff(src->tx, dest->tx);
382 dest->ipackets = transfer_diff(src->ipackets, dest->ipackets);
383 dest->opackets = transfer_diff(src->opackets, dest->opackets);
384 dest->ierrors = transfer_diff(src->ierrors, dest->ierrors);
385 dest->oerrors = transfer_diff(src->oerrors, dest->oerrors);
386 dest->collisions = transfer_diff(src->collisions, dest->collisions);
387 dest->systime = src->systime - dest->systime;
388 }
389
390 *entries = diff_count;
391 return diff;
392 }
393
394 /* NETWORK INTERFACE STATS */
395
396 static void network_iface_stat_init(sg_network_iface_stats *s) {
397 s->interface_name = NULL;
398 s->speed = 0;
399 s->dup = SG_IFACE_DUPLEX_UNKNOWN;
400 }
401
402 static void network_iface_stat_destroy(sg_network_iface_stats *s) {
403 free(s->interface_name);
404 }
405
406 sg_network_iface_stats *sg_get_network_iface_stats(int *entries){
407 VECTOR_DECLARE_STATIC(network_iface_stats, sg_network_iface_stats, 5,
408 network_iface_stat_init, network_iface_stat_destroy);
409 sg_network_iface_stats *network_iface_stat_ptr;
410 int ifaces = 0;
411
412 #ifdef SOLARIS
413 kstat_ctl_t *kc;
414 kstat_t *ksp;
415 kstat_named_t *knp;
416 int sock;
417 #endif
418 #ifdef ALLBSD
419 struct ifaddrs *net, *net_ptr;
420 struct ifmediareq ifmed;
421 struct ifreq ifr;
422 int sock;
423 int x;
424 #endif
425 #ifdef LINUX
426 FILE *f;
427 /* Horrible big enough, but it should be easily big enough */
428 char line[8096];
429 int sock;
430 #endif
431
432 #ifdef ALLBSD
433 if(getifaddrs(&net) != 0){
434 sg_set_error(SG_ERROR_GETIFADDRS, NULL);
435 return NULL;
436 }
437
438 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == 0) return NULL;
439
440 for(net_ptr=net; net_ptr!=NULL; net_ptr=net_ptr->ifa_next){
441 if(net_ptr->ifa_addr->sa_family != AF_LINK) continue;
442
443 if (VECTOR_RESIZE(network_iface_stats, ifaces + 1) < 0) {
444 return NULL;
445 }
446 network_iface_stat_ptr = network_iface_stats + ifaces;
447
448 memset(&ifr, 0, sizeof(ifr));
449 strncpy(ifr.ifr_name, net_ptr->ifa_name, sizeof(ifr.ifr_name));
450
451 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0){
452 continue;
453 }
454 if((ifr.ifr_flags & IFF_UP) != 0){
455 network_iface_stat_ptr->up = 1;
456 }else{
457 network_iface_stat_ptr->up = 0;
458 }
459
460 if (sg_update_string(&network_iface_stat_ptr->interface_name,
461 net_ptr->ifa_name) < 0) {
462 return NULL;
463 }
464
465 network_iface_stat_ptr->speed = 0;
466 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_UNKNOWN;
467 ifaces++;
468
469 memset(&ifmed, 0, sizeof(struct ifmediareq));
470 sg_strlcpy(ifmed.ifm_name, net_ptr->ifa_name, sizeof(ifmed.ifm_name));
471 if(ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed) == -1){
472 /* Not all interfaces support the media ioctls. */
473 continue;
474 }
475
476 /* We may need to change this if we start doing wireless devices too */
477 if( (ifmed.ifm_active | IFM_ETHER) != ifmed.ifm_active ){
478 /* Not a ETHER device */
479 continue;
480 }
481
482 /* Only intrested in the first 4 bits) - Assuming only ETHER devices */
483 x = ifmed.ifm_active & 0x0f;
484 switch(x){
485 /* 10 Mbit connections. Speedy :) */
486 case(IFM_10_T):
487 case(IFM_10_2):
488 case(IFM_10_5):
489 case(IFM_10_STP):
490 case(IFM_10_FL):
491 network_iface_stat_ptr->speed = 10;
492 break;
493 /* 100 Mbit conneections */
494 case(IFM_100_TX):
495 case(IFM_100_FX):
496 case(IFM_100_T4):
497 case(IFM_100_VG):
498 case(IFM_100_T2):
499 network_iface_stat_ptr->speed = 100;
500 break;
501 /* 1000 Mbit connections */
502 case(IFM_1000_SX):
503 case(IFM_1000_LX):
504 case(IFM_1000_CX):
505 #if defined(IFM_1000_TX) && !defined(OPENBSD)
506 case(IFM_1000_TX): /* FreeBSD 4 and others (but NOT OpenBSD)? */
507 #endif
508 #ifdef IFM_1000_FX
509 case(IFM_1000_FX): /* FreeBSD 4 */
510 #endif
511 #ifdef IFM_1000_T
512 case(IFM_1000_T): /* FreeBSD 5 */
513 #endif
514 network_iface_stat_ptr->speed = 1000;
515 break;
516 /* We don't know what it is */
517 default:
518 network_iface_stat_ptr->speed = 0;
519 break;
520 }
521
522 if( (ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active ){
523 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_FULL;
524 }else if( (ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active ){
525 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_HALF;
526 }else{
527 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_UNKNOWN;
528 }
529
530 }
531 freeifaddrs(net);
532 close(sock);
533 #endif
534
535 #ifdef SOLARIS
536 if ((kc = kstat_open()) == NULL) {
537 sg_set_error(SG_ERROR_KSTAT_OPEN, NULL);
538 return NULL;
539 }
540
541 if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
542 sg_set_error(SG_ERROR_SOCKET, NULL);
543 return NULL;
544 }
545
546 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
547 if (!strcmp(ksp->ks_class, "net")) {
548 struct ifreq ifr;
549
550 kstat_read(kc, ksp, NULL);
551
552 strncpy(ifr.ifr_name, ksp->ks_name, sizeof ifr.ifr_name);
553 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
554 /* Not a network interface. */
555 continue;
556 }
557
558 if (VECTOR_RESIZE(network_iface_stats, ifaces + 1) < 0) {
559 return NULL;
560 }
561 network_iface_stat_ptr = network_iface_stats + ifaces;
562 ifaces++;
563
564 if (sg_update_string(&network_iface_stat_ptr->interface_name,
565 ksp->ks_name) < 0) {
566 return NULL;
567 }
568
569 if ((ifr.ifr_flags & IFF_UP) != 0) {
570 network_iface_stat_ptr->up = 1;
571 } else {
572 network_iface_stat_ptr->up = 1;
573 }
574
575 if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL) {
576 network_iface_stat_ptr->speed = knp->value.ui64 / (1000 * 1000);
577 } else {
578 network_iface_stat_ptr->speed = 0;
579 }
580
581 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_UNKNOWN;
582 if ((knp = kstat_data_lookup(ksp, "link_duplex")) != NULL) {
583 switch (knp->value.ui32) {
584 case 1:
585 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_HALF;
586 break;
587 case 2:
588 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_FULL;
589 break;
590 }
591 }
592 }
593 }
594
595 close(sock);
596 kstat_close(kc);
597 #endif
598 #ifdef LINUX
599 f = fopen("/proc/net/dev", "r");
600 if(f == NULL){
601 sg_set_error(SG_ERROR_OPEN, "/proc/net/dev");
602 return NULL;
603 }
604
605 /* Setup stuff so we can do the ioctl to get the info */
606 if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
607 sg_set_error(SG_ERROR_SOCKET, NULL);
608 return NULL;
609 }
610
611 /* Ignore first 2 lines.. Just headings */
612 if((fgets(line, sizeof(line), f)) == NULL) {
613 sg_set_error(SG_ERROR_PARSE, NULL);
614 return NULL;
615 }
616 if((fgets(line, sizeof(line), f)) == NULL) {
617 sg_set_error(SG_ERROR_PARSE, NULL);
618 return NULL;
619 }
620
621 while((fgets(line, sizeof(line), f)) != NULL){
622 char *name, *ptr;
623 struct ifreq ifr;
624 struct ethtool_cmd ethcmd;
625 int err;
626
627 /* Get the interface name */
628 ptr = strchr(line, ':');
629 if (ptr == NULL) continue;
630 *ptr='\0';
631 name = line;
632 while(isspace(*(name))){
633 name++;
634 }
635
636 memset(&ifr, 0, sizeof ifr);
637 strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
638
639 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
640 continue;
641 }
642
643 /* We have a good interface to add */
644 if (VECTOR_RESIZE(network_iface_stats, ifaces + 1) < 0) {
645 return NULL;
646 }
647 network_iface_stat_ptr = network_iface_stats + ifaces;
648
649 if (sg_update_string(&network_iface_stat_ptr->interface_name,
650 name) < 0) {
651 return NULL;
652 }
653 if ((ifr.ifr_flags & IFF_UP) != 0) {
654 network_iface_stat_ptr->up = 1;
655 } else {
656 network_iface_stat_ptr->up = 0;
657 }
658
659 memset(&ethcmd, 0, sizeof ethcmd);
660 ethcmd.cmd = ETHTOOL_GSET;
661 ifr.ifr_data = (caddr_t) &ethcmd;
662
663 err = ioctl(sock, SIOCETHTOOL, &ifr);
664 if (err == 0) {
665 network_iface_stat_ptr->speed = ethcmd.speed;
666
667 switch (ethcmd.duplex) {
668 case 0x00:
669 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_FULL;
670 break;
671 case 0x01:
672 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_HALF;
673 break;
674 default:
675 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_UNKNOWN;
676 }
677 } else {
678 /* Not all interfaces support the ethtool ioctl. */
679 network_iface_stat_ptr->speed = 0;
680 network_iface_stat_ptr->dup = SG_IFACE_DUPLEX_UNKNOWN;
681 }
682
683 ifaces++;
684 }
685 close(sock);
686 fclose(f);
687 #endif
688 *entries = ifaces;
689 return network_iface_stats;
690 }
691