/*******************************************************************************
 *	Main.cc		Tms process
 *			T.Barnaby,	BEAM Ltd,	2007-02-07
 *******************************************************************************
 */

#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <main.h>
#include <Control.h>
#include <BDebug.h>
#include <ucontext.h>
#include <Debug.h>

#define		COREDUMP	0

typedef void (*SigFunction)(int, siginfo_t*, void*);

Config		config;
int		realTime;

void sigCrash(int sig, siginfo_t* sigInfo, void* context){
	ucontext_t*	ucontext = (ucontext_t*)context;
	char		strBuf[1024];
	time_t		t;
	BDebugBacktrace	bt;

	t = time(0);
	sprintf(strBuf, "TmsServer: Version %s crashed with signal %d at location: 0x%x accessing 0x%p on %s",
		VERSION, sig, ucontext->uc_mcontext.gregs[REG_EIP], sigInfo->si_addr, ctime(&t));

	bt.dumpBacktraceSyslog(strBuf);
	bt.dumpBacktraceStdout(strBuf);
	bt.dumpBacktraceFile("/var/log/tmsServerCrash", strBuf);
	bt.dumpBacktraceFile("/var/log/tmsServer.log", strBuf);
	_exit(1);
}

void signalSet(int sigNumber, SigFunction sigFunction){
	struct sigaction	sigAction;
	
	sigAction.sa_sigaction = sigFunction;
	sigemptyset(&sigAction.sa_mask);
	sigAction.sa_flags = SA_SIGINFO;
	sigAction.sa_restorer = 0;

	sigaction(sigNumber, &sigAction, 0);
}

void usage(){
	fprintf(stderr, "Usage: tmsServer [-c <filename> -f -d <n>]\n");
	fprintf(stderr, "	-c <filename> : Configuration file to use\n");
	fprintf(stderr, "	-f            : Run in the foreground\n");
	fprintf(stderr, "	-d <n>        : Debug level\n");
}

int main(int argc, char** argv){
	BError		err;
	Control*	control;
	int		a;
	BString		s;
	int		db = 0;
	int		dbFixed = 0;
	int		foreground = 0;
	sigset_t	sigm;
	struct rlimit	rlim;
	BString		configFile;

	// Allow process to lock 500M of available memory
	rlim.rlim_max = 500*1024*1024;
	rlim.rlim_cur = rlim.rlim_max;
	if(setrlimit(RLIMIT_MEMLOCK, &rlim))
		perror("Unable to enable memory locking");

#if COREDUMP
	rlim.rlim_max = 1024*1024*1024;
	rlim.rlim_cur = rlim.rlim_max;
	if(setrlimit(RLIMIT_CORE, &rlim))
		perror("Unable to enable core dump");
	printf("Set to core dump\n");
#endif

	// Relinqish root ownership
	if(geteuid() == 0){
		realTime = 1;
		seteuid(getuid());
		setegid(getgid());
	}

	// Lock in all of the pages of this application
	if(mlockall(MCL_CURRENT | MCL_FUTURE) < 0)
		fprintf(stderr, "Warning: unable to lock in memory pages\n");

	// Make sure master thread ignores termination signals sent to the other threads
	// Also ignore file size signal, the write function will return an error instead.
	sigemptyset(&sigm);
	sigaddset(&sigm, SIGUSR1);
	sigaddset(&sigm, SIGXFSZ);
	sigaddset(&sigm, SIGPIPE);
	sigprocmask(SIG_BLOCK, &sigm, 0);
	
	// Setup signals for program crash
	signalSet(SIGSEGV, sigCrash);
	signalSet(SIGHUP, sigCrash);
	signalSet(SIGILL, sigCrash);
	signalSet(SIGABRT, sigCrash);
	signalSet(SIGFPE, sigCrash);
	signalSet(SIGBUS, sigCrash);

	openlog("tmsServer", LOG_CONS, LOG_DAEMON);
	
	for(a = 1; a < argc; a++){
		if(argv[a][0] == '-'){
			switch(argv[a][1]){
			case 'c':
				configFile = argv[++a];
				break;
			case 'f':
				foreground = 1;
				break;
			case 'd':
				if(argc <= (a + 1)){
					usage();
					return 1;
				}
				setDebug(strtol(argv[++a], 0, 0));
				dbFixed = 1;
				break;
			default:	usage();	return 1;
			}
		}
	}

	// Open and read configuration
	if(configFile != ""){
		if(err = config.open(configFile)){
			eprintf("Error: Unable to open config file %s: %s\n", configFile.retStr(), err.getString().retStr());
			printf("Error: Unable to open config file %s: %s\n", configFile.retStr(), err.getString().retStr());
			return 1;
		}
	}
	else {
		if(config.open("tmsServerDebug.conf"))
			if(config.open("tmsServer.conf"))
				config.open("/etc/tmsServer.conf");
	}

	config.read();
	if(!dbFixed){
		s = config.findValue("Debug:");
		if(s != ""){
			db = strtol(s, 0, 0);
			setDebug(db);
		}
	}

	// Create daemon process	
	if(!foreground)
		daemon(0, 0);
		
	
	// Create main control class		
	control = new Control(dbFixed);
	if(err = control->init()){
		eprintf("Error: %s\n", err.getString().retStr());
		printf("Error: %s\n", err.getString().retStr());
		return 1;
	}
	
	control->run();
	
	return 0;
}
