/*
 *========================================================================
 * $Id: get_proc_stat.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/stat and packs its values into doc.
 */

void get_proc_stat(xmlNodePtr proc)
{

 int i,numfields, cpuid;
 struct timeval tv;
 xmlNodePtr stat,statname,statvalue;

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

 /* 
  * Create a timestamped child of document root.  Everything below
  * will belong to this
  */
 stat = xmlNewChild(proc,NULL,(xmlChar*) "stat",NULL);
 gettimeofday(&tv,0);
 sprintf(outbuf,"%d",tv.tv_sec);
 xmlSetProp(stat,(xmlChar*) "tv_sec",(xmlChar*) outbuf);
 sprintf(outbuf,"%d",tv.tv_usec);
 xmlSetProp(stat,(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 stat the file in question 
  * on EACH open/close).
  */

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

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

 while(TRUE){

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

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

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

   /*
    * Now we go down a simple lookup table to assemble each statistic
    * by name.  fields[] now contains the parsed fields from a line of
    * /proc/stat.  We identify the line by its first entry (the name
    * of the /proc/stat entity) and use the following values to build 
    * one or more statistics.
    */

   /* 
    * CPU is killing me.  "cpu" is total (a derived value).  Otherwise
    * cpu0, cpu1, etc. are the actual cpus.  We elect NOT TO SEND
    * cpu as it is actually easier to add it up at the other end than
    * to screw around with differentiating the total cpu tag from
    * the individual cpu tags.
    */
   if(strncmp(fields[0],"cpu",3) == 0){
     if(strlen(fields[0]) >= 4){
       statname = xmlNewChild(stat,NULL,(xmlChar*) "cpu",NULL);
       /* This should work even for double digit numbers of cpus */
       sprintf(outbuf,"%d",atoi(&fields[0][3]));
       xmlSetProp(statname,(xmlChar*) "id",(xmlChar*) outbuf);
       statvalue = xmlNewChild(statname,NULL,(xmlChar*) "user",(xmlChar*) fields[1]);
       statvalue = xmlNewChild(statname,NULL,(xmlChar*) "nice",(xmlChar*) fields[2]);
       statvalue = xmlNewChild(statname,NULL,(xmlChar*) "sys",(xmlChar*) fields[3]);
       statvalue = xmlNewChild(statname,NULL,(xmlChar*) "idle",(xmlChar*) fields[4]);
     }
   }

   /* PAGE */
   if(strncmp(fields[0],"page",4) == 0){
     statname = xmlNewChild(stat,NULL,(xmlChar*) "page",NULL);
     statvalue = xmlNewChild(statname,NULL,(xmlChar*) "in",(xmlChar*) fields[1]);
     statvalue = xmlNewChild(statname,NULL,(xmlChar*) "out",(xmlChar*) fields[2]);
   }

   /* SWAP */
   if(strncmp(fields[0],"swap",4) == 0){
     statname = xmlNewChild(stat,NULL,(xmlChar*) "swap",NULL);
     statvalue = xmlNewChild(statname,NULL,(xmlChar*) "in",(xmlChar*) fields[1]);
     statvalue = xmlNewChild(statname,NULL,(xmlChar*) "out",(xmlChar*) fields[2]);
   }

   /* INTR */
   /*
    * We only do the first field (total interrupt activity) because that
    * seems like all that one really needs to remotely monitor.  If anyone
    * needs per-interrupt stats, they can probably achieve this by adding
    * a loop over all the fields[] and using the interrupt number as the
    * interrupt id (see the way cpu is handled above).
    */
   if(strncmp(fields[0],"intr",4) == 0){
     statname = xmlNewChild(stat,NULL,(xmlChar*) "intr",(xmlChar*) fields[1]);
   }

   /* PROCESSES */
   if(strncmp(fields[0],"processes",9) == 0){
     statname = xmlNewChild(stat,NULL,(xmlChar*) "processes",(xmlChar*) fields[1]);
   }

   /* PROCS_RUNNING */
   if(strncmp(fields[0],"procs_running",13) == 0){
     statname = xmlNewChild(stat,NULL,(xmlChar*) "procs_running",(xmlChar*) fields[1]);
   }

   /* PROCS_BLOCKED */
   if(strncmp(fields[0],"procs_blocked",13) == 0){
     statname = xmlNewChild(stat,NULL,(xmlChar*) "procs_blocked",(xmlChar*) fields[1]);
   }

   /* CONTEXT */
   if(strncmp(fields[0],"ctxt",4) == 0){
     statname = xmlNewChild(stat,NULL,(xmlChar*) "ctxt",(xmlChar*) fields[1]);
   }

   /* DISK */
   /*
    * Disk activity (other than page/swap) is not currently parsed.
    * This may be added at some future time once I figure out the
    * format. Or you (reading this comment) can add it and send it
    * back to me if you need it right away and have the time and skill.
    */

   /* OTHERS 
    * ... can be added by anyone that needs them ditto.  On my
    * systems at least kstats are really really boring, and btime
    * doesn't appear useful.
    */

 }

}

