/* Copyright (C) 2001 Beau Kuiper

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "ftpd.h"

#define FALSE 0
#define TRUE !FALSE

int signumber;

void sighandler(int signum)
{
	signumber = signum;
}

int openfifo(char *fifoname)
{
	struct stat inf;
	
	if (stat(fifoname, &inf) == -1)
	{
		/* try creating it */
		
		if (mknod(fifoname, S_IFIFO | 0600, 0) == -1)
			return(-1);
	}
	else
	{
		/* make sure it is a fifo */
		
		if (!S_ISFIFO(inf.st_mode))
		{
			errno = EEXIST;
			return(-1);
		}
	}
	
	return(open(fifoname, O_RDONLY));
}

int reopenlog(char *logfile, int *logfd)
{
	int newlogfd;
	
	newlogfd = open(logfile, O_APPEND | O_CREAT | O_WRONLY, 0600);
	if (newlogfd != -1)
	{
		close(*logfd);
		*logfd = newlogfd;
	}
	return(newlogfd != -1);
}

char *reloadlogfile(char *fconfig)
{
	CONFIGFILECACHE *cfiledata;
	int line, error, section;
	char *newlogfile;

	cfiledata = loadconfigcache(fconfig, &line, &error);
	
	if (cfiledata == NULL)
		return(NULL);
		
	section = getsectionid(cfiledata, "logger");
	if (section == -1)
		goto error;
	
	loadstrfromconfig(cfiledata, section, "logfile",
			  &newlogfile, NULL);
	if (!newlogfile)
		goto error;
	if (newlogfile[0] != '/')
		goto error;
	newlogfile = strdupwrapper(newlogfile);

	/* free config info */

	freeconfigcache(cfiledata);
	return(newlogfile);
	
error:
	freeconfigcache(cfiledata);
	return(NULL);
}


void usage(char *name)
{
	printf("mudlogd, handles muddleftpd logging and rotation.\n\n");
	printf("Usage: %s [-V][-h][-c configfile]\n\n", name);
	printf("	-V		Show version information.\n");
	printf("	-h		Show usage information.\n");
	printf("	-c configfile	Specify config file mudlogd to run using.\n\n");
	exit(1);
}

int main(int argc, char **argv)
{
	CONFIGFILECACHE *cfiledata;
	struct sigaction sig1act;
	char *fconfig = NULL;
	char *fifoname = NULL;
	char *logoutname = NULL;
	int section, line, error;
	int ch, f;
	int outfile, infile;
	extern char *optarg;
	char *newlogfile = NULL;
	
	while((ch = getopt(argc, argv, "Vc:h")) != EOF)
	{
		switch(ch)
		{
		case 'V':
			showversion("mudlogd");
			break;
		case 'c':
			fconfig = optarg;
			break;
		case 'h':
		default:
			usage(argv[0]);
		}
		
	}			

	if (fconfig == NULL)
		fconfig = CONFIGFILE;

	cfiledata = loadconfigcache(fconfig, &line, &error);
	
	/* obtain scratchfile name */
	if (cfiledata == NULL)
		ERRORMSGFATAL(safe_snprintf("Could not load line %d of config file: %s", line, config_errorstr(error)));
	section = getsectionid(cfiledata, "logger");
	if (section == -1)
		ERRORMSGFATAL("Could not find logger section in config file");

	loadstrfromconfig(cfiledata, section, "fifoname",
			  &fifoname, NULL);
	if (!fifoname)
		ERRORMSGFATAL("fifoname not specified in config file");
	if (fifoname[0] != '/')
		ERRORMSGFATAL("fifoname is not a valid absolute filename");
	fifoname = strdupwrapper(fifoname);
	
	loadstrfromconfig(cfiledata, section, "logfile",
			  &logoutname, NULL);
	if (!logoutname)
		ERRORMSGFATAL("logfile not specified in config file");
	if (logoutname[0] != '/')
		ERRORMSGFATAL("logfile is not a valid absolute filename");
	logoutname = strdupwrapper(logoutname);

	/* free config info */
	
	freeconfigcache(cfiledata);
	
	/* open the log file, make sure it works before returning to the user */
	
	outfile = open(logoutname, O_APPEND | O_CREAT | O_WRONLY, 0600);
	if (outfile == -1)
		ERRORMSGFATAL(safe_snprintf("could not open logfile (%s)", strerror(errno)));

	f = fork();
	
	if (f == -1)
		ERRORMSGFATAL("could not fork()");
	else if (f == 0)
	{
		int len, doquit, schedrotate;
		char data[4096];
		char lastch = '\n';

		setsid();
		signumber = 0;
		
		sig1act.sa_handler = sighandler;
		sig1act.sa_flags = 0;
		sigaction(SIGUSR1, &sig1act, NULL);

		sig1act.sa_handler = sighandler;
		sig1act.sa_flags = 0;
		sigaction(SIGHUP, &sig1act, NULL);
		
		while(1)
		{
			schedrotate = FALSE;

			/* reopen the fifo channel, ignoring any signals for now */
			do
			{
				infile = openfifo(fifoname);
				if ((infile == -1) && (errno == EINTR))
					schedrotate = TRUE; 
				signumber = 0;
			} 
			while ((infile == -1) && (errno == EINTR));
			
			if (infile == -1)
				ERRORMSGFATAL(safe_snprintf("could not open fifo file (%s)", strerror(errno)));
		
			doquit = FALSE;
			while(!doquit)
			{
				if (schedrotate && (lastch == '\n'))
				{
					if (newlogfile)
					{
						if (reopenlog(newlogfile, &outfile))
						{
							freewrapper(logoutname);
							logoutname = newlogfile;				
						}
						else
							freewrapper(newlogfile);
						newlogfile = NULL;
					}
					else
						reopenlog(logoutname, &outfile);
					schedrotate = FALSE;
				}
				errno = 0;
				len = read(infile, data, 4096);
				
				if (signumber != 0)
				{
					if (signumber == SIGUSR1)
					{
						if (lastch == '\n')
							reopenlog(logoutname, &outfile);
						else	
							schedrotate = TRUE;	
					} 
					else if (signumber == SIGHUP)
					{
						/* load config again */
						
						newlogfile = reloadlogfile(fconfig);
						if (newlogfile)
						{
							if (lastch == '\n')
							{
								if (reopenlog(newlogfile, &outfile))
								{
									freewrapper(logoutname);
									logoutname = newlogfile;				
								}
								else
									freewrapper(newlogfile);
								newlogfile = NULL;
							}
							else
								schedrotate = TRUE;
						}
					}
					signumber = 0;
				}

				if (len > 0)
				{
					lastch = data[len-1];
					len = write(outfile, data, len);
				}				
				if ((len <= 0) && (errno != EINTR))
					doquit = TRUE;
			}
			close(infile);
		}
	}
	/* not reached but shuts the compiler up */
	exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1