/* top.c -- top-like program (subroutines for monitor-program 
** Copyright (c) 1991 Jussi Maki. All Rights Reserved.
** NON-COMMERCIAL USE ALLOWED. YOU ARE FREE TO DISTRIBUTE
** THIS PROGRAM AND MODIFY IT AS LONG AS YOU KEEP ORIGINAL
** COPYRIGHTS.
**/
/*
** modified 910710:  changed top_calcsortinfo() to work correctly
** modified 910711:  version 1.04 to 1.04.2
** modified 910715:  version 1.04.2 to 1.04.3 optimized top_calcsortinfo
** modified 910902:  fixed top_calcsortinfo
** modified 911101:  added option for user+system time display
*/

#include <procinfo.h>
#include <curses.h>
#include <pwd.h>

#include "getproc.h"
#include "top.h"


/* memory allocation doesn't really happen until
 * memory is touched so these large arrays don't 
 * waste memory in AIX 3
 */
struct procinfo top_proc1[NPROCS];
struct userinfo top_user1[NPROCS];
struct procinfo top_proc2[NPROCS];
struct userinfo top_user2[NPROCS];
struct procsortinfo top_sortinfo[NPROCS];

/* array of chars which contains process statussymbols */
/* <sys/proc.h> p_stat SNONE,SSLEEP,?,SRUN,SIDL,SZOMB,SSTOP */
char *statch[] = {"?","sleep","?","run","T","zombie","sleep"};


#define SLEEPTIME 2

#ifdef TOP_MAIN

sighandler(signum)
int signum;
{
  switch (signum) {
  case SIGTSTP:
    endwin();
    kill(getpid(),SIGSTOP);
    initscr();
    clear();
    break;
  case SIGINT:
    endwin();
    exit(0);
    break;
  default: fprintf(stderr,"unknown signal %d\n",signal); fflush(stderr);
  }
  signal(signum,sighandler);
}


main()
{
  int result;
  int i;
  int top_nproc1,top_nproc2;
  double top_cpusum;
  int nproctoprint=15;

  signal(SIGTSTP,sighandler);
  signal(SIGINT,sighandler);

  initscr();
  clear();

  top_nproc1 = top_getprocinfo(top_proc1,top_user1);
  sleep(SLEEPTIME);
  while (1) {
    top_nproc2 = top_getprocinfo(top_proc2,top_user2);
    top_cpusum = top_calcsortinfo(top_proc1,top_user1,top_nproc1,
				  top_proc2,top_user2,top_nproc2,top_sortinfo);
    top_print(nproctoprint,top_proc2,top_user2,top_sortinfo,top_nproc2,top_cpusum);
    sleep(SLEEPTIME);

    top_nproc1 = top_getprocinfo(top_proc1,top_user1);
    top_cpusum = top_calcsortinfo(top_proc2,top_user2,top_nproc2,
				  top_proc1,top_user1,top_nproc1,top_sortinfo);
    top_print(nproctoprint,top_proc1,top_user1,top_sortinfo,top_nproc1,top_cpusum);
    sleep(SLEEPTIME);
  }
  endwin();
}

#endif /* TOP_MAIN */

#define SEC2TIME(a) (a)/60,(a)%60

int topflag_usersystemtime=0;

void top_print(topproc,proc,user,procsortinfo,nproc,cpusum)
int topproc;
struct procinfo *proc;
struct userinfo *user;
struct procsortinfo *procsortinfo; 
int nproc;
double cpusum;
{
  int i,j;
  char username[32];
  struct passwd *passwd;

#ifdef TOP_MAIN
  move(2,0);
#endif
  if (!topflag_usersystemtime) {
    printw("   PID USER     PRI NICE   SIZE     RES STAT      TIME   CPU%% COMMAND\n");
    for (j=0; j<min(topproc,nproc); j++) {
      i = procsortinfo[j].index;
      passwd = getpwuid(proc[i].pi_uid);
      strcpy(username,passwd->pw_name);
      printw("%6d %-8s %3d %3d %6dK %6dK %-6s%5d:%02d %5.1f%% %s",
	     proc[i].pi_pid,	   username,
	     proc[i].pi_pri,	   proc[i].pi_nice-20,
	     user[i].ui_tsize/1024+user[i].ui_dvm*4, /* SIZE */
	     (user[i].ui_drss+user[i].ui_trss)*4, /* RES, pagesize is 4kB */
	     statch[proc[i].pi_stat],
	     (int)procsortinfo[j].cputime/60,((int)procsortinfo[j].cputime)%60,
	     procsortinfo[j].deltacputime/cpusum*100.0,
	     user[i].ui_comm);
      if (proc[i].pi_flag&SKPROC) printw(" (kproc)");
      printw("\n");
    }
  } else {
    printw("   PID USER     PRI NICE   SIZE     RES STAT      TIME    USER+SYSTIME  CPU%% COMMAND\n");
    for (j=0; j<min(topproc,nproc); j++) {
      i = procsortinfo[j].index;
      passwd = getpwuid(proc[i].pi_uid);
      strcpy(username,passwd->pw_name);
      printw("%6d %-8s %3d %3d %6dK %6dK %-6s%5d:%02d%5d:%02d%5d:%02d %5.1f%% %s",
	     proc[i].pi_pid,	   username,
	     proc[i].pi_pri,	   proc[i].pi_nice-20,
	     user[i].ui_tsize/1024+user[i].ui_dvm*4, /* SIZE */
	     (user[i].ui_drss+user[i].ui_trss)*4, /* RES, pagesize is 4kB */
	     statch[proc[i].pi_stat],
	     (int)procsortinfo[j].cputime/60,((int)procsortinfo[j].cputime)%60,
	     SEC2TIME(user[i].ui_ru.ru_utime.tv_sec),SEC2TIME(user[i].ui_ru.ru_stime.tv_sec),
	     procsortinfo[j].deltacputime/cpusum*100.0,
	     user[i].ui_comm);
      if (proc[i].pi_flag&SKPROC) printw(" (kproc)");
      printw("\n");
    }
  }
  refresh();
}

