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

void get_proc_cpuinfo(xmlNodePtr proc)
{

 int i,numfields;
 struct timeval tv;
 xmlNodePtr cpuinfo,processor,processorval;
 /*
  * This is cruftish, but the keepers of /proc/cpuinfo
  * didn't see fit to put cpu cores into ALL /proc/cpuinfo
  * files.  So we'll have to parse on the basis of an assumed
  * 1 and override if we find something else below
  */
 int cores = 1;

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

 /* 
  * Create a timestamped child of proc node.  Everything below
  * will belong to this
  */
 cpuinfo = xmlNewChild(proc,NULL,(xmlChar*) "cpuinfo",NULL);
 gettimeofday(&tv,0);
 sprintf(outbuf,"%d",tv.tv_sec);
 xmlSetProp(cpuinfo,(xmlChar*) "tv_sec",(xmlChar*) outbuf);
 sprintf(outbuf,"%d",tv.tv_usec);
 xmlSetProp(cpuinfo,(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_CPUINFO */
 errno = 2;
 if(stat_fd[PROC_CPUINFO]){
   rewind(stat_fd[PROC_CPUINFO]);	/* void, so tough to check errors */
 } else {
   xmlSetProp(cpuinfo,(xmlChar*) "available",(xmlChar*) "no");
   return;
 }
 if(errno == EBADF){
   fprintf(stderr,"Error: The %s file descriptor/stream is not seekable.\n",procpaths[PROC_CPUINFO]);
   fclose(stat_fd[PROC_CPUINFO]); 
   fprintf(stderr,"Closing and reopening %s.\n",procpaths[PROC_CPUINFO]);
   stat_fd[PROC_CPUINFO] = fopen(procpaths[PROC_CPUINFO],"r");
 }

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

 while(TRUE){

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

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

   /* 
    * Handle condemnable empty lines!  Who writes this shit! 
    */
   if(numfields != 0) {

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

     /*
      * Now we go down a simple lookup table to assemble each statistic
      * by name.  We will sort of automatically recurse (we hope) through
      * multiple cpu's, since each cpu just recapitulates in order with
      * a different id.
      */
  
     /* processor wraps all other field values */
     if(strncmp(fields[0],"processor",9) == 0 && strlen(fields[0]) == 9){
       processor = xmlNewChild(cpuinfo,NULL,(xmlChar*) "processor",NULL);
       xmlSetProp(processor,(xmlChar*) "id",(xmlChar*) fields[1]);
     }
     /* now we grab properties and values for the processor */
     /* vendor_id */
     if(strncmp(fields[0],"vendor_id",9) == 0 && strlen(fields[0]) == 9){
       processorval = xmlNewChild(processor,NULL,(xmlChar*) "vendor_id",(xmlChar*) fields[1]);
     }
     /* model */
     if( strncmp(fields[0],"model",5) == 0 && strlen(fields[0]) == 5 ){
       if(strncmp(fields[1],"name",4) == 0 && strlen(fields[1]) == 4 ){
         sprintf(outbuf,"%s ",fields[2]);
         for(i=3;i<numfields;i++){
           strcat(outbuf,fields[i]);
           if(i != numfields-1) strcat(outbuf," ");
         }
         processorval = xmlNewChild(processor,NULL,(xmlChar*) "model_name",(xmlChar*) outbuf);
       } else {
         processorval = xmlNewChild(processor,NULL,(xmlChar*) "model_num",(xmlChar*) fields[1]);
       }
     }
     /*
      * cpu
      *
      * This one is a bit tricky, because the cpu cores line only appears
      * on multicores.  We have to 
      */
     if( strncmp(fields[0],"cpu",3) == 0 && strlen(fields[0]) == 3 ){
       if(strncmp(fields[1],"family",6) == 0 && strlen(fields[1]) == 6 ){
         /* cpu family */
         processorval = xmlNewChild(processor,NULL,(xmlChar*) "family",(xmlChar*) fields[2]);
       } else if(strncmp(fields[1],"cores",6) == 0 && strlen(fields[1]) == 5 ){
         /* cpu cores */
	 cores = strtol(fields[2],0,10);
       } else {
         /* cpu clock (we hope) */
         processorval = xmlNewChild(processor,NULL,(xmlChar*) "clock",(xmlChar*) fields[2]);
         xmlSetProp(processorval,(xmlChar*) "units",(xmlChar*) fields[1]);
       }
     }

     /*
      * IF we wanted to parse bogomips, we could, but we're using this
      * as a kludge to know when to output the cores tag, per core.
      */
     if( strncmp(fields[0],"bogomips",8) == 0 && strlen(fields[0]) == 8 ){
       snprintf(fields[2],4,"%d",cores);
       processorval = xmlNewChild(processor,NULL,(xmlChar*) "cores",(xmlChar*) fields[2]);
     }
     
     /* cache size */
     if( strncmp(fields[0],"cache",5) == 0 && strlen(fields[0]) == 5 && 
           strncmp(fields[1],"size",3) == 0 && strlen(fields[1]) == 4 ){
       processorval = xmlNewChild(processor,NULL,(xmlChar*) "cachesize",(xmlChar*) fields[2]);
       xmlSetProp(processorval,(xmlChar*) "units",(xmlChar*) fields[3]);
     }
   }
 }

}

