// Emacs style mode select -*- C++ -*-
//--------------------------------------------------------------------------
//
// Copyright(C) 2000 Simon Howard
//
// 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//--------------------------------------------------------------------------
//
// Main
//
// Init Program, Read Cmd line etc.
//
//--------------------------------------------------------------------------

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

#include "main.h"
#include "serv.h"
#include "cmds.h"
#include "html.h"

boolean debug = true;

time_t startup_time;

// cmd-line args

int orig_argc;       // original command line parameters
char **orig_argv;

int s_argc;          // original and config file
char **s_argv;

//------------------------------------------------------------------------
//
// General helper functions
//

// call perror (or lsck_perror in dos) and exit

#ifndef lsck_perror
#define lsck_perror perror
#endif

void error(char *function_name)
{
  lsck_perror(function_name);
  exit(-1);
}

// clean a string

void strclean(char *s)
{
  char *p;

  for(p=s; *s; s++)
    if(isprint(*s))
      *p++ = *s;

  *p = '\0';    
}

//--------------------------------------------------------------------------
//
// Log file
// 
// We log all connections and commands sent to the server
//

static char *logfilename = NULL;
static FILE *logfile = NULL;

void LogMessage(char *s, ...)
{
  va_list args;
  time_t timer;
  char *time_string;

  if(!logfile)
    return;

  // get time and description of time
  time(&timer);
  time_string = strdup(ctime(&timer));

  // clean time and print to file
  strclean(time_string);
  fprintf(logfile, "[%s] ", time_string);
  free(time_string);

  // print log message
  va_start(args, s);
  vfprintf(logfile, s, args);
  va_end(args);

  fprintf(logfile, "\n");

  fflush(logfile);
}

void CloseLogFile()
{
  LogMessage("Shutting down server");
  fclose(logfile);
}

void OpenLogFile()
{
  // open log file if one chosen
  // use stdout for logfile if in debug mode

  if(logfilename)
    {
      logfile = fopen(logfilename, "a");
      if(!logfile)
	error("OpenLogFile");
    }
  else
    if(debug)
      logfile = stdout;

  LogMessage("Started Server");

  atexit(CloseLogFile);
}

//--------------------------------------------------------------------------
//
// Command lines
//
// Read command lines from actual command line and cfg file

boolean force_load = false;

// read commands lines from cfg file and append actual command
// lines to end

#define MAXCMDLINES 128

static void ReadCfgFile()
{
  FILE *cfg_file;
  char cfg_filename[128];
  int i;

  s_argv = malloc(MAXCMDLINES * sizeof(*s_argv));

  s_argc = 1;
  s_argv[0] = orig_argv[0];  // copy program name

  sprintf(cfg_filename, "%s/.smmuservrc",
	  getenv("HOME") ? getenv("HOME") : ".");
  cfg_file = fopen(cfg_filename, "r");

  if(cfg_file)
    {
      char read_buffer[BUFFER_SIZE] = "";
      char c;
      boolean quoted = false;      // quoted argv

      while(!feof(cfg_file))
	{
	  // read next char from file
	  fread(&c, 1, sizeof(c), cfg_file);

	  // allow enclosure of args in quote " marks

	  if(c == '\"')
	    {
	      quoted = !quoted;
	      continue;
	    }

	  // if we reach a space, end the argv
	  // ignore this if we are reading an arg
	  // in " quotes

	  if(!isgraph(c) && !quoted)
	    {
	      // only add if there's something to add
	      if(read_buffer[0])
		{
		  // add to arg list
		  s_argv[s_argc++] = strdup(read_buffer);
		  
		  // clear read buffer
		  read_buffer[0] = '\0';
		}
	    }
	  else
	    {
	      // add c to read_buffer
	      read_buffer[strlen(read_buffer) + 1] = '\0';
	      read_buffer[strlen(read_buffer)] = c;
	    }
	}

      fclose(cfg_file);
    }

  for(i=1; i<orig_argc; i++)
    {
      s_argv[s_argc++] = orig_argv[i];
    }
}
static void ReadCmdLine()
{
  int i;

  ReadCfgFile();

  for(i=1; i<s_argc; i++)
    {
      if(!strcasecmp(s_argv[i], "-logfile"))
	{
	  logfilename = s_argv[++i];
	  continue;
	}

      if(!strcasecmp(s_argv[i], "-motd"))
	{
	  motd = strdup(s_argv[++i]);
	  continue;
	}

      if(!strcasecmp(s_argv[i], "-force"))
	{
	  // override pid file check
	  force_load = true;
	  continue;
	}

      if(!strcasecmp(s_argv[i], "-html"))
	{
	  html_filename = s_argv[++i];
	  continue;
	}

      if(!strcasecmp(s_argv[i], "-help"))
	{
	  printf
	    (
	     "cmd lines:\n"
	     "-logfile <logfile>    -- save log to logfile\n"
	     "-motd <motd>          -- set message of the day\n"
	     "-force                -- force loading: override pid file check\n"
	     "-html <htmlfile>      -- regularly write html stats file\n"
	     );
	  exit(0);
	}
    }
}