/***************************************************************************/

/* sort-order by deltacputime, second key is cputime */
int cmp_deltacputime(a,b)
struct procsortinfo *a,*b;
{
  if (a->deltacputime > b->deltacputime) return (-1);
  else if (a->deltacputime < b->deltacputime) return ( 1);
  else if (a->cputime > b->cputime) return (-1);
  else if (a->cputime < b->cputime) return ( 1);
  else return(0);
}

/* lets assume that procinfo-array is kept in order by PID */
double top_calcsortinfo(proc1,user1,nproc1,proc2,user2,nproc2,procsortinfo)
struct procinfo *proc1; /* older values */
struct userinfo *user1;
int nproc1;
struct procinfo *proc2; /* newer values */
struct userinfo *user2;
int nproc2;
struct procsortinfo *procsortinfo;
{
    int i,j;
    double cpusum=0.0;
    int procmatch;
    struct procinfo *p1,*p2;
    struct userinfo *u1,*u2;
  
    /* lets try finding same process to calculate time process got from cpu */
    p1 = proc1; u1 = user1;
    p2 = proc2; u2 = user2;
    p1[nproc1].pi_pid = 0x7ffffff; /* lets put largest positive long to stop 
				   ** while loop not to go over nproc1 items, 
				   ** and lets hope there are not pid with 
				   ** that number */
    p2[nproc2].pi_pid = 0;         /* and something smaller here */
    for (i=0; i<nproc2; i++) { 
	while (p1->pi_pid < p2->pi_pid) {p1++;u1++;}
	procmatch = (p1->pi_pid == p2->pi_pid);
	
	if (p2->pi_stat == SZOMB) { /* if process is zombie we don't trust the information */
	    procsortinfo[i].cputime = procsortinfo[i].deltacputime = 0.0;
	} else {
	    procsortinfo[i].cputime =
	      u2->ui_ru.ru_utime.tv_sec
		+ u2->ui_ru.ru_utime.tv_usec*1.0e-6
		  + u2->ui_ru.ru_stime.tv_sec
		    + u2->ui_ru.ru_stime.tv_usec*1.0e-6;
	    procsortinfo[i].deltacputime = procsortinfo[i].cputime;
	    if (procmatch) { 
		/* lets watch out for processes which have identical pid 
		 ** but which are not same process between two samples 
		 */
		if (u1->ui_start == u2->ui_start)
		  procsortinfo[i].deltacputime -=  
		    (u1->ui_ru.ru_utime.tv_sec
		     + u1->ui_ru.ru_utime.tv_usec*1.0e-6
		     + u1->ui_ru.ru_stime.tv_sec
		     + u1->ui_ru.ru_stime.tv_usec*1.0e-6);
	    }
	}
	procsortinfo[i].index = i;
	cpusum += procsortinfo[i].deltacputime;
	p2++;u2++;
    }
    qsort(procsortinfo,nproc2,sizeof(struct procsortinfo),cmp_deltacputime);
    return(cpusum);
}

int top_getprocinfo(procinfo,userinfo)
struct procinfo *procinfo;
struct userinfo *userinfo;
{
  int nproc;
  char *swappername = "swapper";
  int i;

  nproc = getproc(procinfo,NPROCS,sizeof(struct procinfo));
  for (i=0; i<nproc; i++) {
    getuser(&procinfo[i],sizeof(struct procinfo),
	    &userinfo[i],sizeof(struct userinfo));
  }
  strcpy(userinfo[0].ui_comm,swappername); /* first process is always pid 0 */
  return(nproc);
}
