/* FIX - use K&R style C syntax for compatibility w/ old stuff */

#include "headers.h" /* includes function declarations, macros, etc. */


/* ----- global data ----- */
int sockfd;               /* main fd to the server         */

int curlogfd = ERROR;     /* index into logs[]             */
int curuser = ERROR;      /* index into usernames[]        */

int nullfd;               /* /dev/null, used for stdin     */
int klogfd = ERROR;       /* /dev/klog fd                  */

int  dblogfd = 0;         /* fd for the debug log          */
FILE *dblogfd1 = NULL;    /* same file but FILE *          */ 

int errlogfd = 0;         /* fd for the error log          */
FILE *errlogfd1 = NULL;   /* same file but FILE *          */ 

FILE *confd = NULL;       /* syslog config file            */
FILE *spoolfd = NULL;     /* where to spool to locally     */

int connected = 0;        /* are we connected to the serv? */

int subID;                /* our sub-ID sent by server     */

int spooling = 0;         /* are we currently spooling?    */            
int didspool = 0;         /* were we previously spooling?  */
pid_t spoolpid = ERROR;   /* pid of the spooling process   */

int pinging = 1;          /* are we pinging or not?        */
volatile int streaming;   /* set to 1 hen we're streaming  */

int  shmID;               /* key ID for shared memory      */
char *segptr;             /* points to shared mem segment  */
char oldbuf[MAXSEGSIZE];  /* has old shared mem.. compare  */

volatile pid_t chpid;     /* child's pid (used to kill it) */

int locPort = ERROR;      /* local port to bind to         */
int curport = ERROR;      /* current port to bind to       */

struct sockaddr_in laddr, daddr;

int useServ = ERROR;        /* server # in list to start w/ #0  */
int curServ = ERROR;        /* index into the server structure  */

char SRSdir[MAXDNAMESIZE]; 

int gotInfoServ = 0; /* successfully connected to info server    */

struct passwd *pwd;

/* things for select() */
int nfds, funix;
fd_set readfds, unixm;

int prevmsg = ERROR; /* used to keep track of previous msg type */

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

int prim = 1;   /* 1 == conn to prim serv, 0 == conn to sec serv */
int silent = 0; /* 1 == don't output conn info */
int errors = 0; /* used to determine if an error occured */

int start = 1;  /* 1 when first started */
int done  = 0;  /* was quit() called?   */

int child = 0;  /* is the ping process up yet? */

int initing;     /* are we initializing? */

/* files the user can define.. */
char *spoolFile, *dbFile, *errorFile;

#ifndef DEBUG
volatile int debugging = 0; /* are we running in debug mode? */
#else
volatile int debugging = 1; /* are we running in debug mode? */
#endif

volatile sig_atomic_t running = 1; /* we start running */
volatile sig_atomic_t newData = 1; /* no new data yet  */
volatile sig_atomic_t gotAlrm = 0; /* no alarm set yet */

jmp_buf dospool, infoconn, reconnect, dropconn, doquit, sigdropconn;

#ifndef NOSSL
SSL *sslconn = NULL;
SSL_CTX *sslctx = NULL;
#endif

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

/* reports proper usage (local function only) */
void usage(char *progname)
{
   (void)fprintf(stderr, "Usage: %s "
                 "[-h] [-d] [-p port] [-e file] [-s file] [-l file]\n\n",
                 progname);

   (void)fprintf(stderr, 
		 "-h        help (this)\n"
		 "-d        enable debugging (good idea to enable)\n"
		 "-p port   local port to bind to (must be between 512-%d)\n"
		 "-e file   file to log error messages to\n"
		 "-s file   file to spool backup syslogs to\n"
		 "-l file   file to log all debugging messages to\n\n",
                 IPPORT_RESERVED-1);

   return;
}


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


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

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

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

   if (argv[1] != NULL)
      (void)printf("%client ID: %s\n%c", (debugging != 1 ? 'C' : 'c'), ID,
                   (strncmp(argv[1], "-h", 2) != 0 ? '\n' : '\0'));

   else
      (void)printf("%client ID: %s\n\n", (debugging != 1 ? 'C' : 'c'), ID);

   getArgs(argc, argv);

   if (debugging == 1)
   {
      printf("\n<*> SRS by RSI <*>\n\n");

      printf("Warning: debugging outputs data quickly\n");
      printf("Use %s -h for help on proper usage, and read SRS.doc.\n\n",
             argv[0]);

      printf("Press 'Enter' (or 'Return') to continue\n");
      (void)getchar();
   }

   else
   {
      printf("SRS by RSI..\n");
      printf("Use %s -h for help on proper usage, and read SRS.doc\n\n",
             argv[0]);
   }

#  ifndef SUN
   system("killall syslogd &>/dev/null");
