/* logd.c */


/*
** Log daemon for multiuser. The logd program should be run in the background
** on bootup. When a message is to be appended to the log file, the log
** program will pass the message to the logd program, and logd will add the
** message to the log file.
** This program should be set with the following permissions :
**      "mprotect logd urewd GROUP re OTHER re" and should be owned by root.
** Note the U in the permissions, this is the setuid field, with this set,
** the logd program will run with root access rights. With this, the logd
** program has access to the multiuser.log file.
**		The reason this program is needed is because the log file should not be
**	writeable to everyone, otherwise anyone could overwrite the logfile deleting
**	it's contents. So, the program which writes to the logfile needs the setuid
** bit (u bit) set. But, with this bit set, the current task will be run with
** the uid of root, and so it is impossible to find out who really is running
** the program. That is why I have another program without the u bit set, which
** passes the UserID of the task-owner to this logd program.

**    © 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 DEBUG
*/

#define PORTNAME	"muLog"		/* Port to communicate with the log program */
#define PORTPRI	0				/* Priority of the port */

#define REPLYPORTNAME	"muLogReply"

#define MULTIUSER_MINVERSION	39
#define FILENAMESIZE				256	/* In trouble if filenames ar longer than this */

#define QUITMESSAGE	"quit"	/* If the logd program receives a message with
										** this string, then it must terminate */
#define STRSIZE	256

/* ANSI includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* AMIGA includes */
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/nodes.h>
#include <exec/types.h>
#include <proto/exec.h>

/* Declare message ports and structures */
struct MsgPort *logMsgPort;

struct logMessage
{
	struct Message SystemMsg;		/* System part of message */
	char string[STRSIZE];			/* String to append to the log */
	char logFileName[FILENAMESIZE];	/* Filename of the multiuser log */
};

struct logMessage *logMsg;

void cleanUp( STRPTR text );		/* Print string, and exit */
void main(void);
void shutDown(void);					/* Terminate the logd program */

/* ------------------------
** main()	Keep the daemon running until it receives a signal to quit.
**				This signal is sent by running the logd program a second time.
**--------------------------*/

void main()
{

	FILE *logFileHandle;
	char logFileName[FILENAMESIZE];
	BOOL running = TRUE;
		
	if (FindPort( PORTNAME )) {
	/* logd already running, so terminate it */
#ifdef DEBUG
puts("logd already running, so signal it to quit.");
#endif
		shutDown();
		cleanUp( "logd signaled to quit." );
	}

	/* Create message port */
	logMsgPort = (struct MsgPort *) CreatePort( PORTNAME, 0 );
	if( !logMsgPort )
		cleanUp( "Could not open message port." );


	while (running)
	{

		/* Wait for a message to arrive */
		WaitPort( logMsgPort );

		/* Now one or more messages have arrived, try to collect */
		/* then all                                              */

		while( logMsg = (struct logMessage *) GetMsg( logMsgPort ))
		{
			/* Read the message */

			/* If logd is signaled to quit */
			if (!strcmp(logMsg->string, QUITMESSAGE))
				running = FALSE;
			else	/* Make copy of message, dont leave client process hang while
						we access the disk to write to the log file */
				strcpy(logFileName, logMsg->logFileName);

			/* Reply to message */
			ReplyMsg( (struct Message *)logMsg );

			if (running) {
				/* Write log message to the log file */
		 		logFileHandle = fopen(logFileName, "a");
				if (!logFileHandle)
					puts("Cant open log file.");
				else {
					fprintf(logFileHandle, logMsg->string);
				/* Close, else other programs cant get exclusive lock on log file */
					fclose(logFileHandle);
				}
			}

		}
		/* Got all the messages */

	}

	cleanUp( "logd terminating." );

}

/*------------------------------
** shutDown()	logd is already running, so terminate first the other logd
**					process, then this one.
**------------------------------*/

void shutDown()
{

	struct logMessage *logMsg;	/* Message to send to other logd process */
	struct MsgPort *replyMsgPort, *logMsgPort;

	logMsgPort = FindPort( PORTNAME );
	/* If the port doesnt exist, we shouldnt be in the shutDown function */
#ifdef DEBUG
	if (!logMsgPort)
		cleanUp("ERROR, could not find port, should not be in shutDown");
#endif

	/* 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!" );


	/* 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!" );

	/* NOTE: If any more cleanUp() calls are inserted bellow this point, make
		sure to remove the replyMsgPort in cleanUp */

	/* 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, QUITMESSAGE);

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

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

	DeletePort(replyMsgPort);

}



void cleanUp( STRPTR text )
{
	/* If we have successfully created a port, close it */ 
	if( logMsgPort )
		DeletePort( logMsgPort);

	if (strcmp(text, ""))
		printf( "%s\n", text );

	/* Dont bother setting the return field */
	exit( 0 );

}

