/* FIX - fix reentrancy in sighandler() */

/* FIX - if the HD fills up.. the load average gets too high.. etc..  */
/*       redirect the client to the next server in the list           */

/* FIX - add K&R style function declartions to all source in client   */
/*       and server (to suppport old compilers                        */

/* ------------------------------------------ */

#include "headers.h"

/* ------ data definitions ------ */
int mainfd;                    /* fd to listen on...       */
int mainpid;                   /* main pid of server       */

int child = 0;                 /* is there a child?        */ 

int locPort = 0;               /* local bind port          */

int nullfd = 0;                /* /dev/null for stdin      */

int  dblogfd = 0;              /* write log msgs to here.. */
FILE *dblogfd1 = NULL;         /* same file but FILE *     */

int  errlogfd = 0;             /* write errors to here..   */
FILE *errlogfd1 = NULL;        /* same file but FILE *     */

int curlogfd = 0;              /* index into logs[]..      */

char *errorFile = NULL;        /* error log file           */
char *servListFile = NULL;     /* server list file         */

struct sockaddr_in laddr;      /* our local serv address   */

int curPid = ERROR;            /* index into pidstats[]    */
int curClient = ERROR;         /* index into clients[]     */

long timeVal;                  /* time() value from client */
long prevTimeVal;              /* prev. time() from client */

int errors;                    /* indicates an error occured */
int silent = 0;                /* are we outputting data?    */

int repcount = 0;
long prevlogtime = 0;
char prevlogmsg[MAXLOGSIZE];

#ifndef NOSSL
DH *dhparms; /* reads the DH parameters */
#endif

int prevmsg = ERROR; /* previous message type  */ 

struct passwd *pwd;

volatile sig_atomic_t nofree;     /* free structure found? */
volatile sig_atomic_t stopped;    /* is the child stopped? */
volatile sig_atomic_t timeout;    /* have we timed out?    */

#ifdef DEBUG
volatile int debugging = 1; /* are we in debugging mode or not */
#else
volatile int debugging = 0; /* are we in debugging mode or not */
#endif

jmp_buf doquit, newconn;

/* ------------------------------ */

/* reports proper usage (local function only)      */
/* [technically this should probably be in misc.c] */
void usage(char *progname)
{
   fprintf(stderr, "Usage: %s [-h] [-d] [-p local port] "
                   "[-e error file] [-s server list file]\n\n", progname);

   return;
}
/* ------------------------------ */


int main(int argc, char **argv)
{
   int res;

   if (getuid() != 0)
   {
      fprintf(stderr, "Error: This must be run as root (uid 0).\n");
      exit(ERROR);
   }

   memset(prevlogmsg, 0, sizeof(prevlogmsg));

   mainpid = getpid();
   doArgs(argc, argv), setup();

   (void)printf("Now starting SRS.. by RSI.\n");
   (void)printf("[if you have problems, try: %s -h, and read SRS.doc]\n\n", 
                argv[0]);

   if (debugging != 1) daemonize();
   else debug("running in debug mode.. not forking/daemonizing\n");

   init(), setupClients(), initconn();

   getSRSuser();

   res = seteuid(pwd->pw_uid);
   if (res == ERROR)
   {
      error("error with seteuid: %s\n\n", strerror(errno));
      quit(ERROR);
   }

   if (debugging == 1)
   {
      (void)putchar('\n');
      (void)write(dblogfd, "\n", 1);
   }

   debug("local timezone: %s\n", tzname[0]);
   
   /* main loop.. wait for connection and then handle it */
   while(1)
   {
      res = setjmp(newconn);

      if ((res == 1) || (errors == 1) || (timeout == 1))
         if (curClient > 0)
         {
            if (clients[curClient].sockfd > 0)
            {
               debug("now dropping client\n");
               (void)close(clients[curClient].sockfd);
               clients[curClient].free = 1;
            }
         }

      res = seteuid(pwd->pw_uid);
      if (res == ERROR)
      {
         error("error with seteuid: %s\n\n", strerror(errno));
         quit(ERROR);
      }

      timeout = 0, errors = 0, errno = 0;

      res = getconn(); /* this accepts the connection and sets stuff up */

      if (res == ERROR) 
      {
         while(1)
         {
            if (nofree != 1) break;
            sleep(MAXPAUSE * 3);
         }

         error("error in getconn().. restarting at top\n\n");

         if (clients[curClient].sockfd > 0)
            (void)close(clients[curClient].sockfd);

         clients[curClient].free = 1;
         continue;
      }

      procData(1); /* just to get the ID and auth client */

      /* we had an error in procData() before returning */
      if ((errors == 1) || (timeout == 1)) 
      {
         if (timeout == 1)
            error("timed out waiting for ID (and/or authentication)\n\n");

         else error("error in procData() or authClient.. "
                    "restarting at top\n\n");

         if (clients[curClient].sockfd > 0)
            (void)close(clients[curClient].sockfd);

         clients[curClient].free = 1;

         continue;
      }

      setupSubIDs();

      if (errors == 1)
      {
         error("error setting up sub-ID's\n\n"), errors = 0; 

         if (clients[curClient].sockfd > 0)
            (void)close(clients[curClient].sockfd);

         clients[curClient].free = 1;

         continue;
      }

      debug("now forking a child to handle the client\n");

      res = fork();
      if (res == ERROR)
      {
         error("error forking child process for new connection: %s\n\n",
               strerror(errno));

         quit(ERROR);
      }   

      else if (res == 0) /* 0 == child */
      {
         signal(SIGPIPE, sighandler);
         signal(SIGCHLD, SIG_IGN);

         stopped = 1; /* child can't run yet */

         makeHostArgs(argc, argv);

         (clients[curClient]).pidstats[curPid].pid  = getpid(); 
         (clients[curClient]).pidstats[curPid].ppid = getppid();
         (clients[curClient]).pidstats[curPid].pgid = getpgid(0);

         shrMemInit();

         signal(SIGUSR1, sighandler); 
         while (stopped == 1) (void)sleep(1);

         procData(0); /* process data from client */
      }

      else setPids(res);
   }

   return SUCCESS;
}
