#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/tasks.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <devices/timer.h>
#include <proto/all.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int tolower(int);

#define NAMESIZE	256
#define VARSIZE		160
#define EXTVARS		50

long	__stack = 4000;
char	*__procname = "SysGhost";
long	__priority = 5;
long	__BackGroundIO = 0;

extern void (*taskaddaddr)(void);

extern void __stdargs InstallAddTask(void);
extern void __stdargs UnInstallAddTask(void);


char   *vers="\0$VER: AX-Sysghost 1.34 (1.1.95)";
char	portname[13]="AX-Sysghost",
		aika[18],
		uptimestr[40];
int		oldpri,
		secs = 0,
		mins = 0,
		hours= 0,
		days = 0;
__aligned struct DateStamp v;


#define	TIME15	900
#define	TIME5	300
#define	TIME1	60

BOOL	timeropen = FALSE;
BOOL	keepgoing = TRUE, time_requested = FALSE;

extern	struct ExecBase	*SysBase;
struct	MsgPort *timerport = NULL;
struct	timerequest *timermsg = NULL;
struct	MsgPort *myport = NULL;

/* sysghost commands */
#define COM_ADD		'a'		/* add IO user tty */
#define COM_REMOVE	'r'		/* remove IO */
#define COM_UPDATE	'u'		/* update IO what (obsolete) */
#define COM_WRITE	'w'		/* write IO user text */
#define COM_WHO		'l'		/* who IO buffer maxlen */
#define COM_QUIT	'q'		/* quit */
#define COM_SET		's'		/* set IO env value */
#define COM_SETSYS	'y'		/* setsys IO sys value */
#define COM_UNSET	'x'		/* unset IO env */
#define COM_GET		'g'		/* get IO env/sys value */
#define COM_GETN	'h'		/* get IO n value */

/* New for 1.31 */
#define COM_READ	'd'		/* read IO buffer maxlen */
#define COM_KICK	'k'		/* kick IO user  */
#define COM_TIME	't'		/* time IO limit */
#define COM_SHUTDOWN 'X'	/* <no arguments> */

struct msg
{
	struct Node node;
	long size;
	char message[0];
};

struct	whonode
{
	struct Node node;

	char *What;
	long IO;			/* (BPTR)(FileHandle *) */
	long Idle;
	long Elapsed;
	long Limit;
	struct Task *LastCaller;

	struct List messages;

	long Kicked;
	char User[16];
	char Tty[10];
	char When[16];
	char Accesslevel[40];
	char Realname[80];
	char Home[80];
	char Host[80];
	char cpu[10];
	char dosv[4];
	char Envs[EXTVARS][VARSIZE];
};

struct List wholist;
int Users=0, count=0;	/* Number of users */

struct mymsg
{
	struct Message Msg;
	UBYTE Action;
	UBYTE Return;
	LONG  arg1;
	char *arg2;
	char *arg3;
};

void cleanexit(int error);
void converttime(long sum, int count, register char *position);
void convert_datestamp(char *, struct DateStamp *);
void openthings(void);
void addtimerequest(long secs, long micros);
int  update_memory(char *buffer, long maxlen);
void lowercase(char *, char *);


static int mysetmode(BPTR out, LONG flags)
{
	struct MsgPort *port,*rp;
	struct StandardPacket sp;

	if(!(port=(struct MsgPort *)(((struct FileHandle *)(out<<2))->fh_Type)))
		return 0;
	if(!(rp=CreatePort(0,0)))
		return 0;
	sp.sp_Pkt.dp_Type = ACTION_SCREEN_MODE;
	sp.sp_Msg.mn_Node.ln_Name = (char *)&sp.sp_Pkt;
	sp.sp_Pkt.dp_Link = &sp.sp_Msg;
	sp.sp_Pkt.dp_Port = rp;
	sp.sp_Pkt.dp_Arg1 = flags;

	PutMsg(port, &sp.sp_Msg);
	WaitPort(rp);
	GetMsg(rp);
	DeletePort(rp);
	return -1;
}