#  endif

   createDirs(), setupFiles(), init(); 

   debug("local timezone: %s\n\n", tzname[0]);

   /* set up signals */
   (void)signal(SIGHUP, SIG_IGN);

   (void)signal(SIGINT, sighandler);
   (void)signal(SIGTERM, sighandler);

   (void)signal(SIGPIPE, SIG_IGN);

   (void)signal(SIGUSR1, SIG_IGN); 
   (void)signal(SIGUSR2, SIG_IGN);
   
   if (debugging != 1)
   {
      /* we only want to be able to produce a core if debugging */
      (void)signal(SIGQUIT, sighandler);

      /* we need these to make sure we exit properly */
      (void)signal(SIGILL, sighandler);
      (void)signal(SIGBUS, sighandler);
      (void)signal(SIGSEGV, sighandler); 
   }

   /* ------------- */
   
   debug("parsing %s... don't try to understand this\n", CONFILE);
   debug("--------------------------------------------------------\n\n");


   config(); /* handle config file */
   (void)signal(SIGHUP, sighandler); /* re-handle the config file */ 

   initStream(); /* create /dev/log, setup unix socket, etc. */

   getSRSuser();

#  if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
#  endif

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

   (void)setjmp(infoconn);

#  if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
#  endif

InfoConn:
   signal(SIGPIPE, SIG_IGN);

/*
   if (connected == 1)
   {
      connected = 0; 

      debug("now disconnecting from server...\n");   
      send_data("QUIT\n"), sleep(NORMPAUSE);

      res = close(sockfd);
      if (res == ERROR)
      {
         error("error closing the socket: %s\n\n", strerror(errno));
         quit(ERROR);
      }  

      sockfd = ERROR;
   }

   else
*/
      (void)close(sockfd), sockfd = ERROR;

   gotInfoServ = 0;
   errors = 0, connected = 0;

   (void)signal(SIGPIPE, dropinfoconn);

   initConn(PRIMSERV, SECSERV);  /* connect to info server */

   if ((errors == 1) && (connected != 1))
   {
      signal(SIGPIPE, SIG_IGN);

      if (spooling != 1)
      {
         error("error connecting to both info servers..\n"
               "now starting spooling\n\n");

         silent = 1, forkSpool();
         goto InfoConn;
      }

      else
      { 
         (void)sleep(MAXPAUSE);
         debug("(in spool parent) now restarting with info servers\n");

         goto InfoConn;
      }
   }

   else if ((connected == 1) && (spooling == 1))
   {
      silent = 0;
      debug("(in spool parent) now connected to an info server..\n");
      killSpooler();      
   }

   (void)signal(SIGPIPE, dropinfoconn); /* for the info server    */
   
   (void)printf("%cow initializing, please hold..\n",
                (debugging != 1 ? 'N' : 'n'));

   (void)printf("%cttemping to authenticate with the info server...\n",
               (debugging != 1 ? 'A' : 'a'));

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

   send_data("ID %s", ID), doAuth();

   getVers();

   debug("now we have list of new client/server versions..\n"); 

   getServList();  /* get list of servers */
   debug("now we have the streaming server list..\n"); 

   gotInfoServ = 1;

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

   (void)setjmp(reconnect);  /* longjmp() here to reconnect */
   (void)signal(SIGPIPE, SIG_IGN);

#  if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif
   
      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
#  endif

   errors = 0;
   nextServer(); /* get to next server (curServ changes) */

   /* FIX - still need to compare it to localhost */

   if ((errors == 1) && (initing == 1))
   {
      debug("info server and streaming server the same.. continuing\n");
      goto init1;
   }

/*
   if (connected == 1)
   {
      connected = 0;
      signal(SIGPIPE, SIG_IGN);

      debug("now disconnecting from server...\n");   
      send_data("QUIT\n"), sleep(NORMPAUSE);

      res = close(sockfd);
      if (res == ERROR)
      {
         error("error closing the socket: %s\n\n", strerror(errno));
         quit(ERROR);
      }  

      sockfd = ERROR;
   }
 
   else
*/
      (void)close(sockfd), sockfd = ERROR;

   if (child == 1) killPinger();

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

   else connected = 0;
 
   debug("now connecting to a streaming server...\n");

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

   /* when server drops conn or we're spooling.. we come here */

init1:
   (void)setjmp(dropconn), (void)setjmp(dospool);

   errors = 0;

#  if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
#  endif

   if (initing == 1) goto startwork;

   (void)signal(SIGPIPE, SIG_IGN);          /* just to be safe */

   if (child == 1) killPinger();

   doServer();

   connected = 0;
   res = connServ(servList[curServ].name); 
   if (res == ERROR)
   {
      debug("(after connServ) error occured, restarting\n");
      longjmp(dropconn, 1);
   }

   (void)signal(SIGPIPE, sighandler);

   if (connected == 1)
   {
      silent = 0;
      debug("(in parent) connected to a streaming server..\n");

      if (spooling == 1) killSpooler();
   }
 
   else longjmp(dropconn, 1);

   (void)printf("%cuthenticating ourselves with the streaming server...\n\n",
               (debugging != 1 ? 'A' : 'a'));

   send_data("ID %s", ID), doAuth();

startwork:
   if (initing == 1) initing = 0;

   (void)signal(SIGPIPE, sighandler);

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

   start = 0, running = 1;

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

   /* getSubID(); */ /* do we really need it? */

   while(1) procData();
   return SUCCESS;
}
