/*
**	bootlog.c - record reason/date/time of system boot
**	Copyright © 1995 Michael Letowski
*/

#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/datetime.h>
#include <dos/rdargs.h>
#include <support/types.h>
#include <support/exec.h>
#include <support/dos.h>

#include <string.h>

#include <proto/exec.h>
#include <proto/dos.h>

#include "bootlog.rev.h"

#define DOS_NAME		"dos.library"
#define DOS_VERN		37L

#define TEMPLATE		"LOGFILE,QUIET/S"

/* Boot reasons */
#define BOOT_ALERT		0
#define BOOT_POWER		1
#define BOOT_RESET		2
#define BOOT_COUNT		3

/* Defaults */
#define DEF_LOGFILE			"DEVS:Logs/Boot.log"
#define LOG_FORMAT			"%s: %s %s %s\n"

#define PStr(arg,def)		(*(arg)=='\0' ? (def) : (arg))

STATIC CONST TEXT VersionString[]=
	VERSION(PROG_NAME,PROG_VERSION,PROG_REVISION,PROG_DATE);

struct Options
{
	STRPTR  opt_LogFile;
	LONG    opt_Quiet;
};	/* Options */

struct FormatData
{
	STRPTR fd_Reason;
	STRPTR fd_Day;
	STRPTR fd_Date;
	STRPTR fd_Time;
};	/* FormatData */

STATIC struct MemList *FindKickMem(STRPTR name);
STATIC struct MemList *AddKickMem(STRPTR name);

LONG BootLog(VOID)
{
	STATIC CONST STRPTR BootReasons[BOOT_COUNT]=
	{
		"Alert","Power","Reset"
	};	/* BootReasons */

	struct ExecBase *SysBase=*((struct ExecBase **)4);
	struct DosLibrary *DOSBase;

	CHAR StrDay[LEN_DATSTRING], StrDate[LEN_DATSTRING], StrTime[LEN_DATSTRING];

	struct Options Opts;
	struct DateTime DT;
	struct FormatData Data;
	struct RDArgs *Args;
	STRPTR LogFile;
	BPTR FH;
	LONG Boot,RC=RETURN_FAIL;

	/* Open DOS */
	unless(DOSBase=(struct DosLibrary *)OpenLibrary(DOS_NAME,DOS_VERN))
		throw2(SetResult2(ERROR_INVALID_RESIDENT_LIBRARY),	NO_DOS);

	/* Read arguments */
	clear(&Opts);																	/* Clear out buffer */
	unless(Args=ReadArgs(TEMPLATE,(LONG *)&Opts,NULL))
		throw2(PrintFault(IoErr(),PROG_NAME),	NO_ARGS);
	LogFile=Opts.opt_LogFile ? Opts.opt_LogFile : (STRPTR)DEF_LOGFILE;

	RC=RETURN_ERROR;															/* Working a bit */

	/* Find cause of reboot */
	Forbid();
	if(FindKickMem(PROJ_NAME))										/* Tag found */
		Boot=BOOT_RESET;														/* So this is reset */
	else																					/* Tag not found */
		AddKickMem(PROJ_NAME),	Boot=BOOT_POWER;		/* Add it - this power on */
	if(SysBase->LastAlert[1])											/* Guru? */
		Boot=BOOT_ALERT;														/* This is alert */
	Permit();

	/* Set up date conversion struct */
	DateStamp(&DT.dat_Stamp);											/* Get current date & time */
	DT.dat_Format=FORMAT_DOS;											/* ADOS default format */
	DT.dat_Flags=DTF_FUTURE;											/* Mark 'future' events */
	DT.dat_StrDay=StrDay;													/* Set up string buffers */
	DT.dat_StrDate=StrDate;
	DT.dat_StrTime=StrTime;
	/* Convert date */
	unless(DateToStr(&DT))												/* Conversion unsuccessfull */
		throw2(PrintFault(IoErr(),PROG_NAME),	NO_DATE);

	/* Set up data for formatting */
	Data.fd_Reason=BootReasons[Boot];							/* Set up reason */
	Data.fd_Day=DT.dat_StrDay;										/* And dates */
	Data.fd_Date=DT.dat_StrDate;
	Data.fd_Time=DT.dat_StrTime;
	
	if(FH=Open(LogFile,MODE_READWRITE))						/* Open log file for appending */
	{
		if(Seek(FH,0,OFFSET_END)>=0)								/* Seeked to end */
			if(VFPrintf(FH,LOG_FORMAT,&Data)>=0)			/* FPrintf successfull */
				RC=RETURN_OK;														/* Success */
			else																			/* FPrintf unsuccessfull */
				PrintFault(IoErr(),PROG_NAME);					/* Inform user */
		else																				/* Seek unsuccessfull */
			PrintFault(IoErr(),PROG_NAME);						/* Infor user */
		Close(FH);																	/* Close file */
	}
	else																					/* Couldn't open file */
		PrintFault(IoErr(),PROG_NAME);							/* Inform user */

	unless(Opts.opt_Quiet)												/* 'Loud' mode */
		VPrintf(LOG_FORMAT,&Data);									/* Print to stdout, too */

	catch(NO_DATE,	);
	catch(NO_ARGS,	FreeArgs(Args));
	catch(NO_DOS,		CloseLibrary((struct Library *)DOSBase));
	return(RC);
}	/* BootLog */

STATIC struct MemList *FindKickMem(STRPTR name)
{
	struct ExecBase *SysBase=*((struct ExecBase **)4);
	struct MemList *Curr;

	Curr=SysBase->KickMemPtr;
	while(Curr && strcmp(Curr->ml_Node.ln_Name,name))
		Curr=Succ(&Curr->ml_Node);
	return(Curr);
}	/* FindKickMem */

STATIC struct MemList *AddKickMem(STRPTR name)
{
	struct ExecBase *SysBase=*((struct ExecBase **)4);
	struct MemList *ML;
	struct Node *Node;
	ULONG Size=sizeof(struct MemList)+strlen(name)+1;

	if(ML=AllocMem(Size,MEMF_KICK | MEMF_CLEAR))	/* Get Kick-able memory */
	{
		/* Initialize */
		ML->ml_Node.ln_Type=NT_KICKMEM;
		ML->ml_Node.ln_Name=(STRPTR)ML+sizeof(struct MemList);
		strcpy(ML->ml_Node.ln_Name,name);
		ML->ml_NumEntries=1;
		ML->ml_ME[0].me_Addr=ML;
		ML->ml_ME[0].me_Length=Size;
		/* Link into exec */
		if(Node=(ML->ml_Node.ln_Succ=SysBase->KickMemPtr))
			Node->ln_Pred=&ML->ml_Node;
		SysBase->KickMemPtr=ML;
		SysBase->KickCheckSum=(APTR)SumKickData();
		CacheClearU();															/* As documented in AutoDocs */
	}
	return(ML);
}	/* AddKickMem */
