/*
 * Uptime
 *
 * This file is public domain.
 *
 * Author: Petri Nordlund <petrin@megabaud.fi>
 *
 * $Id: uptime.c 1.5 1995/10/05 12:31:31 petrin Exp petrin $
 *
 */

/*
 * This is a fairly complete uptime-program. The sourcecode comes from Executive
 * uptime-client, it's just modified to support SysInfo.library. This program
 * can be compiled for multiuser.library support to show the number of users
 * currently logged in. This requires multiuser.library include-files and
 * a stub-library if you are using GCC. See GCC:geninline/README.glue for
 * information about how to generate the stub-library.
 */

#include "defs.h"
#include <proto/SysInfo.h>
#include <libraries/SysInfo.h>

/* PROTOTYPES */
#ifdef USE_MULTIUSER
static void AddUser(ULONG user, UWORD *uids, ULONG *nusers);
ULONG CountTasks(void);
#endif
static void MyExit(void);
static void GetTime(void);
static void Users(void);
static void LoadAverages(void);
static void Uptime(void);


/* EXTERNAL VARIABLES */
extern struct ExecBase *SysBase;


/* VARIABLES */
#ifdef USE_MULTIUSER
struct muBase *muBase = NULL;
#endif
struct SysInfo *si = NULL;
struct Library *SysInfoBase = NULL;


int
main(int argc, char **argv)
{
	/* MyExit will be called when we exit */
	atexit(MyExit);

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

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

#ifdef USE_MULTIUSER
    /* If this fails, we'll just output `1 user' */
	muBase = (struct muBase *) OpenLibrary(MULTIUSERNAME,MULTIUSERVERSION);
#endif

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

	return(RETURN_OK);
}


/*
 * Exit. Free everything.
 */
static void
MyExit(void)
{
#ifdef USE_MULTIUSER
	if(muBase)
		CloseLibrary((struct Library *) muBase);
#endif

	/* Free SysInfo.library */
	if(si)
		FreeSysInfo(si);

	if(SysInfoBase)
		CloseLibrary(SysInfoBase);
}


/*
 * 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;

#ifdef USE_MULTIUSER
	if(muBase)
	{
		ULONG numtasks;
		UWORD *uids;

		Forbid();

		/* 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 Task *task;

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

			nusers = 0;
			AddUser(muGetTaskOwner(FindTask(NULL)), uids, &nusers);
			for(task = (struct Task *)SysBase->TaskReady.lh_Head;
				task->tc_Node.ln_Succ;
				task = (struct Task *)task->tc_Node.ln_Succ)
				AddUser(muGetTaskOwner(task), uids, &nusers);
			for(task = (struct Task *)SysBase->TaskWait.lh_Head;
				task->tc_Node.ln_Succ;
				task = (struct Task *)task->tc_Node.ln_Succ)
				AddUser(muGetTaskOwner(task), uids, &nusers);
			FreeVec(uids);
		}

		Permit();
	}
#endif

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


#ifdef USE_MULTIUSER
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 = 1;
	struct Task *task;

	for (task = (struct Task *)SysBase->TaskReady.lh_Head;
		  task->tc_Node.ln_Succ; task = (struct Task *)task->tc_Node.ln_Succ)
		  i++;
	for (task = (struct Task *)SysBase->TaskWait.lh_Head;
		  task->tc_Node.ln_Succ; task = (struct Task *)task->tc_Node.ln_Succ)
		  i++;
	return(i);
}
#endif


/*
 * Print uptime. We use RAM:-disk creation time.
 */
static void
Uptime(void)
{
	LONG boottime;
	LONG currenttime;
	struct DateTime dtime;
	BPTR lock;
	struct InfoData *infodata;
	struct DeviceList *ramdevice;

/*
 * InfoData-structure must be long word aligned. By allocating it this way,
 * we can make sure it is aligned properly. GCC has a bug in it's aligned-
 * attribute <sigh> so it's not possible to just use: struct InfoData infodata.
 */

	if(!(infodata=AllocMem(sizeof(struct InfoData),MEMF_ANY)))
		return;

	if(lock = Lock("RAM:", SHARED_LOCK))
	{
		if((Info(lock, infodata)) == DOSTRUE)
		{
			ramdevice = BADDR(infodata->id_VolumeNode);

			boottime =  SMult32(ramdevice->dl_VolumeDate.ds_Days, 86400) +
						SMult32(ramdevice->dl_VolumeDate.ds_Minute, 60) +
						SDivMod32(ramdevice->dl_VolumeDate.ds_Tick, TICKS_PER_SECOND);

			DateStamp(&dtime.dat_Stamp);

			currenttime = 	SMult32(dtime.dat_Stamp.ds_Days, 86400) +
							SMult32(dtime.dat_Stamp.ds_Minute, 60) +
							SDivMod32(dtime.dat_Stamp.ds_Tick, TICKS_PER_SECOND);

			currenttime -= boottime;

			if(currenttime > 0)
			{
				ULONG days, hrs, mins, hrs_tmp;

				/* Calculate days, hours and minutes */
				days = currenttime/86400;
				hrs_tmp = hrs = currenttime%86400;
				hrs = hrs/3600;
				mins = (hrs_tmp%3600) / 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);
				}
			}
		}
		UnLock(lock);
	}

	FreeMem(infodata, sizeof(struct InfoData));
}


/*
 * 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 is not supported */
			printf("-");
	}
}
