/* vim: set shiftwidth=3 softtabstop=3 expandtab: */ 

/*
Copyright (C) 2002-2003  Erik Fears
 
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.
 
*/

#include "setup.h"

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <sys/resource.h> /* getrlimit */
#include <fcntl.h>

#ifdef STDC_HEADERS
#include <stdlib.h>
#include <string.h>
#endif

#include "compat.h"
#include "config.h"
#include "extern.h"
#include "irc.h"
#include "log.h"
#include "opercmd.h"
#include "scan.h"
#include "stats.h"
#include "negcache.h"
#include "options.h"
#include "malloc.h"
#include "firedns.h"
#include "main.h"

RCSID("$Id: main.c,v 1.18 2003/06/22 13:19:39 andy Exp $");

static RETSIGTYPE do_signal(int signum);

int RESTART = 0;             /* Flagged to restart on next cycle */
int ALARMED = 0;             /* Flagged to call timer functions on next cycle */
int REOPEN  = 0;             /* Flagged to reopen log files on next cycle */
unsigned int OPT_DEBUG = 0;  /* Debug level */

char *CONFNAME = DEFAULTNAME;
char *CONFDIR = BOPM_ETCDIR;
char *LOGDIR = BOPM_LOGDIR;
char *CONFFILE, *LOGFILE;

struct sigaction ALARMACTION;
struct sigaction INTACTION;
struct sigaction USR1ACTION;

int main(int argc, char **argv)
{
   char spid[16];
   pid_t pid;
   int c;
   size_t lenc, lenl, lenp;
   unsigned int nc_counter, i;
   FILE *pidout;
   struct rlimit rlim;

   nc_counter = 0;

   while (1)
   {
      c = getopt(argc, argv, "dc:");

      if (c == -1)
         break;

      switch (c)
      {
         case 'c':
            CONFNAME = strdup(optarg);
            break;
         case 'd':
            OPT_DEBUG++;
            break;
         case '?':
         default:
            /* Unknown arg, guess we'll just do nothing for now. */
            break;
      }
   }

   lenc = strlen(CONFDIR) + strlen(CONFNAME) + strlen(CONFEXT) + 3;
   lenl = strlen(LOGDIR) + strlen(CONFNAME) + strlen(LOGEXT) + 3;
   lenp = strlen(LOGDIR) + strlen(CONFNAME) + strlen(PIDEXT) + 3;

   CONFFILE = MyMalloc(lenc * sizeof *CONFFILE);
   LOGFILE = MyMalloc(lenl * sizeof *LOGFILE);

   snprintf(CONFFILE, lenc, "%s/%s.%s", CONFDIR, CONFNAME, CONFEXT);
   snprintf(LOGFILE, lenl, "%s/%s.%s", LOGDIR, CONFNAME, LOGEXT);

   /* Fork off. */

   if (OPT_DEBUG <= 0)
   {
      if ((pid = fork()) < 0)
      {
         perror("fork()");
         exit(EXIT_FAILURE);
      }
      else if (pid != 0)
      {
         _exit(EXIT_SUCCESS);
      }

      /* Get us in our own process group. */
      if (setpgid(0, 0) < 0)
      {
         perror("setpgid()");
         exit(EXIT_FAILURE);
      }

      /* Reset file mode. */
      /* shasta: o+w is BAD, mmkay? */
      umask(002);

      /* Close file descriptors. */
      close(STDIN_FILENO);
      close(STDOUT_FILENO);
      close(STDERR_FILENO);

      log_open(LOGFILE);
   }
   else
      log_printf("MAIN -> Debug level %d", OPT_DEBUG);


   log_printf("MAIN -> BOPM %s started.", VERSION);
   log_printf("MAIN -> Reading configuration file...");

   config_load(CONFFILE);

   if (OptionsItem->scanlog)
      scanlog_open(OptionsItem->scanlog);

   pid = getpid();

   pidout = fopen(OptionsItem->pidfile, "w");
   snprintf(spid, 16, "%u", pid);

   if (pidout)
   {
      fwrite(spid, sizeof(char), strlen(spid), pidout);
      fclose(pidout);
   }
   else
   {
      log_printf("MAIN -> Error opening %s: %s", OptionsItem->pidfile,
          strerror(errno));
      exit(EXIT_FAILURE);
   }

   /* Setup alarm & int handlers. */

   ALARMACTION.sa_handler = &(do_signal);
   ALARMACTION.sa_flags = SA_RESTART;
   INTACTION.sa_handler = &(do_signal);
   USR1ACTION.sa_handler = &(do_signal);

   sigaction(SIGALRM, &ALARMACTION, 0);
   sigaction(SIGINT, &INTACTION, 0);
   sigaction(SIGUSR1, &USR1ACTION, 0);

   /* Ignore SIGPIPE. */
   signal(SIGPIPE, SIG_IGN);

   alarm(1);

   while (1)
   {
      

      /* Main cycles */
      irc_cycle();
      scan_cycle();


      /* Restart bopm if main_restart() was called (usually happens by m_kill in irc.c) */
      if(RESTART)
      {
         /* If restarted in debug mode, die */
         if(OPT_DEBUG)
            return(1); 

         log_printf("MAIN -> Restarting process");

         /* Get upper file descriptor limit */
         if(getrlimit(RLIMIT_NOFILE, &rlim) == -1) 
         {
            log_printf("MAIN RESTART -> getrlimit() error retrieving RLIMIT_NOFILE (%s)", strerror(errno));
            return(1); 
         }

         /* Set file descriptors 0-rlim_cur close on exec */
         for(i = 0; i < rlim.rlim_cur; i++)
            fcntl(i, F_SETFD, FD_CLOEXEC);

         /* execute new process */
         if(execve(argv[0], argv, NULL) == -1)
            log_printf("MAIN RESTART -> Execution of \"%s\" failed. ERROR: %s", argv[0], strerror(errno));

         /* Should only get here if execve failed */
         RESTART = 0;
      }

      /* Check for log reopen */
      if(REOPEN)
      {
         log_printf("MAIN -> Caught SIGUSR1, reopening logfiles");
         log_close();
         log_open(LOGFILE);

         if(OptionsItem->scanlog)
         {
            scanlog_close();
            scanlog_open(OptionsItem->scanlog);
         }

         log_printf("MAIN -> reopened logfiles");

         REOPEN = 0;
      }

      /* Call 1 second timers */
      if(ALARMED)
      {
         irc_timer();
         scan_timer();
         command_timer();

         ALARMED = 0;
      }


   }

   if (!OPT_DEBUG)
      log_close();

   /* If there's no scanlog open then this will do nothing anyway */
   scanlog_close();

   return(0);
}

static void do_signal(int signum)
{
   switch (signum)
   {
      case SIGALRM:
         ALARMED = 1;
         alarm(1);
         break;
      case SIGINT:
         log_printf("MAIN -> Caught SIGINT, bye!");
         exit(0);
         break;
      case SIGUSR1:
         REOPEN = 1;
         break;
   }
}


void main_restart(void)
{
   RESTART = 1;
}


syntax highlighted by Code2HTML, v. 0.9.1