/************************************************************************/
/*                                                                      */
/*  FILE:       Mk4d.c                                                  */
/*                                                                      */
/*  PURPOSE:    Main program file for the "Mk 4 Server Deamon".         */
/*                                                                      */
/*  AUTHOR:     Chris Albertson, June 1998                              */
/*                                                                      */
/*  HISTORY:    June 1998 -- Started work                               */
/*                                                                      */
/************************************************************************/
/*                                                                      */
/*                  Copyright (C) 1998 Chris Albertson.                 */
/*                                                                      */
/*   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; see the file COPYING.  If not, write to   */
/*   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,   */ 
/*   USA.                                                               */
/*                                                                      */
/************************************************************************/

/* Used by RCS and ident */
char Mk4d_rcsid[] = 
    "$Id: Mk4d.c,v 1.8 1998/08/20 00:24:05 chris Exp $";

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <values.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <netdb.h>
#include <signal.h>
#include <unistd.h>

#include "MLog.h"
#include "parameters.h"
#include "server_lib.h"
#include "commands.h"
#include "tasks.h"


/* This is the cycle period.  Actually the IO timeout for socket data   */
/* Set it to a large value 1.0 for low CPU utilization or to something  */
/* small 0.1 for better time resolution in task scheduling.  A  good    */
/* starting place is 0.5                                                */
#define MK4D_PERIOD 0.5

/* The socked FD on which we listen from clients.  */
static int listen_socket;


/* Local prototypes */
void cleanup(int sig);



void main(int argc, char *argv[])
{
    int                 FDlimit;
    fd_set              FDset;
    struct timeval      timeout;
    int                 res;
    int                 len;
    int                 sd;
    char		msg_buffer[MK4D_MAXMSG];
    
    double              SockTimeout = MK4D_PERIOD;
    int                 SockTimeout_sec;
    int                 SockTimeout_usec;
    double              TimeTillNextTask  = MAXDOUBLE;
    double              waittime;
    
    /********************************************************************/
    /* Initialization stuff                                             */
    /********************************************************************/
    
    /* Catch signals that would otherwise terminate this process    */
    signal (SIGHUP,  cleanup);
    signal (SIGINT,  cleanup);
    signal (SIGQUIT, cleanup);
    signal (SIGTERM, cleanup);

    /* Read arguments and mk4d.conf file */
    ProcArgs(argc, argv);
    ProcConf();


 
    /* Sign on message */
    ml_printf(ML_MANDATORY, ML_INFORMATION,
    	"Mk4d Version %s started...\n", VERS);
    
     
    /* Initialize the client table. */
    ct_Initialize();

    /* Create a socket and listen on it.    */
    if (( listen_socket = sl_Setup_Server_Socket(gbl_port) ) == 0 )
    {
        /* Unable to set up a socket. */
	ml_print(ML_MANDATORY, ML_ERROR,
    	    "Unable to set up a socket.  Terminating\n");
        cleanup(RTN_ERR);
    }


    /********************************************************************/
    /*									*/
    /*  This is the "main loop".					*/
    /*                                                                  */
    /*  To Do List:                                                     */
    /*              1) Shorten timeout value so we don't wait for IO    */
    /*                 past the scheduled time of the next task.        */
    /*                 Need to modify "AddTask"/RunTaskList to track    */
    /*                 the time to run next task.                       */
    /*									*/
    /********************************************************************/
    while(1)
    {
        /* Re-compute the timeout value. */        
        waittime = SockTimeout;
        if(TimeTillNextTask < waittime) waittime = TimeTillNextTask;
        
        SockTimeout_sec  = (int)waittime;
        SockTimeout_usec = (int)(0.5 + 
            (1000000.0 * (waittime - (double)SockTimeout_sec)));
        timeout.tv_sec  = SockTimeout_sec;
        timeout.tv_usec = SockTimeout_usec;
  
        /* Get an fd_set with for all connected clients (may be zero) */
	ct_get_fdset(&FDlimit, &FDset);
	
	/* add the listen socket to the set */
	FD_SET(listen_socket, &FDset);
	if (listen_socket+1 > FDlimit) FDlimit = listen_socket+1;
	
	/* Wait for IO on any socket up to timeout */
        res = select(FDlimit, &FDset, NULL, NULL, &timeout);

        /* if there is data act on it now */
        if (res > 0)
        {
            if (FD_ISSET(listen_socket, &FDset))
            {
                /* Got data on our "well known socket".  Must be a new  */
                /* client trying to connect                             */
                sl_ConnentToNextClient(listen_socket);
            }
            else
            {
                /* Got data from a connected client */
                for (sd=0; sd<FDlimit; sd++)
                {
                    /* check if it was this socket */
                    if ((sd != listen_socket) && (FD_ISSET(sd, &FDset)))
                    {
                        /* Socket number sd has data.  If we get a whole    */
                        /* message it is returned in the buffer.  If only   */
                        /* a partial message then we get a zero             */
                        len = sl_GetClientMessage(sd, msg_buffer);
                        if (len > 0)
                        { 
		            dispatch(sd, msg_buffer);
                        }
                    }
                }
            }
        }
        else if (res == -1)
        {
            /* Select returns error */
            ml_printf(ML_MANDATORY, ML_ERROR,
    	        "Error from select.  %s\n", strerror(errno));
            cleanup(RTN_ERR);
        }
        else
        {
            /* No data waiting and no error.  We will run the task list */
            /* Run any tasks that are scheduled to run at this time     */
            TimeTillNextTask =  RunTaskList();
        }
    }
    
    cleanup(RTN_OK);
}


/************************************************************************/
/*									*/
/************************************************************************/
void cleanup(int sig)
{
    char    cause[32];
    
    /* clear the string */
    memset(cause, 0, sizeof(cause));
    
    switch(sig)
    {
      case RTN_ERR:
          strncpy(cause, "(ERROR)", sizeof(cause));
          break;
          
      case RTN_OK:
          strncpy(cause, "(Normal shutdown)", sizeof(cause));
          break;

      case SIGHUP:
          strncpy(cause, "(SIGHUP)", sizeof(cause));
          break;

      case SIGINT:
          strncpy(cause, "(SIGINT)", sizeof(cause));
          break;

      case SIGQUIT:
          strncpy(cause, "(SIGQUIT)", sizeof(cause));
          break;

      case SIGTERM:
          strncpy(cause, "(SIGTERM)", sizeof(cause));
          break;
    }
    
    ct_RemoveAllClients();
    
    close(listen_socket);
    ml_printf(ML_MANDATORY, ML_INFORMATION,
    	"Mk4d terminated %s\n", cause);
    
    if (sig == RTN_ERR)
    {
        exit(1);    /* return code 1 undictates some error */
    }
    else
    {
        exit(0);
    }
}
