/*
 * proc.c:
 * interface to process table entry in /proc
 *
 * Copyright (C) 1997, 1998 by Raju Mathur (raju@sgi.com)
 *
 * See file COPYING (included in this distribution) for copyright information.
 */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include "common.h"
/*
 * memory (in Kbytes)
 */
int
   sysmem = 0 ,
   kernelmem = 0 ,
   freemem = 0 ,
   buffermem = 0 ;
/*
 * Set values for various memory usages
 */
static void
SetMemInfo ( void )
{
   struct stat
      s ;
   char
      buf [128] ;
   FILE
      *meminfo ;
   int
      totalmem ;
   const char
      *MemLine = "Mem:" ;
   const int
      MemLineLen = strlen ( MemLine ) ;
/*
 * Cheap way to get amount of installed mem in the computer -- get size of
 * /proc/kcore.
 */
   if ( stat ( "/proc/kcore" , &s ) < 0 )
   {
      fprintf ( stderr , "%s: cannot stat /proc/kcore" , progname ) ;
      perror ( "" ) ;
      exit ( 1 ) ;
   }
   sysmem = s . st_size ; /* in Kilobytes */
/*
 * Read /proc/meminfo to get usable memory and buffers. Presumably buffers
 * should be considered a part of the kernel memory.
 */
   if ( ( meminfo = fopen ( "/proc/meminfo" , "r" ) ) == NULL )
   {
      fprintf ( stderr , "%s: cannot open /proc/meminfo" , progname ) ;
      perror ( "" ) ;
      exit ( 1 ) ;
   }
   while ( fgets ( buf , sizeof ( buf ) , meminfo ) )
   {
      if ( !strncmp ( buf , MemLine , MemLineLen ) )
      {
	 /* Mem: total used free shared buffers cached */
	 sscanf ( buf , "%*s %d %*d %d %*d %d" , &totalmem , &freemem ,
		  &buffermem ) ;
	 break ;
      }
   }
   sysmem /= 1024 ;
   totalmem /= 1024 ;
   freemem /= 1024 ;
   buffermem /= 1024 ;
   kernelmem = sysmem - totalmem ;
   fclose ( meminfo ) ;
   return ;
}
/*
 * Read /proc to get info about processes.
 * As a side effect :-) also set variables related to main memory.
 */
