text: /*
   * i-scream libstatgrab
   * http://www.i-scream.org
   * Copyright (C) 2000-2013 i-scream
   * Copyright (C) 2010-2013 Jens Rehsack
   * 
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   * 
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   * 
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   * 02111-1307 USA
   *
   * $Id$
   */
  
  #ifdef _AIX
  #define __NEED_SG_GET_SYS_PAGE_SIZE
  #endif
  #include "tools.h"
  
  static void
  sg_network_io_stats_item_init(sg_network_io_stats *d) {
  	memset( d, 0, sizeof(*d) );
  }
  
  static sg_error
  sg_network_io_stats_item_copy(const sg_network_io_stats *s, sg_network_io_stats *d) {
  
  	if( SG_ERROR_NONE != sg_update_string(&d->interface_name, s->interface_name) ) {
  		RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  	}
  
  	d->tx = s->tx;
  	d->rx = s->rx;
  	d->ipackets = s->ipackets;
  	d->opackets = s->opackets;
  	d->ierrors = s->ierrors;
  	d->oerrors = s->oerrors;
  	d->collisions = s->collisions;
  	d->systime = s->systime;
  
  	return SG_ERROR_NONE;
  }
  
  static unsigned long long
  transfer_diff(unsigned long long new, unsigned long long old){
  #if defined(SOL7) || defined(LINUX) || defined(FREEBSD) || defined(DFBSD) || defined(OPENBSD) || defined(WIN32)
  	/* 32-bit quantities, so we must explicitly deal with wraparound. */
  #define MAXVAL 0x100000000LL
  	long long cmp_new = (long long)new, cmp_old = (long long)old;
  	if (cmp_new >= cmp_old) {
  		return new - old;
  	} else {
  		return (unsigned long long)(MAXVAL + cmp_new - cmp_old);
  	}
  #else
  	/* 64-bit quantities, so plain subtraction works. */
  	return new - old;
  #endif
  }
  
  static sg_error
  sg_network_io_stats_item_compute_diff(const sg_network_io_stats *s, sg_network_io_stats *d) {
  
  	d->tx = transfer_diff( d->tx, s->tx );
  	d->rx = transfer_diff( d->rx, s->rx );
  	d->ipackets = transfer_diff( d->ipackets, s->ipackets );
  	d->opackets = transfer_diff( d->opackets, s->opackets );
  	d->ierrors = transfer_diff( d->ierrors, s->ierrors );
  	d->oerrors = transfer_diff( d->oerrors, s->oerrors );
  	d->collisions = transfer_diff( d->collisions, s->collisions );
  	d->systime -= s->systime;
  
  	return SG_ERROR_NONE;
  }
  
  static int
  sg_network_io_stats_item_compare(const sg_network_io_stats *a, const sg_network_io_stats *b) {
  	return strcmp( a->interface_name, b->interface_name );
  }
  
  static void
  sg_network_io_stats_item_destroy(sg_network_io_stats *d) {
  	free(d->interface_name);
  	d->interface_name = NULL;
  }
  
  static void
  sg_network_iface_stats_item_init(sg_network_iface_stats *d) {
  	d->interface_name = NULL;
  	d->speed = 0;
  	d->factor = 0;
  	d->duplex = SG_IFACE_DUPLEX_UNKNOWN;
  	d->up = SG_IFACE_DOWN;
  	d->systime = 0;
  }
  
  static sg_error
  sg_network_iface_stats_item_copy(sg_network_iface_stats *d, const sg_network_iface_stats *s) {
  
  	if( SG_ERROR_NONE != sg_update_string(&d->interface_name, s->interface_name) ) {
  		RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  	}
  
  	d->speed = s->speed;
  	d->factor = s->factor;
  	d->duplex = s->duplex;
  	d->up = s->up;
  	d->systime = s->systime;
  
  	return SG_ERROR_NONE;
  }
  
  #define sg_network_iface_stats_item_compute_diff NULL
  #define sg_network_iface_stats_item_compare NULL
  
  static void
  sg_network_iface_stats_item_destroy(sg_network_iface_stats *d) {
  	free(d->interface_name);
  }
  
  VECTOR_INIT_INFO_FULL_INIT(sg_network_io_stats);
  VECTOR_INIT_INFO_FULL_INIT(sg_network_iface_stats);
  
  /*
   * setup code
   */
  
  #define SG_NETWORK_IO_NOW_IDX	0
  #define SG_NETWORK_IO_DIFF_IDX	1
  #define SG_NETWORK_IFACE_IDX	2
  #define SG_NETWORK_MAX_IDX	3
  
  EXTENDED_COMP_SETUP(network,SG_NETWORK_MAX_IDX,NULL);
  
  #ifdef LINUX
  static regex_t network_io_rx;
  #define RX_MATCH_COUNT (8+1)
  #endif
  
  static sg_error
  sg_network_init_comp(unsigned id) {
  #ifdef __NEED_SG_GET_SYS_PAGE_SIZE
  	ssize_t pagesize;
  #endif
  	GLOBAL_SET_ID(network,id);
  
  #ifdef LINUX
  	if (regcomp(&network_io_rx, "^[[:space:]]*([^:]+):[[:space:]]*" \
  				    "([0-9]+)[[:space:]]+" \
  				    "([0-9]+)[[:space:]]+" \
  				    "([0-9]+)[[:space:]]+" \
  				    "[0-9]+[[:space:]]+" \
  				    "[0-9]+[[:space:]]+" \
  				    "[0-9]+[[:space:]]+" \
  				    "[0-9]+[[:space:]]+" \
  				    "[0-9]+[[:space:]]+" \
  				    "([0-9]+)[[:space:]]+" \
  				    "([0-9]+)[[:space:]]+" \
  				    "([0-9]+)[[:space:]]+" \
  				    "[0-9]+[[:space:]]+" \
  				    "[0-9]+[[:space:]]+" \
  				    "([0-9]+)", REG_EXTENDED)!=0) {
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_PARSE, "regcomp");
  	}
  #endif
  
  #ifdef __NEED_SG_GET_SYS_PAGE_SIZE
  	if((pagesize = sg_get_sys_page_size()) == -1) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_SYSCONF, "_SC_PAGESIZE");
  	}
  #endif
  
  	return SG_ERROR_NONE;
  }
  
  static void
  sg_network_destroy_comp(void) {
  #ifdef LINUX
  	regfree(&network_io_rx);
  #endif
  }
  
  EASY_COMP_CLEANUP_FN(network,SG_NETWORK_MAX_IDX)
  
  /* real stuff */
  
  #ifdef WIN32
  static PMIB_IFTABLE
  win32_get_devices(void)
  {
  	PMIB_IFTABLE if_table;
  	PMIB_IFTABLE tmp;
  	unsigned long dwSize = 0;
  
  	// Allocate memory for pointers
  	if_table = sg_malloc(sizeof(MIB_IFTABLE));
  	if( if_table == NULL ) {
  		return NULL;
  	}
  
  	// Get necessary size for the buffer
  	if(GetIfTable(if_table, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
  		tmp = sg_realloc(if_table, dwSize);
  		if(tmp == NULL) {
  			free(if_table);
  			return NULL;
  		}
  		if_table = tmp;
  	}
  
  	// Get the data
  	if(GetIfTable(if_table, &dwSize, 0) != NO_ERROR) {
  		free(if_table);
  		return NULL;
  	}
  	return if_table;
  }
  #endif /* WIN32 */
  
  static sg_error
  sg_get_network_io_stats_int(sg_vector **network_io_stats_vector_ptr){
  	ssize_t interfaces = 0;
  	sg_network_io_stats *network_io_ptr;
  
  #ifdef HPUX
  /*
   * talk to Data Link Provider Interface aka /dev/dlpi
   * see: http://docs.hp.com/hpux/onlinedocs/B2355-90139/B2355-90139.html
   */
  #define BUF_SIZE 40960
  
  	u_long *ctrl_area = malloc(BUF_SIZE);
  	u_long *data_area = malloc(BUF_SIZE);
  	u_long *ppa_area = malloc(BUF_SIZE);
  	struct strbuf ctrl_buf = {BUF_SIZE, 0, (char*) ctrl_area};
  	struct strbuf data_buf = {BUF_SIZE, 0, (char*) data_area};
  
  	dl_hp_ppa_info_t *ppa_info;
  	dl_hp_ppa_req_t *ppa_req;
  	dl_hp_ppa_ack_t *ppa_ack;
  	char name_buf[24];
  	int fd = -1, ppa_count, count, flags;
  #elif defined(SOLARIS)
  	kstat_ctl_t *kc;
  	kstat_t *ksp;
  	kstat_named_t *knp;
  #elif defined(LINUX)
  	FILE *f;
  	char line[1024];
  	regmatch_t line_match[RX_MATCH_COUNT];
  #elif defined(ALLBSD)
  	struct ifaddrs *net, *net_ptr;
  	struct if_data *net_data;
  #elif defined(WIN32)
  	PMIB_IFTABLE if_table;
  	MIB_IFROW if_row;
  	int i;
  
  	/* used for duplicate interface names. 5 for space, hash, up to two
  	 * numbers and terminating slash */
  	char buf[5];
  #elif defined(AIX)
  	ssize_t i;
  	perfstat_netinterface_t *statp;
  	perfstat_id_t first;
  #endif
  
  #ifdef HPUX
  	if ((NULL == ctrl_area) || (NULL == data_area) || (NULL == ppa_area)) {
  		free(ctrl_area); free(data_area); free(ppa_area);
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_MALLOC, "sg_get_network_io_stats");
  	}
  
  	if ((fd = open("/dev/dlpi", O_RDWR)) < 0) {
  		free(ctrl_area); free(data_area); free(ppa_area);
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_OPEN, "/dev/dlpi");
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP close(fd); free(ctrl_area); free(data_area); free(ppa_area);
  
  	ppa_req = (dl_hp_ppa_req_t *) ctrl_area;
  	ppa_ack = (dl_hp_ppa_ack_t *) ctrl_area;
  
  	ppa_req->dl_primitive = DL_HP_PPA_REQ;
  
  	ctrl_buf.len = sizeof(dl_hp_ppa_req_t);
  	if(putmsg(fd, &ctrl_buf, 0, 0) < 0) {
  		VECTOR_UPDATE_ERROR_CLEANUP
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_PUTMSG, "DL_HP_PPA_REQ");
  	}
  
  	flags = 0;
  	ctrl_area[0] = 0;
  
  	if (getmsg(fd, &ctrl_buf, &data_buf, &flags) < 0) {
  		VECTOR_UPDATE_ERROR_CLEANUP
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_GETMSG, "DL_HP_PPA_REQ");
  	}
  
  	if (ppa_ack->dl_length == 0) {
  		VECTOR_UPDATE_ERROR_CLEANUP
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_PARSE, "DL_HP_PPA_REQ");
  	}
  
  	// save all the PPA information
  	memcpy ((u_char*) ppa_area, &(((u_char*) ctrl_area)[ppa_ack->dl_offset]), ppa_ack->dl_length);
  	ppa_count = ppa_ack->dl_count;
  
  	for (count = 0, ppa_info = (dl_hp_ppa_info_t*) ppa_area;
  	     count < ppa_count;
  	     ++count) {
  		dl_get_statistics_req_t *get_statistics_req = (dl_get_statistics_req_t*) ctrl_area;
  		dl_get_statistics_ack_t *get_statistics_ack = (dl_get_statistics_ack_t*) ctrl_area;
  
  		dl_attach_req_t *attach_req;
  		dl_detach_req_t *detach_req;
  		mib_ifEntry *mib_ptr;
  #ifdef HPUX11
  		mib_Dot3StatsEntry *mib_Dot3_ptr;
  #endif
  
  		attach_req = (dl_attach_req_t*) ctrl_area;
  		attach_req->dl_primitive = DL_ATTACH_REQ;
  		attach_req->dl_ppa = ppa_info[count].dl_ppa;
  		ctrl_buf.len = sizeof(dl_attach_req_t);
  		if (putmsg(fd, &ctrl_buf, 0, 0) < 0) {
  			unsigned ppa_lan_number = ppa_info[count].dl_ppa;
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_PUTMSG, "DL_ATTACH_REQ ppa %u", ppa_lan_number);
  		}
  
  		ctrl_area[0] = 0;
  		if (getmsg(fd, &ctrl_buf, &data_buf, &flags) < 0) {
  			unsigned ppa_lan_number = ppa_info[count].dl_ppa;
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_GETMSG, "DL_ATTACH_REQ ppa %u", ppa_lan_number);
  		}
  
  		get_statistics_req->dl_primitive = DL_GET_STATISTICS_REQ;
  		ctrl_buf.len = sizeof(dl_get_statistics_req_t);
  
  		if (putmsg(fd, &ctrl_buf, NULL, 0) < 0) {
  			unsigned ppa_lan_number = ppa_info[count].dl_ppa;
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_PUTMSG, "DL_GET_STATISTICS_REQ ppa %u", ppa_lan_number);
  		}
  
  		flags = 0;
  		ctrl_area[0] = 0;
  
  		if (getmsg(fd, &ctrl_buf, NULL, &flags) < 0) {
  			unsigned ppa_lan_number = ppa_info[count].dl_ppa;
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_GETMSG, "DL_GET_STATISTICS_REQ ppa %u", ppa_lan_number);
  		}
  		if (get_statistics_ack->dl_primitive != DL_GET_STATISTICS_ACK) {
  			unsigned ppa_lan_number = ppa_info[count].dl_ppa;
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_PARSE, "DL_GET_STATISTICS_ACK ppa %u", ppa_lan_number);
  		}
  
  		mib_ptr = (mib_ifEntry *) (void *)(((u_char*) ctrl_area) + (size_t)(get_statistics_ack->dl_stat_offset));
  		if (0 == (mib_ptr->ifOper & 1))
  			continue;
  
  		VECTOR_UPDATE(network_io_stats_vector_ptr, interfaces + 1, network_io_ptr, sg_network_io_stats);
  
  		snprintf( name_buf, sizeof(name_buf), "lan%d", ppa_info[count].dl_ppa );
  
  		if ( SG_ERROR_NONE != sg_update_string(&network_io_ptr[interfaces].interface_name, name_buf ) ) {
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  		network_io_ptr[interfaces].rx = mib_ptr->ifInOctets;
  		network_io_ptr[interfaces].tx = mib_ptr->ifOutOctets;
  		network_io_ptr[interfaces].ipackets = mib_ptr->ifInUcastPkts + mib_ptr->ifInNUcastPkts;
  		network_io_ptr[interfaces].opackets = mib_ptr->ifOutUcastPkts + mib_ptr->ifOutNUcastPkts;
  		network_io_ptr[interfaces].ierrors = mib_ptr->ifInErrors;
  		network_io_ptr[interfaces].oerrors = mib_ptr->ifOutErrors;
  #ifdef HPUX11
  		mib_Dot3_ptr = (mib_Dot3StatsEntry *) (((char *) mib_ptr) + sizeof (mib_ifEntry));
  		network_io_ptr[interfaces].collisions = mib_Dot3_ptr->dot3StatsSingleCollisionFrames
  						      + mib_Dot3_ptr->dot3StatsMultipleCollisionFrames;
  #else
  		network_io_ptr[interfaces].collisions = 0; /* currently unknown */
  #endif
  		network_io_ptr[interfaces].systime = time(NULL);
  		++interfaces;
  
  		detach_req = (dl_detach_req_t*) ctrl_area;
  		detach_req->dl_primitive = DL_DETACH_REQ;
  		ctrl_buf.len = sizeof(dl_detach_req_t);
  		if (putmsg(fd, &ctrl_buf, 0, 0) < 0) {
  			unsigned ppa_lan_number = ppa_info[count].dl_ppa;
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_PUTMSG, "DL_DETACH_REQ ppa %u", ppa_lan_number);
  		}
  
  		ctrl_area[0] = 0;
  		flags = 0;
  		if (getmsg(fd, &ctrl_buf, &data_buf, &flags) < 0) {
  			unsigned ppa_lan_number = ppa_info[count].dl_ppa;
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_GETMSG, "DL_DETACH_REQ ppa %u", ppa_lan_number);
  		}
  	}
  
  	close(fd);
  
  	free(ctrl_area);
  	free(data_area);
  	free(ppa_area);
  #elif defined(ALLBSD)
  	if (getifaddrs(&net) != 0) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_GETIFADDRS, NULL);
  	}
  
  	for( net_ptr=net; net_ptr != NULL; net_ptr = net_ptr->ifa_next ) {
  		if( net_ptr->ifa_addr->sa_family != AF_LINK )
  			continue;
  
  		++interfaces;
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP freeifaddrs(net);
  
  	VECTOR_UPDATE(network_io_stats_vector_ptr, interfaces, network_io_ptr, sg_network_io_stats);
  
  	for( interfaces = 0, net_ptr = net; net_ptr != NULL; net_ptr = net_ptr->ifa_next ) {
  		if( net_ptr->ifa_addr->sa_family != AF_LINK )
  			continue;
  
  		if( sg_update_string(&network_io_ptr[interfaces].interface_name,
  				     net_ptr->ifa_name) != SG_ERROR_NONE ) {
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  		net_data = (struct if_data *)net_ptr->ifa_data;
  		network_io_ptr[interfaces].rx = net_data->ifi_ibytes;
  		network_io_ptr[interfaces].tx = net_data->ifi_obytes;
  		network_io_ptr[interfaces].ipackets = net_data->ifi_ipackets;
  		network_io_ptr[interfaces].opackets = net_data->ifi_opackets;
  		network_io_ptr[interfaces].ierrors = net_data->ifi_ierrors;
  		network_io_ptr[interfaces].oerrors = net_data->ifi_oerrors;
  		network_io_ptr[interfaces].collisions = net_data->ifi_collisions;
  		network_io_ptr[interfaces].systime = time(NULL);
  
  		++interfaces;
  	}
  	freeifaddrs(net);
  #elif defined(AIX)
  	/* check how many perfstat_netinterface_t structures are available */
  	interfaces = perfstat_netinterface(NULL, NULL, sizeof(perfstat_netinterface_t), 0);
  
  	/* allocate enough memory for all the structures */
  	statp = calloc(interfaces, sizeof(perfstat_netinterface_t));
  	if( NULL == statp ) {
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_MALLOC, "sg_get_network_io_stats");
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP free(statp);
  
  	/* set name to first interface */
  	strcpy(first.name, FIRST_NETINTERFACE);
  
  	/* ask to get all the structures available in one call */
  	/* return code is number of structures returned */
  	interfaces = perfstat_netinterface(&first, statp, sizeof(perfstat_netinterface_t), interfaces);
  	if( -1 == interfaces ) {
  		free(statp);
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_SYSCTLBYNAME, "perfstat_netinterface");
  	}
  
  	VECTOR_UPDATE(network_io_stats_vector_ptr, interfaces, network_io_ptr, sg_network_io_stats);
  
  	/* print statistics for each of the interfaces */
  	for (i = 0; i < interfaces; ++i) {
  		if (sg_update_string(&network_io_ptr[i].interface_name, statp[i].name) != SG_ERROR_NONE ) {
  			free(statp);
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  		network_io_ptr[i].tx = statp[i].obytes;
  		network_io_ptr[i].rx = statp[i].ibytes;
  		network_io_ptr[i].opackets = statp[i].opackets;
  		network_io_ptr[i].ipackets = statp[i].ipackets;
  		network_io_ptr[i].oerrors = statp[i].oerrors;
  		network_io_ptr[i].ierrors = statp[i].ierrors;
  		network_io_ptr[i].collisions = statp[i].collisions;
  		network_io_ptr[i].systime = time(NULL);
  	}
  
  	free(statp);
  #elif defined(SOLARIS)
  	if ((kc = kstat_open()) == NULL) {
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_KSTAT_OPEN, NULL);
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP kstat_close(kc);
  
  	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
  		if (strcmp(ksp->ks_class, "net") == 0) {
  			kstat_read(kc, ksp, NULL);
  
  #ifdef SOL7
  #       define LRX "rbytes"
  #       define LTX "obytes"
  #       define LIPACKETS "ipackets"
  #       define LOPACKETS "opackets"
  #       define VALTYPE value.ui32
  #else
  #       define LRX "rbytes64"
  #       define LTX "obytes64"
  #       define LIPACKETS "ipackets64"
  #       define LOPACKETS "opackets64"
  #       define VALTYPE value.ui64
  #endif
  
  			/* Read rx */
  			if((knp=kstat_data_lookup(ksp, LRX))==NULL){
  				/* This is a network interface, but it doesn't
  				 * have the rbytes/obytes values; for instance,
  				 * the loopback devices have this behaviour
  				 * (although they do track packets in/out). */
  				/* FIXME: Show packet counts when byte counts
  				 * not available. */
  				continue;
  			}
  
  			VECTOR_UPDATE(network_io_stats_vector_ptr, interfaces + 1, network_io_ptr, sg_network_io_stats);
  
  			/* Read interface name */
  			if (SG_ERROR_NONE != sg_update_string(&network_io_ptr[interfaces].interface_name, ksp->ks_name)) {
  				kstat_close(kc);
  				RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  			}
  
  			/* Finish reading rx */
  			network_io_ptr[interfaces].rx=knp->VALTYPE;
  
  			/* Read tx */
  			if((knp=kstat_data_lookup(ksp, LTX))==NULL)
  				continue;
  			network_io_ptr[interfaces].tx=knp->VALTYPE;
  
  			/* Read ipackets */
  			if((knp=kstat_data_lookup(ksp, LIPACKETS))==NULL)
  				continue;
  			network_io_ptr[interfaces].ipackets=knp->VALTYPE;
  
  			/* Read opackets */
  			if((knp=kstat_data_lookup(ksp, LOPACKETS))==NULL)
  				continue;
  			network_io_ptr[interfaces].opackets=knp->VALTYPE;
  
  			/* Read ierrors */
  			if((knp=kstat_data_lookup(ksp, "ierrors"))==NULL)
  				continue;
  			network_io_ptr[interfaces].ierrors=knp->value.ui32;
  
  			/* Read oerrors */
  			if((knp=kstat_data_lookup(ksp, "oerrors"))==NULL)
  				continue;
  			network_io_ptr[interfaces].oerrors=knp->value.ui32;
  
  			/* Read collisions */
  			if((knp=kstat_data_lookup(ksp, "collisions"))==NULL)
  				continue;
  			network_io_ptr[interfaces].collisions=knp->value.ui32;
  
  
  			/* Store systime */
  			network_io_ptr[interfaces].systime=time(NULL);
  
  			++interfaces;
  		}
  	}
  
  	kstat_close(kc);
  #elif defined(LINUX)
  	f=fopen("/proc/net/dev", "r");
  	if(f==NULL) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_OPEN, "/proc/net/dev");
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP fclose(f);
  
  	/* read the 2 lines.. Its the title, so we dont care :) */
  	if( ( NULL == fgets(line, sizeof(line), f) ) ||
  	    ( NULL == fgets(line, sizeof(line), f) ) ) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_PARSE, "/proc/net/dev");
  	}
  
  	while((fgets(line, sizeof(line), f)) != NULL){
  		if((regexec(&network_io_rx, line, RX_MATCH_COUNT, line_match, 0))!=0){
  			continue;
  		}
  
  		VECTOR_UPDATE(network_io_stats_vector_ptr, interfaces + 1, network_io_ptr, sg_network_io_stats);
  
  		if( network_io_ptr[interfaces].interface_name != NULL ) {
  			free(network_io_ptr[interfaces].interface_name);
  			network_io_ptr[interfaces].interface_name = NULL;
  		}
  
  		network_io_ptr[interfaces].interface_name = sg_get_string_match(line, &line_match[1]);
  		network_io_ptr[interfaces].rx = sg_get_ll_match(line, &line_match[2]);
  		network_io_ptr[interfaces].tx = sg_get_ll_match(line, &line_match[5]);
  		network_io_ptr[interfaces].ipackets = sg_get_ll_match(line, &line_match[3]);
  		network_io_ptr[interfaces].opackets = sg_get_ll_match(line, &line_match[6]);
  		network_io_ptr[interfaces].ierrors = sg_get_ll_match(line, &line_match[4]);
  		network_io_ptr[interfaces].oerrors = sg_get_ll_match(line, &line_match[7]);
  		network_io_ptr[interfaces].collisions = sg_get_ll_match(line, &line_match[8]);
  		network_io_ptr[interfaces].systime = time(NULL);
  
  		++interfaces;
  	}
  
  	fclose(f);
  #elif defined(WIN32)
  	if((if_table = win32_get_devices()) == NULL) {
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_DEVICES, "win32_get_devices");
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP free(if_table);
  
  	VECTOR_UPDATE(network_io_stats_vector_ptr, interfaces + 1, network_io_ptr, sg_network_io_stats);
  
  	for ( i = 0; i < if_table->dwNumEntries; ++i ) {
  		if_row = if_table->table[i];
  
  		if(SG_ERROR_NONE != sg_update_string(&network_io_ptr[i].interface_name, if_row.bDescr) ) {
  			free(if_table);
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  
  		network_io_ptr[i].tx = if_row.dwOutOctets;
  		network_io_ptr[i].rx = if_row.dwInOctets;
  		network_io_ptr[i].ipackets = if_row.dwInUcastPkts + if_row.dwInNUcastPkts;
  		network_io_ptr[i].opackets = if_row.dwOutUcastPkts + if_row.dwOutNUcastPkts;
  		network_io_ptr[i].ierrors = if_row.dwInErrors;
  		network_io_ptr[i].oerrors = if_row.dwOutErrors;
  		network_io_ptr[i].collisions = 0; /* can't do that */
  		network_io_ptr[i].systime = time(NULL);
  
  		++interfaces;
  	}
  	free(if_table);
  
  	/* Please say there's a nicer way to do this...  If windows has two (or
  	 * more) identical network cards, GetIfTable returns them with the same
  	 * name, not like in Device Manager where the other has a #2 etc after
  	 * it. So, add the #number here. Should we be doing this? Or should the
  	 * end programs be dealing with duplicate names? Currently breaks
  	 * watch.pl in rrdgraphing. But Unix does not have the issue of
  	 * duplicate net device names.
  	 */
  	for (i=0; i < interfaces; ++i) {
  		int no = 2, j;
  		for(j=i+1; j<interfaces; ++j) {
  			if(strcmp(network_io_ptr[i].interface_name, network_io_ptr[j].interface_name) == 0) {
  				if(snprintf(buf, sizeof(buf), " #%d", no) < 0) {
  					break;
  				}
  				if(sg_concat_string(&network_io_ptr[i].interface_name, buf) != 0) {
  					RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  				}
  
  				++no;
  			}
  		}
  	}
  #else
  	RETURN_WITH_SET_ERROR("network", SG_ERROR_UNSUPPORTED, OS_TYPE);
  #endif
  
  	return SG_ERROR_NONE;
  }
  
  MULTI_COMP_ACCESS(sg_get_network_io_stats,network,network_io,SG_NETWORK_IO_NOW_IDX)
  MULTI_COMP_DIFF(sg_get_network_io_stats_diff,sg_get_network_io_stats,network,network_io,SG_NETWORK_IO_DIFF_IDX,SG_NETWORK_IO_NOW_IDX)
  
  int
  sg_network_io_compare_name(const void *va, const void *vb) {
  	const sg_network_io_stats *a = va, *b = vb;
  	return strcmp(a->interface_name, b->interface_name);
  }
  
  /* NETWORK INTERFACE STATS */
  
  static sg_error
  sg_get_network_iface_stats_int(sg_vector **network_iface_vector_ptr){
  	sg_network_iface_stats *network_iface_stat;
  	size_t ifaces = 0;
  	time_t now = time(NULL);
  
  #ifdef SOLARIS
  	kstat_ctl_t *kc;
  	kstat_t *ksp;
  	kstat_named_t *knp;
  	int sock;
  #elif defined(ALLBSD)
  	struct ifmediareq ifmed;
  	struct ifaddrs *net, *net_ptr;
  	struct ifreq ifr;
  	int sock;
  #elif defined(LINUX)
  	FILE *f;
  	/* Horrible big enough, but it should be easily big enough */
  	char line[8096];
  	int sock;
  #elif defined(WIN32)
  	PMIB_IFTABLE if_table;
  	MIB_IFROW if_row;
  	int i,j,no;
  	char buf[5];
  #elif defined(AIX)
  	int fd, n;
  	struct ifreq *ifr, *lifr, iff;
  	struct ifconf ifc;
  #elif defined(HPUX)
  	int fd;
  	struct ifreq *ifr, *lifr, iff;
  	struct ifconf ifc;
  #endif
  
  #ifdef ALLBSD
  	if(getifaddrs(&net) != 0) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_GETIFADDRS, NULL);
  	}
  
  	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == 0) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_SOCKET, NULL);
  	}
  
  	for( net_ptr = net; net_ptr != NULL; net_ptr = net_ptr->ifa_next ) {
  		if( net_ptr->ifa_addr->sa_family != AF_LINK )
  			continue;
  
  		++ifaces;
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP close(sock); freeifaddrs(net);
  
  	VECTOR_UPDATE(network_iface_vector_ptr, ifaces, network_iface_stat, sg_network_iface_stats);
  
  	for( ifaces = 0, net_ptr = net; net_ptr != NULL; net_ptr = net_ptr->ifa_next ) {
  		if( net_ptr->ifa_addr->sa_family != AF_LINK )
  			continue;
  
  
  		memset(&ifr, 0, sizeof(ifr));
  		strncpy(ifr.ifr_name, net_ptr->ifa_name, sizeof(ifr.ifr_name));
  
  		if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
  			continue;
  
  		network_iface_stat[ifaces].up = ((ifr.ifr_flags & IFF_UP) != 0) ? SG_IFACE_UP : SG_IFACE_DOWN;
  
  		if (sg_update_string(&network_iface_stat[ifaces].interface_name,
  				     net_ptr->ifa_name) != SG_ERROR_NONE ) {
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  
  		network_iface_stat[ifaces].speed = network_iface_stat[ifaces].factor = 0;
  		network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_UNKNOWN;
  
  		network_iface_stat[ifaces].systime = now;
  
  		memset(&ifmed, 0, sizeof(struct ifmediareq));
  		sg_strlcpy(ifmed.ifm_name, net_ptr->ifa_name, sizeof(ifmed.ifm_name));
  		if(ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed) == -1){
  			/* Not all interfaces support the media ioctls. */
  			goto skip;
  		}
  
  		/* We may need to change this if we start doing wireless devices too */
  		if( (ifmed.ifm_active | IFM_ETHER) != ifmed.ifm_active ){
  			/* Not a ETHER device */
  			goto skip;
  		}
  
  		/* Assuming only ETHER devices */
  		switch(IFM_TYPE(ifmed.ifm_active)) {
  		case IFM_ETHER:
  			switch(IFM_SUBTYPE(ifmed.ifm_active)) {
  #if defined(IFM_HPNA_1) && ((!defined(IFM_10G_LR)) || (IFM_10G_LR != IFM_HPNA_1))
  				/* HomePNA 1.0 (1Mb/s) */
  				case(IFM_HPNA_1):
  					network_iface_stat[ifaces].speed = 1;
  					break;
  #endif
  				/* 10 Mbit connections. Speedy :) */
  				case(IFM_10_T): /* 10BaseT - RJ45 */
  				case(IFM_10_2): /* 10Base2 - Thinnet */
  				case(IFM_10_5): /* 10Base5 - AUI */
  				case(IFM_10_STP): /* 10BaseT over shielded TP */
  				case(IFM_10_FL): /* 10baseFL - Fiber */
  					network_iface_stat[ifaces].speed = 10;
  					break;
  				/* 100 Mbit connections */
  				case(IFM_100_TX): /* 100BaseTX - RJ45 */
  				case(IFM_100_FX): /* 100BaseFX - Fiber */
  				case(IFM_100_T4): /* 100BaseT4 - 4 pair cat 3 */
  				case(IFM_100_VG): /* 100VG-AnyLAN */
  				case(IFM_100_T2): /* 100BaseT2 */
  					network_iface_stat[ifaces].speed = 100;
  					break;
  				/* 1000 Mbit connections */
  				case(IFM_1000_SX): /* 1000BaseSX - multi-mode fiber */
  				case(IFM_1000_LX): /* 1000baseLX - single-mode fiber */
  				case(IFM_1000_CX): /* 1000baseCX - 150ohm STP */
  #if defined(IFM_1000_TX) && !defined(OPENBSD)
  				/* FreeBSD 4 and others (but NOT OpenBSD)? */
  				case(IFM_1000_TX):
  #endif
  #ifdef IFM_1000_FX
  				case(IFM_1000_FX):
  #endif
  #ifdef IFM_1000_T
  				case(IFM_1000_T):
  #endif
  					network_iface_stat[ifaces].speed = 1000;
  					break;
  #if defined(IFM_10G_SR) || defined(IFM_10G_LR) || defined(IFM_10G_CX4) || defined(IFM_10G_T)
  # ifdef IFM_10G_SR
  				case(IFM_10G_SR):
  # endif
  # ifdef IFM_10G_LR
  				case(IFM_10G_LR):
  # endif
  # ifdef IFM_10G_CX4
  				case(IFM_10G_CX4):
  # endif
  # ifdef IFM_10G_TWINAX
  				case(IFM_10G_TWINAX):
  # endif
  # ifdef IFM_10G_TWINAX_LONG
  				case(IFM_10G_TWINAX_LONG):
  # endif
  # ifdef IFM_10G_T
  				case(IFM_10G_T):
  # endif
  					network_iface_stat[ifaces].speed = 10000;
  					break;
  #endif
  #if defined(IFM_2500_SX)
  # ifdef IFM_2500_SX
  				case(IFM_2500_SX):
  # endif
  					network_iface_stat[ifaces].speed = 2500;
  					break;
  #endif /* any 2.5GBit stuff ...*/
  				/* We don't know what it is */
  				default:
  					network_iface_stat[ifaces].speed = 0;
  					break;
  			}
  			break;
  
  #ifdef IFM_TOKEN
  		case IFM_TOKEN:
  			switch(IFM_SUBTYPE(ifmed.ifm_active)) {
  				case IFM_TOK_STP4: /* Shielded twisted pair 4m - DB9 */
  				case IFM_TOK_UTP4: /* Unshielded twisted pair 4m - RJ45 */
  					network_iface_stat[ifaces].speed = 4;
  					break;
  
  				case IFM_TOK_STP16: /* Shielded twisted pair 16m - DB9 */
  				case IFM_TOK_UTP16: /* Unshielded twisted pair 16m - RJ45 */
  					network_iface_stat[ifaces].speed = 16;
  					break;
  #if defined(IFM_TOK_STP100) || defined(IFM_TOK_UTP100)
  # ifdef IFM_TOK_STP100
  				case IFM_TOK_STP100: /* Shielded twisted pair 100m - DB9 */
  # endif
  # ifdef IFM_TOK_UTP100
  				case IFM_TOK_UTP100: /* Unshielded twisted pair 100m - RJ45 */
  # endif
  					network_iface_stat[ifaces].speed = 100;
  					break;
  #endif
  
  				/* We don't know what it is */
  				default:
  					network_iface_stat[ifaces].speed = 0;
  					break;
  			}
  			break;
  #endif
  
  #ifdef IFM_FDDI
  		case IFM_FDDI:
  			switch(IFM_SUBTYPE(ifmed.ifm_active)) {
  				/* We don't know what it is */
  				default:
  					network_iface_stat[ifaces].speed = 0;
  					break;
  			}
  			break;
  #endif
  
  		case IFM_IEEE80211:
  			switch(IFM_SUBTYPE(ifmed.ifm_active)) {
  				case IFM_IEEE80211_FH1: /* Frequency Hopping 1Mbps */
  				case IFM_IEEE80211_DS1: /* Direct Sequence 1Mbps */
  					network_iface_stat[ifaces].speed = 1;
  					break;
  
  				case IFM_IEEE80211_FH2: /* Frequency Hopping 2Mbps */
  				case IFM_IEEE80211_DS2: /* Direct Sequence 2Mbps */
  					network_iface_stat[ifaces].speed = 2;
  					break;
  
  				case IFM_IEEE80211_DS5: /* Direct Sequence 5Mbps*/
  					network_iface_stat[ifaces].speed = 5;
  					break;
  
  				case IFM_IEEE80211_DS11: /* Direct Sequence 11Mbps*/
  					network_iface_stat[ifaces].speed = 11;
  					break;
  
  				case IFM_IEEE80211_DS22: /* Direct Sequence 22Mbps */
  					network_iface_stat[ifaces].speed = 22;
  					break;
  
  				/* We don't know what it is */
  				default:
  					network_iface_stat[ifaces].speed = 0;
  					break;
  			}
  			break;
  
  		default:
  			network_iface_stat[ifaces].speed = 0;
  			break;
  		}
  		network_iface_stat[ifaces].factor = 1000U * 1000U;
  
  		if( (ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active ) {
  			network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_FULL;
  		}
  		else if( (ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active ) {
  			network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_HALF;
  		}
  		else {
  			network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_UNKNOWN;
  		}
  
  skip:
  
  		++ifaces;
  	}
  	freeifaddrs(net);
  	close(sock);
  #elif defined(AIX)
  /*
   * Fix up for variable length of struct ifr
   */
  #ifndef _SIZEOF_ADDR_IFREQ
  #define _SIZEOF_ADDR_IFREQ(ifr) \
  	(((ifr).ifr_addr.sa_len > sizeof(struct sockaddr)) \
  	? sizeof(struct ifreq) - sizeof(struct sockaddr) + (ifr).ifr_addr.sa_len \
  	: sizeof(struct ifreq))
  #endif
  
  	if( (fd = socket (PF_INET, SOCK_DGRAM, 0)) == -1 ) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_SOCKET, NULL);
  	}
  
  	n = 2;
  	ifr = (struct ifreq *)malloc( n * sys_page_size );
  	if( NULL == ifr ) {
  		close (fd);
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_MALLOC, NULL);
  	}
  	bzero(&ifc, sizeof(ifc));
  	ifc.ifc_req = ifr;
  	ifc.ifc_len = n * sys_page_size;
  	while( ( ioctl( fd, SIOCGIFCONF, &ifc ) == -1 ) ||
  	       ( ((ssize_t)ifc.ifc_len) >= ( (n-1) * sys_page_size)) )
  	{
  		n *= 2;
  		if( (n * sys_page_size) > INT_MAX ) {
  			free( ifc.ifc_req );
  			close (fd);
  			RETURN_WITH_SET_ERROR_WITH_ERRNO_CODE("network", SG_ERROR_MALLOC, ERANGE, "SIOCGIFCONF");
  		}
  		ifr = (struct ifreq *)realloc( ifr, n * sys_page_size );
  		if( NULL == ifr ) {
  			free( ifc.ifc_req );
  			close (fd);
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_MALLOC, NULL);
  		}
  		ifc.ifc_req = ifr;
  		ifc.ifc_len = n * sys_page_size;
  	}
  
  	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
  	for ( ifr = ifc.ifc_req;
  	      ifr < lifr;
  	      ifr = (struct ifreq *)&(((char *)ifr)[_SIZEOF_ADDR_IFREQ(*ifr)]) )
  	{
  		struct sockaddr *sa = &ifr->ifr_addr;
  
  		if(sa->sa_family != AF_LINK)
  			continue;
  
  		++ifaces;
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP close(fd); free(ifc.ifc_req);
  
  	VECTOR_UPDATE(network_iface_vector_ptr, ifaces, network_iface_stat, sg_network_iface_stats);
  
  	for ( ifaces = 0, ifr = ifc.ifc_req;
  	      ifr < lifr;
  	      ifr = (struct ifreq *)&(((char *)ifr)[_SIZEOF_ADDR_IFREQ(*ifr)]) )
  	{
  		struct sockaddr *sa = &ifr->ifr_addr;
  
  		if(sa->sa_family != AF_LINK)
  			continue;
  
  		memset(&iff, 0, sizeof(iff));
  		strncpy(iff.ifr_name, ifr->ifr_name, sizeof(iff.ifr_name));
  
  		if (ioctl(fd, SIOCGIFFLAGS, &iff) < 0)
  			continue;
  
  		network_iface_stat[ifaces].up = ((iff.ifr_flags & IFF_UP) != 0) ? SG_IFACE_UP : SG_IFACE_DOWN;
  		if (sg_update_string(&network_iface_stat[ifaces].interface_name,
  				     ifr->ifr_name) != SG_ERROR_NONE ) {
  			close (fd);
  			free( ifc.ifc_req );
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  
  		network_iface_stat[ifaces].speed = 0;
  		network_iface_stat[ifaces].factor = 0;
  		/* XXX */
  		network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_UNKNOWN;
  		network_iface_stat[ifaces].systime = now;
  		++ifaces;
  	}
  
  	close (fd);
  	free( ifc.ifc_req );
  #elif defined(HPUX)
  /*
   * Fix up for variable length of struct ifr
   */
  #ifndef _SA_LEN
  #define _SA_LEN(sa) \
     ((sa).sa_family == AF_UNIX ? sizeof(struct sockaddr_un) : \
     (sa).sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : \
     sizeof(struct sockaddr))
  #endif
  
  #ifndef _SIZEOF_ADDR_IFREQ
  #define _SIZEOF_ADDR_IFREQ(ifr) \
  	((_SA_LEN((ifr).ifr_addr) > sizeof(struct sockaddr)) \
  	? sizeof(struct ifreq) - sizeof(struct sockaddr) + _SA_LEN((ifr).ifr_addr) \
  	: sizeof(struct ifreq))
  #endif
  	if ((fd = socket (PF_INET, SOCK_DGRAM, 0)) == -1) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_SOCKET, NULL);
  	}
  
  	bzero(&ifc, sizeof(ifc));
  	if (ioctl (fd, SIOCGIFNUM, &ifc) != -1) {
  		ifr = calloc(sizeof (*ifr), ifc.ifc_len);
  		if( NULL == ifr ) {
  			close (fd);
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_MALLOC, NULL);
  		}
  		ifc.ifc_req = ifr;
  		ifc.ifc_len *= sizeof (*ifr);
  		if (ioctl (fd, SIOCGIFCONF, &ifc) == -1) {
  			free (ifr);
  			close (fd);
  			RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_GETIFADDRS, NULL);
  		}
  	}
  	else {
  		close (fd);
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_GETIFADDRS, NULL);
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP close(fd); free(ifc.ifc_req);
  
  	lifr = (struct ifreq *)((void *)(&ifc.ifc_buf[ifc.ifc_len]));
  	for ( ifr = ifc.ifc_req;
  	      ifr < lifr;
  	      ifr = (struct ifreq *)((void *)(((char *)ifr) + _SIZEOF_ADDR_IFREQ(*ifr))) )
  	{
  		VECTOR_UPDATE(network_iface_vector_ptr, ifaces + 1, network_iface_stat, sg_network_iface_stats);
  
  		bzero(&iff, sizeof(iff));
  		strncpy(iff.ifr_name, ifr->ifr_name, sizeof(iff.ifr_name));
  
  		if (ioctl(fd, SIOCGIFFLAGS, &iff) < 0) {
  			continue;
  		}
  
  		network_iface_stat[ifaces].up = ((iff.ifr_flags & IFF_UP) != 0) ? SG_IFACE_UP : SG_IFACE_DOWN;
  		if (sg_update_string(&network_iface_stat[ifaces].interface_name,
  				     ifr->ifr_name) != SG_ERROR_NONE ) {
  			close (fd);
  			free( ifc.ifc_req );
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  
  		/**
  		 * for physical devices we now could ask /dev/dlpi for speed and type,
  		 * but for monitoring we don't care
  		 */
  		network_iface_stat[ifaces].speed = 0;
  		network_iface_stat[ifaces].factor = 0;
  		network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_UNKNOWN;
  		network_iface_stat[ifaces].systime = now;
  		++ifaces;
  	}
  
  	close (fd);
  	free( ifc.ifc_req );
  #elif defined(SOLARIS)
  	if ((kc = kstat_open()) == NULL) {
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_KSTAT_OPEN, NULL);
  	}
  
  	if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
  		kstat_close(kc);
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_SOCKET, NULL);
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP close(sock); kstat_close(kc);
  
  	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
  		if (strcmp(ksp->ks_class, "net") == 0) {
  			struct ifreq ifr;
  
  			kstat_read(kc, ksp, NULL);
  
  			strncpy(ifr.ifr_name, ksp->ks_name, sizeof(ifr.ifr_name));
  			if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
  				/* Not a network interface. */
  				continue;
  			}
  
  			VECTOR_UPDATE(network_iface_vector_ptr, ifaces + 1, network_iface_stat, sg_network_iface_stats);
  
  			if (sg_update_string(&network_iface_stat[ifaces].interface_name,
  					     ksp->ks_name) != SG_ERROR_NONE ) {
  				VECTOR_UPDATE_ERROR_CLEANUP
  				RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  			}
  
  			if ((ifr.ifr_flags & IFF_UP) != 0) {
  				if ((knp = kstat_data_lookup(ksp, "link_up")) != NULL) {
  					/* take in to account if link
  					 * is up as well as interface */
  					network_iface_stat[ifaces].up = (knp->value.ui32 != 0u) ? SG_IFACE_UP : SG_IFACE_DOWN;
  				}
  				else {
  					/* maintain compatibility */
  					network_iface_stat[ifaces].up = SG_IFACE_UP;
  				}
  			}
  			else {
  				network_iface_stat[ifaces].up = SG_IFACE_DOWN;
  			}
  
  			if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL) {
  				network_iface_stat[ifaces].factor = 1000U * 1000U;
  				network_iface_stat[ifaces].speed = knp->value.ui64 / network_iface_stat[ifaces].factor;
  			}
  			else {
  				network_iface_stat[ifaces].speed = network_iface_stat[ifaces].factor = 0;
  			}
  
  			network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_UNKNOWN;
  			if ((knp = kstat_data_lookup(ksp, "link_duplex")) != NULL) {
  				switch (knp->value.ui32) {
  				case 1:
  					network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_HALF;
  					break;
  				case 2:
  					network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_FULL;
  					break;
  				}
  			}
  
  			network_iface_stat[ifaces].systime = now;
  
  			++ifaces;
  		}
  	}
  
  	close(sock);
  	kstat_close(kc);
  #elif defined(LINUX)
  	f = fopen("/proc/net/dev", "r");
  	if(f == NULL) {
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_OPEN, "/proc/net/dev");
  	}
  
  	/* Setup stuff so we can do the ioctl to get the info */
  	if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  		fclose(f);
  		RETURN_WITH_SET_ERROR_WITH_ERRNO("network", SG_ERROR_SOCKET, NULL);
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP close(sock); fclose(f);
  
  	/* Ignore first 2 lines.. Just headings */
  	if((fgets(line, sizeof(line), f)) == NULL) {
  		VECTOR_UPDATE_ERROR_CLEANUP
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_PARSE, NULL);
  	}
  	if((fgets(line, sizeof(line), f)) == NULL) {
  		VECTOR_UPDATE_ERROR_CLEANUP
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_PARSE, NULL);
  	}
  
  	while((fgets(line, sizeof(line), f)) != NULL){
  		char *name, *ptr;
  		struct ifreq ifr;
  		struct ethtool_cmd ethcmd;
  		int err;
  
  		/* Get the interface name */
  		ptr = strchr(line, ':');
  		if (ptr == NULL)
  			continue;
  		*ptr='\0';
  		name = line;
  		while(isspace(*(name))){
  			++name;
  		}
  
  		memset(&ifr, 0, sizeof ifr);
  		strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
  
  		if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
  			continue;
  		}
  
  		/* We have a good interface to add */
  		VECTOR_UPDATE(network_iface_vector_ptr, ifaces + 1, network_iface_stat, sg_network_iface_stats);
  
  		if (sg_update_string(&network_iface_stat[ifaces].interface_name, name) != SG_ERROR_NONE ) {
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  		network_iface_stat[ifaces].up = ((ifr.ifr_flags & IFF_UP) != 0) ? SG_IFACE_UP : SG_IFACE_DOWN;
  
  		memset(&ethcmd, 0, sizeof ethcmd);
  		ethcmd.cmd = ETHTOOL_GSET;
  		ifr.ifr_data = (caddr_t) &ethcmd;
  
  		err = ioctl(sock, SIOCETHTOOL, &ifr);
  		if (err == 0) {
  #ifdef HAVE_ETHTOOL_CMD_SPEED_HI
  			network_iface_stat[ifaces].speed = ethtool_cmd_speed(&ethcmd);
  #else
  			switch(ethcmd.speed) {
  			case SPEED_10:
  				network_iface_stat[ifaces].speed = 10;
  			case SPEED_100:
  				network_iface_stat[ifaces].speed = 100;
  			case SPEED_1000:
  				network_iface_stat[ifaces].speed = 1000;
  			case SPEED_2500:
  				network_iface_stat[ifaces].speed = 2500;
  			case SPEED_10000:
  				network_iface_stat[ifaces].speed = 10000;
  			/* We don't know what it is */
  			default:
  				network_iface_stat[ifaces].speed = 0;
  				break;
  			}
  #endif
  			network_iface_stat[ifaces].factor = 1000U * 1000U;
  
  			switch (ethcmd.duplex) {
  			case DUPLEX_FULL:
  				network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_FULL;
  				break;
  			case DUPLEX_HALF:
  				network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_HALF;
  				break;
  			default:
  				network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_UNKNOWN;
  			}
  		}
  		else {
  			/* Not all interfaces support the ethtool ioctl. */
  			network_iface_stat[ifaces].speed = 0;
  			network_iface_stat[ifaces].factor = 0;
  			network_iface_stat[ifaces].duplex = SG_IFACE_DUPLEX_UNKNOWN;
  		}
  
  		network_iface_stat[ifaces].systime = now;
  
  		++ifaces;
  	}
  	close(sock);
  	fclose(f);
  #elif defined(WIN32)
  	if((if_table = win32_get_devices()) == NULL) {
  		RETURN_WITH_SET_ERROR("network", SG_ERROR_DEVICES, "network interfaces");
  	}
  
  #undef VECTOR_UPDATE_ERROR_CLEANUP
  #define VECTOR_UPDATE_ERROR_CLEANUP free(if_table);
  
  	VECTOR_UPDATE(network_iface_vector_ptr, if_table->dwNumEntries, network_iface_stat, sg_network_iface_stats);
  
  	for( i = 0; i < if_table->dwNumEntries; ++i ) {
  		if_row = if_table->table[i];
  
  		if(sg_update_string(&network_iface_stat[i].interface_name,
  					if_row.bDescr) != SG_ERROR_NONE ) {
  			VECTOR_UPDATE_ERROR_CLEANUP
  			RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  		}
  		network_iface_stat[ifaces].factor = 1000U * 1000U;
  		network_iface_stat[i].speed = if_row.dwSpeed / network_iface_stat[ifaces].factor;
  
  		if( ( if_row.dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED ||
  		      if_row.dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL ) &&
  		    if_row.dwAdminStatus == 1 ) {
  			network_iface_stat[i].up = SG_IFACE_UP;
  		}
  		else {
  			network_iface_stat[i].up = SG_IFACE_DOWN;
  		}
  
  		network_iface_stat[i].systime = now;
  
  		++ifaces;
  	}
  	free(if_table);
  
  	/* again with the renumbering */
  	for (i=0; i < ifaces; ++i) {
  		int no = 2, j;
  		for(j=i+1; j<ifaces; ++j) {
  			if(strcmp(network_io_ptr[i].interface_name, network_io_ptr[j].interface_name) == 0) {
  				if(snprintf(buf, sizeof(buf), " #%d", no) < 0) {
  					break;
  				}
  				if(sg_concat_string(&network_io_ptr[i].interface_name, buf) != 0) {
  					RETURN_FROM_PREVIOUS_ERROR( "network", sg_get_error() );
  				}
  
  				++no;
  			}
  		}
  	}
  #else
  	RETURN_WITH_SET_ERROR("network", SG_ERROR_UNSUPPORTED, OS_TYPE);
  #endif
  
  	return SG_ERROR_NONE;
  }
  
  MULTI_COMP_ACCESS(sg_get_network_iface_stats,network,network_iface,SG_NETWORK_IFACE_IDX)
  
  int
  sg_network_iface_compare_name(const void *va, const void *vb) {
  	const sg_network_iface_stats *a = va, *b = vb;
  	return strcmp(a->interface_name, b->interface_name);
  }
  