void cleanexit(int error)
{
	struct Node *a;
	struct Message *message;
	struct msg *msg;

	Forbid();
	if(myport)
	{
		while(message = GetMsg(myport))
			ReplyMsg(message);
		DeletePort(myport);

		if(taskaddaddr)
		{
			UnInstallAddTask();
		}
	}
	Permit();

	if(timeropen)
	{
		if(time_requested)
		{
			AbortIO((struct IORequest *)timermsg);
			WaitIO((struct IORequest *)timermsg);
		}
		CloseDevice((struct IORequest *)timermsg);
	}
	if(timermsg)
		DeleteExtIO((struct IORequest *)timermsg);
	if(timerport)
		DeletePort(timerport);

	while(a = RemHead(&wholist))
	{
		while(msg = (struct msg *)RemHead(&((struct whonode *)a)->messages))
		{
			FreeMem(msg, msg->size);
		}
		FreeMem((UBYTE *)a, sizeof(struct whonode));
	}

	SetTaskPri(FindTask(NULL), oldpri);
	XCEXIT(error);
}

void openthings(void)
{
	Forbid();
	if(!FindPort(portname))
	{
		myport = CreatePort(portname, 0);
		Permit();
	}
	else
	{
		Permit();
		cleanexit(11);
	}

	if(!(timerport = CreatePort(0, 0)))
		cleanexit(22);
	if(!(timermsg = (struct timerequest *) CreateExtIO(timerport, sizeof(struct timerequest))))
		cleanexit(22);
	if(OpenDevice("timer.device", UNIT_VBLANK, ((struct IORequest *) timermsg), 0))
		cleanexit(22);
	timeropen = TRUE;

	InstallAddTask();

	oldpri = SetTaskPri(FindTask(NULL), 2);
}

void converttime(long sum, int count, register char *position)
{
	long temp;

	temp = (sum*100L+50L)/count;
	*(position+4) = '0'+(int)(temp%10);
	temp/=10;
	*(position+3) = '0'+(int)(temp%10);
	temp/=10;
	*(position+1) = '0'+(int)(temp%10);
	temp/=10;
	if(temp)
		*position = '0'+(int)(temp%10);
	else
		*position = ' ';
}

