/* log.c */

/*
**		This is the second program with logd. This program acts as the client,
**		(served by logd). This program should be set with permissions :
**					mprotect log rewd GROUP re OTHER re
**		With the U bit not set, this process can check with the multiuser.library
**		to see who the task owner is. The owner, and the current date and argument
**		string passed with argv are all passwd in a message to the logd program
**		which MUST be running in order for the program to work (otherwise it will
**		exit with an error message).

**    © 1995 Chris Mc Carthy (No rights reserved :-) ).
**    cmccarth@icl.rtc-cork.ie
**    cmccarth@dullahan.rtc-cork.ie
**    atoz@nether.net
**    bg966@freenet.carleton.ca
*/

#define MULTIUSER_MINVERSION	39
#define DOS_MINVERSION			36

#define PORTNAME			"muLog"			/* Port to communicate with logd process */
#define REPLYPORTNAME	"muLogReply"
#define PORTPRI			0
#define REPLYPORTPRI		5		/* A reply will be called soon after this port is
										** created, so this priority should be > 0 */

#define FILENAMESIZE		256
#define STRSIZE			256

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/nodes.h>
#include <exec/types.h>

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

#include <libraries/multiuser.h>
#include <libraries/dos.h>

struct MsgPort *logMsgPort, *replyMsgPort;

/* The message will be in this form: */
struct logMessage
{
	struct Message SystemMsg;	/* System part of the message */
	char string[STRSIZE];
	char logFileName[STRSIZE];

};

/* Declare a pointer to the message */
struct logMessage *logMsg, *msg;


/* Declare the functions: */
void cleanUp( STRPTR text );
void sendMsg(char string[STRSIZE], char logFileName[FILENAMESIZE]);

/* Library bases */
struct muBase *muBase;
struct DosBase *DosBase;

/* main()	-------------------------------------------- */
/* Get message userid, and time, then pass these to logd */
/* ----------------------------------------------------- */
main(int argc, char *argv[])
{
	struct muUserInfo *userInfo;

	char buf[STRSIZE];
	char userMsg[STRSIZE];
	char dateBuf[STRSIZE];

   BPTR configDirLock;			/* Lock on multiuser config dir */
	char logFileName[FILENAMESIZE];
	struct tm *tp;
	long uid;
	int i;
	long t;

	/* Open multiuser library */
	if (!(muBase = (struct muBase *)OpenLibrary(MULTIUSERNAME, MULTIUSER_MINVERSION))) {
		printf("Need %d of %s.\n", MULTIUSERNAME, MULTIUSER_MINVERSION);
		exit(ERROR_INVALID_RESIDENT_LIBRARY);
	
	}
	/* Open Dos library */
	if (!(DosBase = (struct DosBase *)OpenLibrary(DOSNAME, DOS_MINVERSION))) {
		printf("Need %d of %s.\n", DOSNAME, DOS_MINVERSION);
		CloseLibrary((struct Library *)muBase);
		exit(ERROR_INVALID_RESIDENT_LIBRARY);
	}

	/* Get uid (through away gid) */
	uid = muGetTaskOwner(NULL);
	uid >>= 16;

	userInfo = muAllocUserInfo();
	if (!userInfo)
		cleanUp("Out of memory");

	/* find UserID from uid */
	userInfo->uid = uid;
	if (!muGetUserInfo(userInfo, muKeyType_uid))
		cleanUp("Could not get user id");

	/* Get date and time */
	time(&t);
	tp = localtime(&t);

	/* Read the command line args */
	strcpy(userMsg, "");
	for ( i = 1; i < argc; i++)
	{
		strcat(userMsg, argv[i]);
		strcat(userMsg, " ");
	}
	strcat(userMsg, "\0");

	/* Format the time string to match standard multiuser log */
	strftime(dateBuf, STRSIZE, "%d-%b-%y, %H:%M:%S:", tp);
	/* Format the rest of the string to match standard multiuser log */
	sprintf(buf, "%s logd: \'%s\' %s\n", dateBuf, userInfo->UserID, userMsg);

	muFreeUserInfo(userInfo);


	/* Get lock on config directory */
   configDirLock = muGetConfigDirLock();

	/* Get name of config directory */
	if (!NameFromLock(configDirLock, logFileName, FILENAMESIZE)) {
		puts("Cannot get lock on config directory");
		exit(EXIT_FAILURE);
	}

	/* Append the log filename to get the absolute path of the log file */
	strcat(logFileName, "/");
	strcat(logFileName, muLog_FileName);

	/* Release lock on config dir */
	UnLock(configDirLock);

	/* Closedown libraries */
	CloseLibrary((struct Library *)muBase);


	sendMsg(buf, logFileName);
	return 0;

}

/* Send the supplied string to the log daemon.
*/

void sendMsg(char string[STRSIZE], char logFileName[FILENAMESIZE])
{
	/* Allocate memory for the message */
	/* (Make it public and clear it.)   */
	logMsg = (struct logMessage *)
	AllocMem( sizeof( struct logMessage ), MEMF_PUBLIC|MEMF_CLEAR );

	/* Check if we have allocated the memory successfully: */
	if( !logMsg )
		cleanUp( "Not enough memory for message!" );

	/* Try to find the message port PORTNAME */
	logMsgPort = (struct MsgPort *) FindPort( PORTNAME );

	/* Have we found the message port? */
	if( !logMsgPort )
		cleanUp( "logd not running." );

	/* Create a reply port: (When the other task replies */
	/* we will receive a message at this port).          */
	replyMsgPort = (struct MsgPort *) CreatePort( REPLYPORTNAME, 0 );

	/* Have we received a reply port? */
	if( !replyMsgPort )
		cleanUp( "Could not create the reply port!" );


	/* Initialise the message */
	logMsg->SystemMsg.mn_Node.ln_Type = NT_MESSAGE;

	/* Give the message a pointer to our reply port */
	logMsg->SystemMsg.mn_ReplyPort = replyMsgPort;

	/* Set the length of the message */
	logMsg->SystemMsg.mn_Length = sizeof( struct logMessage );

	/* Message data */
	strcpy(logMsg->string, string);
	strcpy(logMsg->logFileName, logFileName);

	/* Send the message */  
	PutMsg( logMsgPort, (struct Message *)logMsg );

	/* Wait at our reply port */
	WaitPort( replyMsgPort );

	/* The End! */
	cleanUp( "" );

}

/* ----------------------------------------*/
/* Output message, free ports etc and exit */
/* ----------------------------------------*/

void cleanUp( STRPTR text )
{
	/* Deallocate the message memory */
	if( logMsg )
		FreeMem( logMsg, sizeof( *logMsg ) );

	/* If we have we successfully created a reply */
	/* port, close it                             */
	if( replyMsgPort )
		DeletePort( replyMsgPort);

	/* Print the message, if any */
	if (strcmp(text, ""))
		printf( "%s\n", text );

	/* Dont bother setting the exit code */
	exit( 0 );

}

