/*
 * gather.c		- gather and report process info.
 *
 * Michael Hamilton (michael@actrix.gen.nz).
 * Copyright (c) 1995
 *
 * Snarfed and HEAVILY modified from top in process ps
 * by Branko Lankester and Roger Binns.
 *
 */


#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>
#include <pwd.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <termcap.h>
#include <termios.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <ctype.h>
#include <setjmp.h>
#include <stdarg.h>

#include "ps.h"
#include "sysinfo.h"
#include "readproc.h"
#include "whattime.h"
#include "signals.h"

#define MAX_HEIGHT 10.0

static unsigned **memory_info = NULL;

void show_process(proc_t *info, double up_seconds, time_t now)
{
  int pid;
  double cpu_utilisation;	/* cpu_time / total_time */
  double memory_utilisation;
  double resident;
  double total_time;		/* elapsed_time / system_uptime */
  double cpu_ticks;
  double elapsed_ticks;

  if (!info) {
    return ;
  }

  cpu_ticks = (info->utime + info->stime) ;

  /* Start_time is in herz from boot time - dispite what any other comment says. */

				/* Percent of uptime of a processes existance. */
  elapsed_ticks = up_seconds * HZ - info->start_time;

  total_time = MAX_HEIGHT * (elapsed_ticks / (double) HZ) / up_seconds; 

  cpu_utilisation = MAX_HEIGHT * cpu_ticks / elapsed_ticks ;

  memory_utilisation = 
    MAX_HEIGHT * (double) info->size * 4096 / memory_info[meminfo_main][meminfo_used];

  resident = 
    MAX_HEIGHT * (double) info->resident * 4096 / memory_info[meminfo_main][meminfo_used] ;

  printf("% 5d\n%s\n %f %f %f %f\n",
	 info->pid,
	 strcmp(info->user, "root") == 0 ? "{ root }" : info->user,
	 cpu_utilisation,
	 resident,
	 memory_utilisation,
	 total_time
	 ) ;
  
}


/*#####################################################################
 *#######   A readproctable function that uses already allocated  #####
 *#######   table entries.                                        #####
 *#####################################################################
 */
#define Do(x) (flags & PROC_ ## x)

proc_t** readproctab2(int flags, proc_t** tab, ...) {
    PROCTAB* PT = NULL;
    static proc_t *buff;
    int n = 0;
    static int len = 0;
    va_list ap;

    va_start(ap, tab);		/* pass through args to openproc */
    if (Do(UID)) {
	/* temporary variables to ensure that va_arg() instances
	 * are called in the right order
	 */
	uid_t* u;
	int i;

	u = va_arg(ap, uid_t*);
	i = va_arg(ap, int);
	PT = openproc(flags, u, i);
    }
    else if (Do(PID) || Do(TTY) || Do(STAT))
	PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
    else
	PT = openproc(flags);
    va_end(ap);
    buff = (proc_t *) 1;
    while (n<len && buff) {     /* read table: (i) already allocated chunks */
	if (tab[n]->cmdline) {
	    free((void*)*tab[n]->cmdline);
	    tab[n]->cmdline = NULL;
	}
        buff = readproc(PT, tab[n]);
	if (buff) n++;
    }
    if (buff) {
	do {               /* (ii) not yet allocated chunks */
	    tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
	    buff = readproc(PT, NULL);		  /* final null to terminate */
	    if(buff) tab[n]=buff;
	    len++;
	    n++;
	} while (buff);			  /* stop when NULL reached */
	tab[n-1] = xcalloc(NULL, sizeof (proc_t));
	tab[n-1]->pid=-1;		 /* Mark end of Table */
    } else {
	if (n == len) {
	    tab = xrealloc(tab, (n+1)*sizeof(proc_t*));
	    tab[n] = xcalloc(NULL, sizeof (proc_t));
	    tab[n]->cmdline = NULL;
	    len++;
	}
	tab[n]->pid=-1;    /* Use this instead of NULL when not at the end of */
    }                   /* the allocated space */
    closeproc(PT);
    return tab;
}

void show_all_processes ()
{
  double up ;
  double idle ;

  double location ;
  time_t now;

  char host[80] ;

  int count, i;

  static proc_t **p_table = NULL;
  const int proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLTTY|PROC_FILLUSR;

  uptime(&up, &idle) ;

  p_table = readproctab2(proc_flags, p_table, NULL);

  for (count = 0; p_table[count]->pid != -1; count++) {} ;

  if (count == 0) {
    fprintf(stderr, "No processes available\n");
    sleep(1);
  }
  else {
    gethostname(host, 79);
    now = time(NULL);
    printf("Process View: %s %s", host, ctime(&now));
    printf("%d %d CPU RSS Mem Time\n", count, 4) ;

    for (i = 0, location = -(count / 2) ;  i < count ; ++i, ++location) {
      show_process(p_table[i], up, now) ;
    }
  }
  /*freeproctab(p_table);*/
}

void usage(void) 
{
  printf("Usage: gather [-sleep n]\n");
}

int
main(int argc, char **argv)
{
  /* loop, collecting process info and sleeping */
  
  int i, seconds = 2;    

  for (i = 1; i < argc; ++i) {

    if (!strcmp("-sleep", argv[i])) {
      seconds = strtol(argv[++i],NULL,0);

    } else {
      usage();
    }
  }

  setvbuf(stdout,NULL,_IONBF,0);

  memory_info = meminfo();

  for (;;) {
    show_all_processes() ;
    sleep(seconds) ;
  }
}



