/*
 *========================================================================
 * $Id: get_proc_net.c 163 2005-09-14 22:54:20Z rgb $
 *
 * See copyright in copyright.h and the accompanying file COPYING
 *========================================================================
 */

#include "xmlsysd.h"

/*
 * This routine parses /proc/net/* (dev and possibly others) and packs 
 * their values into <proc><net>...
 */

void get_proc_net(xmlNodePtr proc)
{

 int i,j,k,icnt,numfields,fieldlen;
 char if_hostname[64];
 struct timeval tv;
 struct ifreq ifr;
 char interface_ip[32];
 struct hostent *host;
 struct in_addr *hostaddr;
 struct sockaddr_in sin;
 int skfd;
 xmlNodePtr net,dev,interface,devtype,interfaceval,txrxval;
 xmlNodePtr sockstat,socktype,sockval;

 if(verbose==102){
   fprintf(stderr,"Starting get_proc_net().\n");
 }

 /* 
  * Create <net> child of <proc> node.  Everything below
  * will belong to this
  */
 net = xmlNewChild(proc,NULL,(xmlChar*) "net",NULL);


 /* 
  * Create <dev> child of <net> node.  This has to be timestamped
  */
 dev = xmlNewChild(net,NULL,(xmlChar*) "dev",NULL);
 gettimeofday(&tv,0);
 sprintf(outbuf,"%d",tv.tv_sec);
 xmlSetProp(dev,(xmlChar*) "tv_sec",(xmlChar*) outbuf);
 sprintf(outbuf,"%d",tv.tv_usec);
 xmlSetProp(dev,(xmlChar*) "tv_usec",(xmlChar*) outbuf);


 /* 
  * Now, for a clever trick.  We RESET the files without actually
  * closing or reopening them.  This should save the overhead of
  * an open/close (presumed relatively large, as one has to 
  * allocate/free certain kernel structures and meminfo the file in question 
  * on EACH open/close).
  */

 /* PROC_NET_DEV */
 errno = 2;
 if(stat_fd[PROC_NET_DEV]){
   rewind(stat_fd[PROC_NET_DEV]);	/* void, so tough to check errors */
 } else {
   xmlSetProp(dev,(xmlChar*) "available",(xmlChar*) "no");
   return;
 }
 if(errno == EBADF){
   fprintf(stderr,"Error: The %s file descriptor/stream is not seekable.\n",procpaths[PROC_NET_DEV]);
   fclose(stat_fd[PROC_NET_DEV]); 
   fprintf(stderr,"Closing and reopening %s.\n",procpaths[PROC_NET_DEV]);
   stat_fd[PROC_NET_DEV] = fopen(procpaths[PROC_NET_DEV],"r");
 }

 if(verbose == 102){
   fprintf(stderr,"Rewound %s.\n",procpaths[PROC_NET_DEV]);
 }

 icnt = 0;	/* the interface id attribute -- increment per interface */
 while(TRUE){

   /* Normal EOF causes break from while loop */
   if((fgets(statbuf,K,stat_fd[PROC_NET_DEV]) == NULL)) break;

   if(verbose == 127){
     fprintf(stderr,"Parsing %s\n",statbuf);
   }

   /* parse the line into fields */
   numfields = parse(statbuf,fields,MAXFIELDNUMBER,K);

   /*
    * This is a bit complicated.  Each line in /proc/net/dev is either
    * a label/comment or a set of values for an interface.  We don't
    * really know a priori what the interface names will be or how many
    * of them there will be.  The best we can do is thus create a tag
    * out of field[0] (the interface name) with the id set from the
    * last character, if a digit.  Yuck.  We then put all the values for
    * that interface in as children of this tag.
    *
    * Alas, we then have approximate duplication of all field names, one
    * for Receive and one for Transmit.  These thus need to be children of
    * the interface tag, each with its own set of labelled values, right?
    *
    * If that isn't bad enough, the first two lines (only) are the label
    * and have to be ignored.
    *
    * Do you get the feeling that every part of /proc was designed by a
    * different person?  I do.  I may find myself making a version of
    * this daemon that simply repacks the whole damn thing into xml
    * following the rough specification developed thus far.
    */

   /*
    * Ignore lines starting with Inter and -face
    */
   if(verbose == 127){
     fprintf(stderr,"Doing line starting with %s\n",fields[0]);
   }
   if( (strncmp(fields[0],"Inter",5) == 0) || (strncmp(fields[0],"face",4) == 0) ){
     /* Comments -- I do it this way to avoid the hassle of ! &&'s in logic */
   } else {
     /*
      * Icon says to make the toplevel here a generic tag like <interface>
      * rgb says (now from experience) to add an id attribute for any
      * tag that can occur more than once with the same path so one has an
      * easy way to identify it and loop over it.
      */
     interface = xmlNewChild(dev,NULL,(xmlChar*) "interface",NULL);
     sprintf(outbuf,"%d",icnt++);
     xmlSetProp(interface,(xmlChar*) "id",(xmlChar*) outbuf);


     /* 
      * We see if we can get some interface information via ioctls 
      * We allocate an internet socket, IP protocol.  We hope.
      */
     skfd = socket(AF_INET,SOCK_STREAM,0);
     if (skfd >= 0) {
	/* strcpy(ifr.ifr_name, fields[0]); */
        strncpy(ifr.ifr_name, fields[0], IFNAMSIZ);
        memset(&sin, 0, sizeof(struct sockaddr));
        sin.sin_family = AF_INET;
        memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
	if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
          memcpy(&sin, &ifr.ifr_addr, sizeof(struct sockaddr));
          sprintf(interface_ip,"%s",inet_ntoa(sin.sin_addr));
          host = gethostbyaddr(&sin.sin_addr,4,AF_INET);
	  if(host == NULL){
            strncpy(if_hostname,"unknown",62);
	  } else {
            strncpy(if_hostname,host->h_name,62);
	  }
          k = 0;
          while(if_hostname[k] != 0 && if_hostname[k] != '.') {
            k++;
	  }
          if_hostname[k] = 0;	/* terminate it */
          if((verbose == D_ALL) || (verbose == D_NET)){
            printf("D_NET: Interface = %s hostname = %s hostip = %s\n",fields[0],if_hostname,inet_ntoa(sin.sin_addr));
	  }
	} else {
          sprintf(interface_ip,"%s","0.0.0.0");
          sprintf(if_hostname,"NotConf");
          if((verbose == D_ALL) || (verbose == D_NET)){
            printf("D_NET: Interface = %s hostname = %s hostip = %s\n",fields[0],if_hostname,inet_ntoa(sin.sin_addr));
	  }
	}
        close(skfd);
     }
     /*
      * We have to make a unique path to all content, and we never
      * want the path itself to depend on lower level content.  We
      * therefore cannot specify the type of interface beneath the
      * interface tag -- it must be an attribute.  So is the device
      * number.  Thus e.g. the number of bytes received by eth0 can 
      * be accessed by an xpath like:
      *
      *   ../interface[@devtype="eth" @devid="0"]/receive/bytes
      *
      * We still put the device name into a tag of its own, but
      * we don't really need it.
      */
     i=0;
     while( isalpha(fields[0][i]) ) {
       /* printf("%c ",fields[0][i]); */
       outbuf[i] = fields[0][i];
       i++;
     }
     /* add the string termination character */
     outbuf[i] = (char)NULL;
     /* printf("= %s\n",outbuf); */
     xmlSetProp(interface,(xmlChar*) "devtype",(xmlChar*) outbuf);
     /* 
      * Now create an attribute string ONLY IF there are trailing
      * digits.  Note that we don't reset i, and we start outbuf
      * over again at 0 in the j-loop.
      */
     j = 0;
     while( isdigit(fields[0][i]) ){
       /* printf("%c ",fields[0][i]); */
       outbuf[j++] = fields[0][i];
       i++;
     }
     /* Again we terminate the result */
     outbuf[j] = (char)NULL;
     /* printf("= %s\n",outbuf); */
     xmlSetProp(interface,(xmlChar*) "devid",(xmlChar*) outbuf);
     interfaceval = xmlNewChild(interface,NULL,(xmlChar*) "name",(xmlChar*) fields[0]);
     interfaceval = xmlNewChild(interface,NULL,(xmlChar*) "ip",(xmlChar*) interface_ip);
     /* interfaceval = xmlNewChild(interface,NULL,(xmlChar*) "host",(xmlChar*) host->h_name); */
     interfaceval = xmlNewChild(interface,NULL,(xmlChar*) "host",(xmlChar*) if_hostname);

     /* First set of values are receive */
     interfaceval = xmlNewChild(interface,NULL,(xmlChar*) "receive",NULL);
     /* Finally, the values */
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "bytes",(xmlChar*) fields[1]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "packets",(xmlChar*) fields[2]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "errs",(xmlChar*) fields[3]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "drop",(xmlChar*) fields[4]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "fifo",(xmlChar*) fields[5]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "frame",(xmlChar*) fields[6]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "compressed",(xmlChar*) fields[7]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "multicast",(xmlChar*) fields[8]);

     /* 
      * Second set of values are transmit.  Note differences in
      * field names.  Note also that I'm not testing for version
      * number, so this won't work for all kernel versions.
      */
     interfaceval = xmlNewChild(interface,NULL,(xmlChar*) "transmit",NULL);
     /* Finally, the values */
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "bytes",(xmlChar*) fields[9]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "packets",(xmlChar*) fields[10]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "errs",(xmlChar*) fields[11]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "drop",(xmlChar*) fields[12]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "fifo",(xmlChar*) fields[13]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "collisions",(xmlChar*) fields[14]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "carrier",(xmlChar*) fields[15]);
     txrxval = xmlNewChild(interfaceval,NULL,(xmlChar*) "compressed",(xmlChar*) fields[16]);
   }

 }

 /* 
  * Create <sockstat> child of <net> node.  This has to be timestamped
  */
 sockstat = xmlNewChild(net,NULL,(xmlChar*) "sockstat",NULL);
 gettimeofday(&tv,0);
 sprintf(outbuf,"%d",tv.tv_sec);
 xmlSetProp(sockstat,(xmlChar*) "tv_sec",(xmlChar*) outbuf);
 sprintf(outbuf,"%d",tv.tv_usec);
 xmlSetProp(sockstat,(xmlChar*) "tv_usec",(xmlChar*) outbuf);


 /* PROC_NET_SOCKSTAT */
 errno = 2;
 if(stat_fd[PROC_NET_SOCKSTAT]){
   rewind(stat_fd[PROC_NET_SOCKSTAT]);	/* void, so tough to check errors */
 } else {
   xmlSetProp(dev,(xmlChar*) "available",(xmlChar*) "no");
   return;
 }
 if(errno == EBADF){
   fprintf(stderr,"Error: The %s file descriptor/stream is not seekable.\n",procpaths[PROC_NET_SOCKSTAT]);
   fclose(stat_fd[PROC_NET_SOCKSTAT]); 
   fprintf(stderr,"Closing and reopening %s.\n",procpaths[PROC_NET_SOCKSTAT]);
   stat_fd[PROC_NET_SOCKSTAT] = fopen(procpaths[PROC_NET_SOCKSTAT],"r");
 }

 if(verbose == 102){
   fprintf(stderr,"Rewound %s.\n",procpaths[PROC_NET_SOCKSTAT]);
 }

 while(TRUE){

   /* Normal EOF causes break from while loop */
   if((fgets(statbuf,K,stat_fd[PROC_NET_SOCKSTAT]) == NULL)) break;

   if(verbose == 127){
     fprintf(stderr,"Parsing %s\n",statbuf);
   }

   /* parse the line into fields */
   numfields = parse(statbuf,fields,MAXFIELDNUMBER,K);

   /*
    * This is a straightforward parse.  There are lines for
    * sockets, TCP, UDP, RAW and FRAG.  Every one of them is
    * a series of named values in the form name value.  Piece of
    * cake.  We'll simply slavishly duplicate this format.
    */

   /* sockets */
   if(strncmp(fields[0],"sockets",7) == 0){
     sockval = xmlNewChild(sockstat,NULL,(xmlChar*) "used",(xmlChar*) fields[2]);
   }

   /* TCP */
   if(strncmp(fields[0],"TCP",3) == 0){
     socktype = xmlNewChild(sockstat,NULL,(xmlChar*) "tcp",NULL);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "inuse",(xmlChar*) fields[2]);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "orphan",(xmlChar*) fields[4]);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "timewait",(xmlChar*) fields[6]);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "alloc",(xmlChar*) fields[8]);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "mem",(xmlChar*) fields[10]);
   }

   /* UDP */
   if(strncmp(fields[0],"UDP",3) == 0){
     socktype = xmlNewChild(sockstat,NULL,(xmlChar*) "udp",NULL);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "inuse",(xmlChar*) fields[2]);
   }

   /* RAW */
   if(strncmp(fields[0],"RAW",3) == 0){
     socktype = xmlNewChild(sockstat,NULL,(xmlChar*) "raw",NULL);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "inuse",(xmlChar*) fields[2]);
   }

   /* FRAG */
   if(strncmp(fields[0],"FRAG",4) == 0){
     socktype = xmlNewChild(sockstat,NULL,(xmlChar*) "frag",NULL);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "inuse",(xmlChar*) fields[2]);
     sockval = xmlNewChild(socktype,NULL,(xmlChar*) "memory",(xmlChar*) fields[4]);
   }

 }

}