void
makeProcs ( void )
{
   DIR
      *proc ;
   struct dirent
      *procdir ;
   FILE
      *StatusFile ;
   char
      buf [128] ;
   char
      procName [14] ;
   int
      procSize ,
      procRSS ,
      procData ,
      procStk ,
      procExe ;
   const char
      *NameLine = "Name:" ,
      *VmSizeLine = "VmSize:" ,
      *VmRSSLine = "VmRSS" ,
      *VmDataLine = "VmData" ,
      *VmStkLine = "VmStk" ,
      *VmExeLine = "VmExe" ;
   const int
      NameLineLen = strlen ( NameLine ) ,
      VmSizeLineLen = strlen ( VmSizeLine ) ,
      VmDataLineLen = strlen ( VmDataLine ) ,
      VmStkLineLen = strlen ( VmStkLine ) ,
      VmExeLineLen = strlen ( VmExeLine ) ,
      VmRSSLineLen = strlen ( VmRSSLine ) ;

   if ( ( proc = opendir ( "/proc" ) ) == NULL )
   {
      fprintf ( stderr , "%s: unable to open /proc" , progname ) ;
      perror ( "" ) ;
      exit ( 1 ) ;
   }
   ClearProcs () ;
/*
 * Get memory info
 */
   SetMemInfo () ;
/*
 * Make sure that the first process is the kernel. We'll be slightly off,
 * but the alternatives (adding this after the rest of the process list
 * has been made) are too terrible to contemplate.
 */
   addProc ( kernelname , kernelmem + buffermem , kernelmem + buffermem ) ;
/*
 * NASTY HACK!
 * Some screwy feature (or my screwy understanding of /proc) causes the
 * sum(RSS) for all the procs + kernel + buffers + freemem to become > system
 * memory. Hence we recalculate system memory based on the actual figures
 * we get from sum(RSS)+kernel+buffers+freemem
 */
   sysmem = 0 ;
   while ( procdir = readdir ( proc ) )
   {
      if ( !index ( "1234567890" , procdir -> d_name [0] ) )
      {
	 continue ;
      }
      sprintf ( buf , "/proc/%s/status" , procdir -> d_name ) ;
      if ( ( StatusFile = fopen ( buf , "r" ) ) == NULL )
      {
	 fprintf ( stderr , "%s: cannot open %s for reading" ,
		   progname , buf ) ;
	 perror ( "" ) ;
	 continue ;
      }
      procRSS = procSize = procData = procStk = procExe = 0 ;
      while ( fgets ( buf , sizeof ( buf ) , StatusFile ) )
      {
/*
 * I hate sscanf's, but am too lazy to replace them with a more elegant,
 * more efficient method right now. Don't you just love TODO lists?
 */
	 if ( !strncmp ( buf , NameLine , NameLineLen ) )
	 {
	    /* Name: procName */
	    sscanf ( buf , "%*s %s" , procName ) ;
	 }
	 else if ( !strncmp ( buf , VmSizeLine , VmSizeLineLen ) )
	 {
	    /* VmSize: procSize kB */
	    sscanf ( buf , "%*s %d" , &procSize ) ;
	 }
	 else if ( !strncmp ( buf , VmRSSLine , VmRSSLineLen ) )
	 {
	    /* VmRSS: procRSS kB */
	    sscanf ( buf , "%*s %d" , &procRSS ) ;
	 }
	 else if ( !strncmp ( buf , VmDataLine , VmDataLineLen ) )
	 {
	    /* VmData: procData kB */
	    sscanf ( buf , "%*s %d" , &procData ) ;
	 }
	 else if ( !strncmp ( buf , VmStkLine , VmStkLineLen ) )
	 {
	    /* VmStk: procStk kB */
	    sscanf ( buf , "%*s %d" , &procStk ) ;
	 }
	 else if ( !strncmp ( buf , VmExeLine , VmExeLineLen ) )
	 {
	    /* VmExe: procExe kB */
	    sscanf ( buf , "%*s %d" , &procExe ) ;
	 }
      }
      fclose ( StatusFile ) ;
      addProc ( procName , procSize , procData + procStk + procExe ) ;
      sysmem += procData + procStk + procExe ;
   }
   closedir ( proc ) ;
   sysmem += kernelmem + buffermem + freemem ;
   addProc ( freename , freemem , freemem ) ;
   return ;
}	/* makeProcs */

#ifdef DEBUG
void
printProcs ( void )
{
   struct ProcInfo
      *pi ,
      *procs ;
   int
      nprocs ,
      nprocs2 ,
      totmem ;
   register int
      i ;
   printf ( "%-20s%8s%8s%8s\n" , "Name" , "Memory" , "RSS" , "Number" ) ;
   totmem = nprocs2 = 0 ;
   procs = AllProcs ( &nprocs ) ;
   for ( i = 0 ; i < nprocs ; i++ )
   {
      pi = &procs[i] ;
      printf ( "%-20s%8d%8d%8d\n" , pi -> procname , pi -> totMem ,
	       pi -> totRSS , pi -> nProcs ) ;
      nprocs2 += pi -> nProcs ;
      if ( strcmp ( pi -> procname , kernelname ) )
      {
	 totmem += pi -> totRSS ;
      }
   }
   printf ( "%d procs\n" , nprocs ) ;
   printf ( "Mem: %d procs, %d total, %d kernel, %d buffers, %d free\n" ,
	    totmem , sysmem , kernelmem , buffermem , freemem ) ;
}
char *progname ;
void
main ( int argc , char **argv )
{
   register int i = 0 ;
   progname = *argv ;

   while ( 1 )
   {
      makeProcs () ;
      printProcs () ;
      printf ( "\n" ) ;
      sleep ( 5 ) ;
   }
}
#endif /* DEBUG */