//---------------------------------------------------------------------------
//
// SpawnChild
//
// We fork() to create a seperate process
// So the program can keep running and the user returns to the command line
//

static void SpawnChild()
{
  int parent = fork();

  if(parent == -1)
    error("SpawnChild");

  if(parent)
    exit(0);
}

//------------------------------------------------------------------------
//
// PID Checking
//
// We do not want two (or more) copies of the server running.
// We create a temporary pid file on startup. When another
// copy is started, it checks for the existence of a pid file
// and will not continue loading if it finds one.
//

char pid_file[128];

void RemovePID()
{
  remove(pid_file);
}

static void WritePID()
{
  FILE *fs;

  // create pid file

  fs = fopen(pid_file, "w");
  fprintf(fs, "(fQ) <tom_> i have a vd");
  fclose(fs);

  atexit(RemovePID);  // delete pid file on exit
}

static void CheckPID()
{
  FILE *fs;

  // get pid file from program file name

  sprintf(pid_file, "%s.pid", s_argv[0]);
  
  // check for pid file already existing

  if(!force_load)
    if(fs = fopen(pid_file, "r"))
      {
	fclose(fs);

	// only display message if a terminal - do not complain to cron
	if(isatty(fileno(stdout)))
	  printf("There appears to be a copy of %s already running.\n"
		 "If not, run with -force to force server load\n",
		 s_argv[0]);
	exit(-1);
      }

  // no pid found  
}

//-------------------------------------------------------------------------
//
// Signal Handler
//
// Deal with error signals and exit() properly
//

static void sig_handler(int s)
{
  signal(s,SIG_IGN);  // Ignore future instances of this signal.

  LogMessage("signal -> Quitting: %s",
	     s==SIGSEGV ? "Segmentation Violation" :
	     s==SIGINT  ? "Interrupted by User" :
	     s==SIGILL  ? "Illegal Instruction" :
	     s==SIGFPE  ? "Floating Point Exception" :
	     s==SIGTERM ? "Killed" : "Terminated by signal");
  
  exit(-1);
}

// init sig handler
// set handler to detect all signals likely to occur

static void InitSig()
{
  signal(SIGSEGV, sig_handler);
  signal(SIGTERM, sig_handler);
  signal(SIGILL,  sig_handler);
  signal(SIGFPE,  sig_handler);
  signal(SIGILL,  sig_handler);
  signal(SIGINT,  sig_handler);
  signal(SIGABRT, sig_handler);
}

//-------------------------------------------------------------------------
//
// Main
//

int main(int argc, char *argv[])
{
  orig_argc = argc; orig_argv = argv;

  // init signal handling
  InitSig();

  // read cmd line options
  ReadCmdLine();

  // check another copy not running
  CheckPID();

  startup_time = time(NULL);

  connections.next = NULL;  // clear list

  // Open Comms Sockets
  CreateSocket();

  // fork()
  SpawnChild();

  // remove pid file on exit
  WritePID();

  // open log file 
  OpenLogFile();

  // Main loop
  Server();

}