void convert_datestamp(char *a, struct DateStamp *v)
{
	char months[13][4]={"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
	int dpm[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	unsigned int year=1978, day, month=1, hours, minutes;

	hours=v->ds_Minute/60;
	minutes=v->ds_Minute%60;
	day=v->ds_Days;

	while(day>=((year%4==0)+365))
	{
		day-=365+(year%4==0);
		year++;
	}
	while(day>=(dpm[month-1]+(year%4==0 && month==2)))
	{
		day-=dpm[month-1]+(year%4==0 && month==2);
		month++;
	}
	day++;

	strcpy(a, "xx:xx DD-MMM-YY");
	a[6]=day/10+48;
	a[7]=day%10+48;
	a[9]=months[month][0];
	a[10]=months[month][1];
	a[11]=months[month][2];
	a[13]=(year/10)%10+48;
	a[14]=year%10+48;

	a[0]=hours/10+48;
	a[1]=hours%10+48;
	a[3]=minutes/10+48;
	a[4]=minutes%10+48;
}

void addtimerequest(long secs, long micros)
{
	time_requested = TRUE;
	timermsg->tr_node.io_Command = TR_ADDREQUEST;
	timermsg->tr_time.tv_secs = secs;
	timermsg->tr_time.tv_micro = micros;
	SendIO((struct IORequest *)timermsg);
}

void __stdargs _main(char *arg)
{
	FILE	*fp;
	ULONG	timersignal, portsignal, signals;
	int		pos = 0,
			pos15 = 0,
			pos5 = TIME15-TIME5,
			pos1 = TIME15-TIME1;
	long	sum15 = 0L,
			sum5 = 0L,
			sum1 = 0L;
	char	temp[NAMESIZE], varname[NAMESIZE];
	static	int last[TIME15];
	struct	mymsg *message;
	struct  msg *msg;
	struct	whonode *mynode, *yournode;
	struct  Node *node;
	int length, i, j;

	NewList(&wholist);
	openthings();

	((struct Process *)SysBase->ThisTask)->pr_WindowPtr = (APTR)-1L;
	timersignal = (1L<<timerport->mp_SigBit);
	portsignal = (1L<<myport->mp_SigBit);
	addtimerequest(1, 0);
	strcpy(uptimestr, "##.## ##.## ##.##");
    for(i=0;i<TIME15;i++)
		last[i] = 0;

	while(1)
	{
		signals = Wait(timersignal | portsignal | SIGBREAKF_CTRL_C);
		if(signals & timersignal)
		{
			GetMsg(timerport);
			addtimerequest(1, 0);

			length = 0;
			Forbid();
			for(node = (SysBase->TaskReady).lh_Head;node = node->ln_Succ;length++);
			Permit();
			sum15 += (length-last[pos15]);
			sum5  += (length-last[pos5]);
			sum1  += (length-last[pos1]);
			last[pos] = length;
			if(++pos15==TIME15)
				pos15 = 0;
			if(++pos5==TIME15)
				pos5 = 0;
			if(++pos1==TIME15)
				pos1 = 0;
			if(++pos==TIME15)
				pos = 0;
			converttime(sum1, TIME1, uptimestr);
			converttime(sum5, TIME5, uptimestr+6);
			converttime(sum15, TIME15, uptimestr+12);

			if(secs==0)
			{
				mynode = (struct whonode *)wholist.lh_Head;
				while(mynode->node.ln_Succ)
				{
					mynode->Idle++;
					mynode->Elapsed++;

					if(mynode->Elapsed == mynode->Limit-10)
					{
						Write(mynode->IO, "\a\nYou have 10 minutes left!\n", 28);
					}
					if(mynode->Elapsed == mynode->Limit-5)
					{
						Write(mynode->IO, "\a\nYou have 5 minutes left!\n", 27);
					}
					if(mynode->Limit && mynode->Elapsed > mynode->Limit)
					{
						if(!mynode->Kicked)
						{
							j = sizeof(struct msg) + 24;

							if(msg = (struct msg *)AllocMem(j, MEMF_CLEAR))
							{
								sprintf(msg->message, "Your time limit is up!\n");
								msg->node.ln_Name = &msg->message[0];
								msg->size = j;
								AddTail(&mynode->messages, &msg->node);
							}
							mynode->Kicked = 1;
						}
						/* Must use my own setmode()!! */
						mysetmode(mynode->IO, 4);
						if(mynode->LastCaller->tc_Node.ln_Type == NT_PROCESS)
							Signal(mynode->LastCaller, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
					}
					mynode=(struct whonode *)mynode->node.ln_Succ;
				}
			}

			if(++secs>59)
			{
				secs = 0;
				if(++mins>59)
				{
					mins = 0;
					if(++hours>23)
					{
						hours = 0;
						++days;
					}
				}
			}
		}

		if((signals & SIGBREAKF_CTRL_C) && !Users)
			break;

		if(signals & portsignal)
		{
			while(message = (struct mymsg *)GetMsg(myport))
			{
				if(message->Action==COM_SHUTDOWN)
				{
					yournode = (struct whonode *)wholist.lh_Head;
					while(yournode->node.ln_Succ)
					{
						Write(yournode->IO, "\a\nEmergency shutdown!! Booting..\n", 33);

						if(!yournode->Kicked)
						{
							DateStamp(&v);
							convert_datestamp(aika, &v);
							j = sizeof(struct msg) + 16 + strlen(aika);

							if(msg = (struct msg *)AllocMem(j, MEMF_CLEAR))
							{
								sprintf(msg->message, "Kicked out at %s\n", aika);
								msg->node.ln_Name = &msg->message[0];
								msg->size = j;
								AddTail(&yournode->messages, &msg->node);
							}
							yournode->Kicked = 1;
						}
						/* Must use my own setmode()!! */
						mysetmode(yournode->IO, 4);
						if(yournode->LastCaller->tc_Node.ln_Type == NT_PROCESS)
							Signal(yournode->LastCaller, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);

						yournode = (struct whonode *)yournode->node.ln_Succ;
					}
					ReplyMsg((struct Message *)message);
					continue;
				}

				mynode = (struct whonode *)wholist.lh_Head;
				while(mynode->node.ln_Succ)
				{
					if(mynode->IO==message->arg1)
					{
						if(message->Action != COM_READ)
						{
							/*  Do not clear when COM_READ, because it
								is done even when AXsh is idle
							 */
							mynode->Idle=0;	/* idle is cleared */
						}
						break;
					}
					mynode = (struct whonode *)mynode->node.ln_Succ;
				}

				if(message->Action==COM_QUIT)	/* quit */
				{
					if(!Users)
					{
						ReplyMsg((struct Message *)message);
						cleanexit(0);
					}
				}
				else
				if(message->Action==COM_WHO)	/* who IO buffer maxlen */
				{
					message->Return=(UBYTE)update_memory((char *)message->arg2, (long)message->arg3);
				}
				else
				if(message->Action==COM_ADD)	/* add IO user tty */
				{
					if(mynode = (struct whonode *)AllocMem(sizeof(struct whonode), MEMF_CLEAR))
					{
						NewList(&mynode->messages);
						mynode->IO = message->arg1;
						lowercase(mynode->User, message->arg2);
						strcpy(mynode->Tty, message->arg3);

						DateStamp(&v);
						convert_datestamp(aika, &v);

						strcpy(mynode->When, aika);
						mynode->What = "-AXsh";
						mynode->Idle = 0;
						mynode->LastCaller = (struct Task *)message->Msg.mn_ReplyPort->mp_SigTask;
						mynode->Kicked = 0;
						mynode->Limit = 0;
						mynode->Envs[0][0] = '\0';

						AddTail(&wholist, &mynode->node);
						Users++;

						if(fp=fopen("AXsh:etc/Last", "a"))
						{
							fprintf(fp, "%-16s%s %s\n", message->arg2, message->arg3, aika);
							fclose(fp);
						}
						message->arg3 = (char *)&mynode->What;
						message->Return=0;
					}
					else
					{
						message->Return=1;
					}
				}
				else
				if(mynode->node.ln_Succ)
				{
					mynode->LastCaller = (struct Task *)message->Msg.mn_ReplyPort->mp_SigTask;
					if(message->Action==COM_REMOVE) /* remove IO */
					{
						Remove(&mynode->node);
						while(msg = (struct msg *)RemHead(&mynode->messages))
						{
							FreeMem(msg, msg->size);
						}
						FreeMem(mynode, sizeof(struct whonode));
						Users--;
					}
					else if(message->Action==COM_UPDATE) /* update IO what */
					{
						mynode->What=message->arg2;
						mynode->Idle=0;
					}
					else if(message->Action==COM_TIME)	/* time IO limit */
					{
						mynode->Limit = (long)message->arg2;
					}
					else if(message->Action==COM_KICK)	/* kick IO user [tty] */
					{
						lowercase(temp, message->arg2);

						message->Return=2;
						yournode=(struct whonode *)wholist.lh_Head;
						while(yournode->node.ln_Succ)
						{
							if(!strncmp(yournode->User, temp, strlen(temp)))
							{
								if(!yournode->Kicked)
								{
									DateStamp(&v);
									convert_datestamp(aika, &v);
									j = sizeof(struct msg) + 20 +
										strlen(mynode->User) + 
										strlen(aika);

									if(msg = (struct msg *)AllocMem(j, MEMF_CLEAR))
									{
										sprintf(msg->message, "Kicked out by %s at %s\n", mynode->User, aika);
										msg->node.ln_Name = &msg->message[0];
										msg->size = j;
										AddTail(&yournode->messages, &msg->node);
										yournode->Kicked = 1;
										message->Return = 0;
									}
									else
										message->Return=1;
								}
								/* Must use my own setmode()!! */
								mysetmode(yournode->IO, 4);
								if(yournode->LastCaller->tc_Node.ln_Type == NT_PROCESS)
									Signal(yournode->LastCaller, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
								break;
							}
							yournode=(struct whonode *)yournode->node.ln_Succ;
						}
					}
					else if(message->Action==COM_WRITE) /* write IO user text */
					{
						if(message->arg2)
						{
							lowercase(temp, message->arg2);
						}
						else
						{
							strcpy(temp, "root");
						}

						yournode=(struct whonode *)wholist.lh_Head;
						while(yournode->node.ln_Succ && strncmp(yournode->User, temp, strlen(temp)))
							yournode=(struct whonode *)yournode->node.ln_Succ;

						if(yournode->node.ln_Succ)
						{
							DateStamp(&v);
							convert_datestamp(aika, &v);
							j = sizeof(struct msg) + 21 +
								strlen(mynode->User) + 
								strlen(aika);
							if(message->arg3)
								j += strlen(message->arg3);

							if(msg = (struct msg *)AllocMem(j, MEMF_CLEAR))
							{
								if(message->arg3)
								{
									sprintf(msg->message,"Message from %s at %s:\n%s\n", mynode->User, aika, message->arg3);
								}
								else
								{
									sprintf(msg->message,"Message from %s at %s:\n\n", mynode->User, aika);
								}
								msg->node.ln_Name = &msg->message[0];
								msg->size = j;
								AddTail(&yournode->messages, &msg->node);
								message->Return=0;
							}
							else
								message->Return=1;
						}
						else
							message->Return=2;
					}
					else if(message->Action==COM_READ)
					{
						if(msg = (struct msg *)RemHead(&mynode->messages))
						{
							strncpy((char *)message->arg2, msg->node.ln_Name, (long)message->arg3);
							message->Return = 1;
							FreeMem(msg, msg->size);
						}
						else
						{
							message->Return = 0;
							if(mynode->Kicked)
							{
								message->Return = 2;
							}
						}
					}
					else if(message->Action==COM_GETN)
					{
						j=message->Return;
						switch(j)
						{
						case 0:
							strcpy(message->arg2, "user");
							strcpy(message->arg3, mynode->User);
							break;
						case 1:
							strcpy(message->arg2, "tty");
							strcpy(message->arg3, mynode->Tty);
							break;
						case 2:
							strcpy(message->arg2, "accesslevel");
							strcpy(message->arg3, mynode->Accesslevel);
							break;
						case 3:
							strcpy(message->arg2, "realname");
							strcpy(message->arg3, mynode->Realname);
							break;
						case 4:
							strcpy(message->arg2, "home");
							strcpy(message->arg3, mynode->Home);
							break;
						case 5:
							strcpy(message->arg2, "host");
							strcpy(message->arg3, mynode->Host);
							break;
						case 6:
							strcpy(message->arg2, "cpu");
							strcpy(message->arg3, mynode->cpu);
							break;
						case 7:
							strcpy(message->arg2, "dosv");
							strcpy(message->arg3, mynode->dosv);
							break;
						case 8:
							strcpy(message->arg2,  "intime");
							sprintf(message->arg3, "%ld", mynode->Elapsed);
							break;
						default:
							strcpy(message->arg2, mynode->Envs[j-9]);
							strcpy(message->arg3, mynode->Envs[j-9]+strlen(mynode->Envs[j-9])+1);
						}
					}
					else
					{
						j=-1;
						lowercase(varname, message->arg2);
						if(!strcmp(varname, "user"))	j=0;
						else if(!strcmp(varname, "tty"))	j=1;
						else if(!strcmp(varname, "accesslevel"))	j=2;
						else if(!strcmp(varname, "realname"))	j=3;
						else if(!strcmp(varname, "home"))	j=4;
						else if(!strcmp(varname, "host"))	j=5;
						else if(!strcmp(varname, "cpu"))	j=6;
						else if(!strcmp(varname, "dosv"))	j=7;
						else if(!strcmp(varname, "intime"))	j=8;

						if(message->Action==COM_SET)	/* set IO name value */
						{
							message->Return=0;
							if(j==-1)
							{
								j=0;
								while(1)
								{
									strncpy(temp, mynode->Envs[j], NAMESIZE);
									lowercase(temp, temp);
									if(!temp[0] || !strcmp(temp, varname))
										break;
									j++;
								}
								if(temp[0])
								{
									strcpy(mynode->Envs[j]+strlen(mynode->Envs[j])+1, message->arg3);
								}
								else
								{
									if(j<EXTVARS-1)
									{
										mynode->Envs[j+1][0]=0;
										strcpy(mynode->Envs[j], message->arg2);
										strcpy(mynode->Envs[j]+strlen(message->arg2)+1, message->arg3);
									}
									else
										message->Return=1;
								}
							}
							else
								message->Return=1;
						}
						else if(message->Action==COM_SETSYS)	/* setsys IO name value */
						{
							message->Return=0;
							switch(j)
							{
							case 0:
								strcpy(mynode->User, message->arg3);
								break;
							case 1:
								strcpy(mynode->Tty, message->arg3);
								break;
							case 2:
								strcpy(mynode->Accesslevel, message->arg3);
								break;
							case 3:
								strcpy(mynode->Realname, message->arg3);
								break;
							case 4:
								strcpy(mynode->Home, message->arg3);
								break;
							case 5:
								strcpy(mynode->Host, message->arg3);
								break;
							case 6:
								strcpy(mynode->cpu, message->arg3);
								break;
							case 7:
								strcpy(mynode->dosv, message->arg3);
								break;
							case 8:
								mynode->Elapsed=atol(message->arg3);
								break;
							default:
								message->Return=1;
								break;
							}
						}
						else if(message->Action==COM_UNSET)	/* unset IO name */
						{
							message->Return=1;
							if(j==-1)
							{
								j=0;
								while(1)
								{
									strncpy(temp, mynode->Envs[j], NAMESIZE);
									lowercase(temp, temp);
									if(!temp[0] || !strcmp(temp, varname))
									{
										message->Return=0;
										break;
									}
									j++;
								}
								if(temp[0])
								{
									while(mynode->Envs[j][0])
									{
										movmem(&mynode->Envs[j+1], &mynode->Envs[j], VARSIZE);
										j++;
									}
								}
							}
						}
						else if(message->Action==COM_GET)	/* get IO name value */
						{
							message->Return=0;
							switch(j)
							{
							case 0:
								strcpy(message->arg3, mynode->User);
								break;
							case 1:
								strcpy(message->arg3, mynode->Tty);
								break;
							case 2:
								strcpy(message->arg3, mynode->Accesslevel);
								break;
							case 3:
								strcpy(message->arg3, mynode->Realname);
								break;
							case 4:
								strcpy(message->arg3, mynode->Home);
								break;
							case 5:
								strcpy(message->arg3, mynode->Host);
								break;
							case 6:
								strcpy(message->arg3, mynode->cpu);
								break;
							case 7:
								strcpy(message->arg3, mynode->dosv);
								break;
							case 8:
								sprintf(message->arg3, "%ld", mynode->Elapsed);
								break;

							default:
								j=0;
								while(1)
								{
									strncpy(temp, mynode->Envs[j], NAMESIZE);
									lowercase(temp, temp);
									if(!temp[0] || !strcmp(temp, varname))
										break;
									j++;
								}
								if(temp[0])
									strcpy(message->arg3, mynode->Envs[j]+strlen(mynode->Envs[j])+1);
								else
									message->Return=1;
							}
						}
					}
				}
				else
				{
					/* Kick from non-logged-in user */
					if(message->Action==COM_KICK)	/* kick IO user [tty] */
					{
						lowercase(temp, message->arg2);

						message->Return=2;
						yournode=(struct whonode *)wholist.lh_Head;
						while(yournode->node.ln_Succ)
						{
							if(!strncmp(yournode->User, temp, strlen(temp)))
							{
								if(!yournode->Kicked)
								{
									DateStamp(&v);
									convert_datestamp(aika, &v);
									j = sizeof(struct msg) + 24 +
										strlen(aika);

									if(msg = (struct msg *)AllocMem(j, MEMF_CLEAR))
									{
										sprintf(msg->message, "Kicked out by root at %s\n", aika);
										msg->node.ln_Name = &msg->message[0];
										msg->size = j;
										AddTail(&yournode->messages, &msg->node);
										yournode->Kicked = 1;
										message->Return = 0;
									}
									else
										message->Return=1;
								}
								/* Must use my own setmode()!! */
								mysetmode(yournode->IO, 4);
								if(yournode->LastCaller->tc_Node.ln_Type == NT_PROCESS)
									Signal(yournode->LastCaller, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
								break;
							}
							yournode=(struct whonode *)yournode->node.ln_Succ;
						}
					}
					else
						message->Return = 1;	/* Unknown message */
				}
				ReplyMsg((struct Message *)message);
			}
		}
	}
	cleanexit(0);
}


int update_memory(char *buffer, long maxlen)
{
	long apu, pos;
	char idle[6], temp[NAMESIZE];
	struct Node *mynode;

	DateStamp(&v);
	convert_datestamp(aika, &v);

	sprintf(temp, "%s  up %02d days, %02d:%02d,  %d users,  load average: %s\n", aika, days, hours, mins, Users, uptimestr);
	if((apu = strlen(temp)) < maxlen)
	{
		strcpy(buffer, temp);
		pos = apu;
	}
	else
		return -1;

	sprintf(temp, "User            Term   Login time      Idle  What");
	if(pos + (apu = strlen(temp)) < maxlen)
	{
		strcpy(buffer+pos, temp);
		pos += apu;
	}
	else
		return -1;

	mynode = wholist.lh_Head;
	while(mynode->ln_Succ)
	{
		if(apu = ((struct whonode *)mynode)->Idle)
		{
			if(apu < 1440)
			{
				sprintf(idle, "%2d:00", (apu/60)%24);
				idle[4] += (apu%60)%10;
				idle[3] += (apu%60)/10;
			}
			else
			{
				strcpy(idle, "0 day");
				idle[0] += apu/1440;
			}
		}
		else
			idle[0]=0;
		sprintf(temp, "\n%-16s%-7s%-16s%5s ",
			((struct whonode *)mynode)->User,
			((struct whonode *)mynode)->Tty,
			((struct whonode *)mynode)->When, idle);
		strncpy(temp+46, ((struct whonode *)mynode)->What, 34);
		temp[79] = '\0';

		if(pos + (apu = strlen(temp)) < maxlen)
		{
			strcpy(buffer+pos, temp);
			pos += apu;
		}
		mynode = mynode->ln_Succ;
	}
	if(pos+1 < maxlen)
	{
		strcpy(buffer+pos, "\n");
	}
	return 0;
}


void lowercase(char *a, char *b)
{
	while(*a++ = tolower(*b))
		b++;
}
