/*
**	$RCSfile: Uptime.c,v $
**	$Filename: Uptime.c $
**	$Revision: 1.0 $
**	$Date: $
**
**	Modified by Etienne Vogt from the SysInfo.library example by Petri Nordlund
**	This file is public domain.
*/

/*
 * This is a fairly complete uptime-program, based on the example provided
 * with the original Executive-based SysInfo.library, which has been modified
 * for use with Sysmon. 
 * It supports multiuser.library support to show the number of users currently
 * logged in.
 */

#include <exec/memory.h>
#include <exec/execbase.h>
#include <dos/datetime.h>
#include <devices/timer.h>
#define __USE_SYSBASE
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/timer.h>
#include <proto/multiuser.h>
#include <libraries/multiuser.h>
#include "SysInfo.h"
#include "SysInfo_protos.h"
#include "SysInfo_pragmas.h"
#include "/sysmon.h"
#include "/sysmon_protos.h"
#include "/sysmon_pragmas.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>


/* PROTOTYPES */
int main(int argc, char **argv);
static void cleanexit(ULONG rc);
static void AddUser(ULONG user, UWORD *uids, ULONG *nusers);
static ULONG CountTasks(void);
static void GetTime(void);
static void Users(void);
static void LoadAverages(void);
static void Uptime(void);
static ULONG EClockDivide(struct EClockVal *numerator, ULONG denominator);


/* VARIABLES */
struct muBase *muBase = NULL;
struct SysInfo *si = NULL;
struct Library *SysInfoBase = NULL;
extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;
struct SysmonBase *SysmonBase = NULL;
struct Device *TimerBase = NULL;
struct timerequest myTimeReq;
  
static UBYTE version[] = "$VER: Uptime 1.0 (3.8.2001)";

int main(int argc, char **argv)
{
  if ((SysmonBase = (struct SysmonBase *)OpenLibrary("sysmon.library",1)) == NULL)
  { puts("Can't open sysmon.library.");
    cleanexit(RETURN_FAIL);
  }

  if (!(SysInfoBase = OpenLibrary(SYSINFONAME, SYSINFOVERSION)))
  { puts("Can't open SysInfo.library.");
    cleanexit(RETURN_FAIL);
  }

  /* Initialize SysInfo.library, this will make the connection to the
   * server-process and allocate the SysInfo-structure. */
  if (!(si = InitSysInfo()))
  { puts("Can't initialize SysInfo.");
    cleanexit(RETURN_FAIL);
  }

  if (OpenDevice(TIMERNAME, UNIT_ECLOCK, (struct IORequest *)&myTimeReq, 0L))
  { printf("Can't open %s\n", TIMERNAME);
    cleanexit(RETURN_FAIL);
  }
  else TimerBase = myTimeReq.tr_node.io_Device;

  /* If this fails, we'll just output "1 user" */
  muBase = (struct muBase *) OpenLibrary(MULTIUSERNAME, MULTIUSERVERSION);

  GetTime();
  printf(", ");
  Uptime();
  printf(", ");
  Users();
  printf(", ");
  LoadAverages();
  printf("\n");

  cleanexit(RETURN_OK);
}


/*
 * Exit. Free everything.
 */
static void cleanexit(ULONG rc)
{
  if (muBase) CloseLibrary((struct Library *)muBase);
  if (TimerBase) CloseDevice((struct IORequest *)&myTimeReq);

  /* Free SysInfo.library */
  if (si) FreeSysInfo(si);
  if (SysInfoBase) CloseLibrary(SysInfoBase);
  if (SysmonBase) CloseLibrary((struct Library *)SysmonBase);
  exit(rc);
}


/*
 * Print system time
 */
static void GetTime(void)
{
  struct DateTime dtime;
  char timestr[LEN_DATSTRING+1];

  DateStamp(&dtime.dat_Stamp);

  dtime.dat_Format = FORMAT_DOS;
  dtime.dat_Flags = 0;
  dtime.dat_StrDay = NULL;
  dtime.dat_StrDate = NULL;
  dtime.dat_StrTime = timestr;

  DateToStr(&dtime);

  printf(timestr);
}


/*
 * Print number of users (from Multiuser if available)
 */
static void Users(void)
{
  ULONG nusers = 1;

  if(muBase)
  { ULONG numtasks;
    UWORD *uids;

    smLockTaskTable(LTTF_READ);

  /* Count the number of tasks currently in system */
    numtasks = CountTasks();

  /* Allocate memory for each task */
    if (uids=AllocVec(numtasks*sizeof(UWORD),MEMF_CLEAR|MEMF_PUBLIC))
    { struct TaskInfo *tinfo = NULL;

    /* Find out how many different uids there are */

      nusers = 0;
      while (tinfo = smNextTaskInfo(tinfo)) AddUser(muGetTaskOwner(tinfo->ti_Task), uids, &nusers);
      FreeVec(uids);
    }

    smUnLockTaskTable(LTTF_READ);
  }

  if ((nusers>1) || (nusers==0)) printf("%d users", nusers);
  else printf("%d user",nusers);
}


static void AddUser(ULONG user, UWORD *uids, ULONG *nusers)
{
  UWORD uid;
  BOOL found = FALSE;
  ULONG i;

  if (user)
  { uid = user>>16;
    for (i=0; !found && (i<*nusers); i++) found = (uids[i] == uid);
    if (!found) uids[(*nusers)++] = uid;
  }
}

static ULONG CountTasks(void)
{ ULONG i = 0;
  struct TaskInfo *tinfo = NULL;

  while (tinfo = smNextTaskInfo(tinfo)) i++;

  return i;
}


/*
 * Print uptime from timer.device EClock.
 */
static void Uptime(void)
{ struct EClockVal UpTime;
  ULONG EClockRate;
  ULONG days, hrs, mins;

  EClockRate = ReadEClock(&UpTime);
  
  mins = EClockDivide(&UpTime, 60*EClockRate);
  days = mins / (24*60);
  mins %= 24*60;
  hrs = mins / 60;
  mins %= 60;

  if (days || hrs || mins) printf("up ");

  if (days > 0)
  { if (days > 1) printf("%d days ", days);
    else printf("%d day ", days);
  }

  if (hrs > 0) printf("%d:%02d", hrs, mins);
  else
  { if ((mins > 1) || (mins == 0)) printf("%d mins", mins);
    else printf("%d min", mins);
  }
}

static ULONG EClockDivide(struct EClockVal *numerator, ULONG denominator)
{ int bits;
  ULONG d1,d2,d3;

  d1 = 0x0;
  d2 = numerator->ev_hi;
  d3 = numerator->ev_lo;

  for (bits = 0; bits < 64; bits++)
  { /* now left-shift d1,d2,d3 */
    d1 <<= 1;
    if (d2 & 0x80000000) d1 |= 0x1;
    d2 <<= 1;
    if (d3 & 0x80000000) d2 |= 0x1;
    d3 <<= 1;

    if (d1 >= denominator) 
    { d1 -= denominator;
      d3 |= 0x1;
    }
  }

  /* fill in the remainder */
  numerator->ev_lo = d1;

  /* return the quotient, or the low part of it, at least */
  return d3;
}


/*
 * Print load averages
 */
static void LoadAverages(void)
{ struct SI_LoadAverage load;	  /* This will be filled by GetLoadAverage() */

  GetLoadAverage(si, &load);	  /* Ask SysInfo.library for current load averages */

  printf("load:");

  switch (si->loadavg_type)
  { case LOADAVG_FIXEDPNT:	 /* Convert fixed point values to floating point values */
      if (si->loadavg_time1) printf(" %.2f",(float) load.lavg_fixed.load1 / (float) si->fscale);
      else printf(" N/A");

      if (si->loadavg_time2) printf(" %.2f",(float) load.lavg_fixed.load2 / (float) si->fscale);
      else printf(" N/A");

      if (si->loadavg_time3) printf(" %.2f",(float) load.lavg_fixed.load3 / (float) si->fscale);
      else printf(" N/A");

      break;

    default:	  		/* Load average type is not supported */
      printf("-");
  }
}
