/************************************************************************
 *   IRC - Internet Relay Chat, src/ircd.c
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Computing Center
 *
 *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: ircd.c,v 1.3 2001/02/02 20:40:01 jpinto Exp $
 */
#include "ircd.h"
#include "channel.h"
#include "class.h"
#include "client.h"
#include "common.h"
#ifdef HAVE_SSL
#include "ssl.h"
#endif
#include "dline_conf.h"
#include "fdlist.h"
#include "hash.h"
#include "help.h"
#include "irc_string.h"
#include "ircd_signal.h"
#include "list.h"
#include "m_gline.h"
#include "motd.h"
#include "patchlevel.h"     /* PATCHLEVEL */
#include "msg.h"         /* msgtab */
#include "mtrie_conf.h"
#include "numeric.h"
#include "parse.h"
#include "res.h"
#include "restart.h"
#include "s_auth.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "s_debug.h"
#include "s_log.h"
#include "s_misc.h"
#include "s_serv.h"      /* try_connections */
#include "s_stats.h"
#include "s_zip.h"
#include "scache.h"
#include "send.h"
#include "struct.h"
#include "whowas.h"
#include "path.h"
#include "help.h"
#include "conf_vars.h"
#include "dconf.h"
#include "m_htm.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <pwd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <signal.h>


#if defined(HAVE_GETOPT_H)
#include <getopt.h>
#endif /* HAVE_GETOPT_H */

#ifdef SETUID_ROOT
#include <sys/lock.h>
#include <unistd.h>
#endif /* SETUID_ROOT */

/*
 * for getopt
 * ZZZ this is going to need confirmation on other OS's
 *
 * #include <getopt.h>
 * Solaris has getopt.h, you should too... hopefully
 * BSD declares them in stdlib.h
 * extern char *optarg;
 * 
 * for FreeBSD the following are defined:
 *
 * extern char *optarg;
 * extern int optind;
 * extern in optopt;
 * extern int opterr;
 * extern in optreset;
 */

/*
 * Try and find the correct name to use with getrlimit() for setting the max.
 * number of files allowed to be open by this process.
 */
#ifdef RLIMIT_FDMAX
# define RLIMIT_FD_MAX   RLIMIT_FDMAX
#else
# ifdef RLIMIT_NOFILE
#  define RLIMIT_FD_MAX RLIMIT_NOFILE
# else
#  ifdef RLIMIT_OPEN_MAX
#   define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
#  else
#   undef RLIMIT_FD_MAX
#  endif
# endif
#endif


#ifdef  REJECT_HOLD
int reject_held_fds = 0;
#endif

#ifdef NEED_SPLITCODE
extern time_t server_split_time;
extern int    server_was_split;
#endif

int cold_start = YES;   /* set if the server has just fired up */

/* /quote set variables */
struct SetOptions GlobalSetOptions;

/* config.h config file paths etc */
ConfigFileEntryType ConfigFileEntry; 

struct  Counter Count;

time_t  CurrentTime;            /* GLOBAL - current system timestamp */
int     ircntp_offset;          /* GLOBAL - irc ntp offset */
int     ServerRunning;          /* GLOBAL - server execution state */
struct Client me;                     /* That's me */
int         no_ssl = 0;

struct Client* GlobalClientList = 0; /* Pointer to beginning of Client list */
/* client pointer lists -Dianora */ 
struct Client *local_cptr_list = NULL;
struct Client *oper_cptr_list  = NULL;
struct Client *serv_cptr_list  = NULL;

static size_t      initialVMTop = 0;   /* top of virtual memory at init */
static const char* logFileName = LPATH;
static int         bootDaemon  = 1;


char**  myargv;
int     dorehash    = 0;
int		silent_start= 0;
int     dconf_check = 0;
int     debuglevel	= -1;        /* Server debug level */
char*   debugmode	= "";        /*  -"-    -"-   -"-  */

char	ircdpath[255];		/* Absolute ircd path */

int     rehashed = YES;
int     dline_in_progress = NO; /* killing off matching D lines ? */
time_t  nextconnect = 1;        /* time for next try_connections call */
time_t  nextping = 1;           /* same as above for check_pings() */

#ifdef HLC   
/* those vars below are for HLC (hub-lag-checking) --fbs */
time_t  nexthubcheck = 1;       /* same as above and above for check_hubping() */
int     sentping = 0;           /* have I pinged my uplink? */
time_t  receivedat = 0;         /* last pong received from hub */
#endif
time_t  nextfdlistcheck = 1;    /* do not check whole fdlist everytime */

/* /htm max | see m_htm.c --fbs */
int     HTMMAX = 0;

int IsHub = 0;

/* code added by mika nystrom (mnystrom@mit.edu) */
/* this flag is used to signal globally that the server is heavily loaded,
   something which can be taken into account when processing e.g. user commands
   and scheduling ping checks */
/* Changed by Taner Halicioglu (taner@CERF.NET) */

#define LOADCFREQ  5     /* every 5s */
#define LOADRECV  30     /* 30k/s */

int    LRV = LOADRECV;
time_t LCF = LOADCFREQ;
float currlife = 0.0;
float cursend = 0.0;

/*
 * get_vm_top - get the operating systems notion of the resident set size
 */
static size_t get_vm_top(void)
{
  /*
   * NOTE: sbrk is not part of the ANSI C library or the POSIX.1 standard
   * however it seems that everyone defines it. Calling sbrk with a 0
   * argument will return a pointer to the top of the process virtual
   * memory without changing the process size, so this call should be
   * reasonably safe (sbrk returns the new value for the top of memory).
   * This code relies on the notion that the address returned will be an 
   * offset from 0 (NULL), so the result of sbrk is cast to a size_t and 
   * returned. We really shouldn't be using it here but...
   */
  void* vptr = sbrk(0);
  return (size_t) vptr;
}

/*
 * get_maxrss - get the operating systems notion of the resident set size
 */
size_t get_maxrss(void)
{
  return get_vm_top() - initialVMTop;
}

/*
 * init_sys
 */
static void init_sys(int boot_daemon)
{
#ifdef RLIMIT_FD_MAX
  struct rlimit limit;

  if (!getrlimit(RLIMIT_FD_MAX, &limit))
    {

      if (limit.rlim_max < MAXCONNECTIONS)
        {
          fprintf(stderr,"ircd fd table too big\n");
          fprintf(stderr,"Hard Limit: %ld IRC max: %d\n",
                        (long) limit.rlim_max, MAXCONNECTIONS);
          fprintf(stderr,"Fix MAXCONNECTIONS\n");
          exit(-1);
        }

      limit.rlim_cur = limit.rlim_max; /* make soft limit the max */
      if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
        {
          fprintf(stderr,"error setting max fd's to %ld\n",
                        (long) limit.rlim_cur);
          exit(-1);
        }

#ifndef USE_POLL
      if( MAXCONNECTIONS > FD_SETSIZE )
        {
          fprintf(stderr, "FD_SETSIZE = %d MAXCONNECTIONS = %d\n",
                  FD_SETSIZE, MAXCONNECTIONS);
          fprintf(stderr,
            "Make sure your kernel supports a larger FD_SETSIZE then " \
            "recompile with -DFD_SETSIZE=%d\n", MAXCONNECTIONS);
          exit(-1);
        }
#endif /* USE_POLL */
    }	
#endif        /* RLIMIT_FD_MAX */

  /* This is needed to not fork if -s is on */
  if (boot_daemon)
    {
      int pid;
      if((pid = fork()) < 0)
        {
          fprintf(stderr, "Couldn't fork: %s\n", strerror(errno));
          exit(0);
        }
      else if (pid > 0)
        exit(0);
#ifdef TIOCNOTTY
      { /* scope */
        int fd;
        if ((fd = open("/dev/tty", O_RDWR)) >= 0)
          {
            ioctl(fd, TIOCNOTTY, NULL);
            close(fd);
          }
      }
#endif
     setsid();
    }
  close_all_connections();
}

/*
 * bad_command
 *      This is called when the commandline is not acceptable.
 *      Give error message and exit without starting anything.
 */
static void bad_command()
{
  fprintf(stderr, 
          "Usage: ircd [-c] [-d dlinefile] [-f configfile] [-h servername] "
          "[-k klinefile] [-l logfile] [-n] [-s] [-v] [-x debuglevel] | [--help]\n");
  fprintf(stderr, "Server not started\n");
  exit(-2);
}


char* cmd_line_options[]={
 PATCHLEVEL,
 " All command line parameters have the syntax \"-f string\" or \"-fstring\"",
 " OPTIONS:",
 " -c          - dconf check",
 " -d path     - specify data path",
 " -f filename - specify config file",
 " -h hostname - specify server name",
 " -k filename - specify k:line file",
 " -l filename - specify log file",
 " -n          - do not fork, run in foreground",
 " -s          - silent start - no start messages",
 " -v          - print version and exit",
 " -x          - set debug level, if compiled for debug logging",
  NULL
};
  
static void parse_command_line(int argc, char* argv[])
{
  const char* options = "d:f:h:k:l:nvx:cs"; 
  int         opt;
  char**      line = cmd_line_options;

  if((argc>1) && !irccmp(argv[1],"--help"))
    {
      while (*line)
        printf("%s\n", *line++);
      exit(0);
    }    
    
  while ((opt = getopt(argc, argv, options)) != EOF) {
    switch (opt) {
    case 'c':
      dconf_check = -1;
      break;
    case 'd': 
      if (optarg)
        ConfigFileEntry.dpath = optarg;
      break;
    case 'f':
#ifdef CMDLINE_CONFIG
      if (optarg)
        ConfigFileEntry.configfile = optarg;
#endif
      break;
    case 'k':
#ifdef KPATH
      if (optarg)
        ConfigFileEntry.klinefile = optarg;
#endif
      break;
    case 'h':
      if (optarg)
        strncpy_irc(me.name, optarg, HOSTLEN);
      break;
    case 'l':
      if (optarg)
        logFileName = optarg;
      break;
    case 'n':
      bootDaemon = 0; 
      break;
    case 'v':
      printf("ircd %s\n\tzlib %s\n\tircd_dir: %s\n", ircdversion,
#ifndef ZIP_LINKS
             "not used",
#else
              zlib_version,
#endif
              ConfigFileEntry.dpath);
      exit(0);
      break;   /* NOT REACHED */
    case 'x':
#ifdef  DEBUGMODE
      if (optarg) {
        debuglevel = atoi(optarg);
        debugmode = optarg;
      }
#endif
      break;
	case 's':
	  silent_start = -1;
	  break;
    default:
      bad_command();
      break;
    }
  }
}

static inline void io_loop(void)
{
  static char   to_send[200];
  static time_t lasttime  = 0;
  static long   lastrecvK = 0, lastsendK = 0;
  static int    lrv       = 0;
  time_t        lasttimeofday;

  lasttimeofday = CurrentTime;
  if ((CurrentTime = time(NULL)) == -1)
    {
      ircd_log(L_ERROR, "Clock Failure (%d)", errno);
      sendto_ops("Clock Failure (%d), TS can be corrupted", errno);
      restart("Clock Failure");
    }
  else CurrentTime += ircntp_offset;

  if (CurrentTime < lasttimeofday)
    {
      ircsprintf(to_send, "System clock is running backwards - (%d < %d)",
                 CurrentTime, lasttimeofday);
      report_error(to_send, me.name, 0);
    }

  /*
   * This chunk of code determines whether or not
   * "life sucks", that is to say if the traffic
   * level is so high that standard server
   * commands should be restricted
   *
   * Changed by Taner so that it tells you what's going on
   * as well as allows forced on (long LCF), etc...
   */
  if (!lasttime)
      lasttime = CurrentTime;

  if ((CurrentTime - lasttime) >= LCF)
    {
      lrv = LRV * LCF;
      lasttime = CurrentTime;
      cursend = (float)((long)me.sendK - lastsendK)/(float)LCF;
      currlife = (float)((long)me.receiveK - lastrecvK)/(float)LCF;
      if (((long)me.receiveK - lrv) > lastrecvK )
        {
          if (!LIFESUX)
            {
              LIFESUX = 1;

              if (NOISYHTM)
                {
                  sprintf(to_send, 
                        "Entering high-traffic mode - (%.1fk/s > %dk/s)",
                                (float)currlife, LRV);
                  sendto_ops("%s", to_send);
                }
            }
          else
            {
              LIFESUX++;                /* Ok, life really sucks! */
              LCF += 2;                 /* Wait even longer */
              if (NOISYHTM) 
                {
                  sprintf(to_send,
                        "Still high-traffic mode %d%s (%d delay): %.1fk/s",
                                LIFESUX,
                                (LIFESUX > 9) ?  " (TURBO)" : "",
                                (int)LCF, (float)currlife);
                  sendto_ops("%s", to_send);
                }
              if (LIFESUX>15) 
              {
                  if (NOISYHTM) 
                      sendto_ops("Resetting HTM and raising limit to: %dk/s\n", LRV + 5);
                  LCF=LOADCFREQ;
                  LIFESUX=0;
                  LRV+=5;
              }
          }
        }
      else
        {
          LCF = LOADCFREQ;
          if (LIFESUX)
            {
              LIFESUX = 0;
              if (NOISYHTM)
                sendto_ops("Resuming standard operation . . . .");
            }
        }
      lastrecvK = (long)me.receiveK;
      lastsendK = (long)me.sendK;
    }

  /*
  ** We only want to connect if a connection is due,
  ** not every time through.  Note, if there are no
  ** active C lines, this call to Tryconnections is
  ** made once only; it will return 0. - avalon
  */

  if (nextconnect && CurrentTime >= nextconnect)
    nextconnect = try_connections(CurrentTime);

  if (CurrentTime >= nextping) {
    nextping = check_pings(CurrentTime);
    timeout_auth_queries(CurrentTime);
  }

#ifdef HLC
    /* code for HLC (hub-lag-checking) --fbs */

  if (!(IsHub) && (CurrentTime >= nexthubcheck)) 
    {
      if (serv_cptr_list)
        {
          time_t lag = (CurrentTime - receivedat);
          if (lag >= HCF) lag -= HCF;
          if (!sentping) {
              sendto_one(serv_cptr_list, "PING :%lu", CurrentTime);
              sentping = 1;
          }

          sendto_realops_flags(FLAGS_DEBUG, "Lag: %d secs", (int)lag);

          if (lag >= LAGLIMIT)
            {
              if (MAXCLIENTS != 1) 
                {
                  sendto_realops("lag: %d seconds - disabling client connections (%d users)", lag, Count.local);
                  sendto_sanotice_butme("lag: %d seconds - disabling client connections (%d users)",
                                        lag, Count.local);
                        LIFESUX = 10;   /* to enter TURBO mode (servers priority) */
                        LCF = 600;      /* load check frequency.. 600 secs
                                           will only do htm-check 10 mins later.
                                           if 10 mins have passed and HLC wasnt
                                           resetted (resetHLC), there's no point
                                           keeping the server in TURBO mode:
                                           the htm-check will disable it (lifesux=0).
                                           --fbs */
                        MAXCLIENTS = 1; /* don't let anyone connect into us! we're
                                           lagged with our hub */
                }
            } 
          else if ((lag <= LAGMIN) && (MAXCLIENTS == 1)) 
            {
                resetHLC();
                sendto_realops("lag: %d seconds - enabling client connections (%d users)", lag, Count.local);
                sendto_sanotice_butme("lag: %d seconds - enabling client connections (%d users)",
                                      lag, Count.local);
            }
        } 
      else if (MAXCLIENTS == 1) { resetHLC(); }
      nexthubcheck = CurrentTime + HCF;
  }
#endif /* HLC */  
  if (dorehash && !LIFESUX)
    {
      rehash(&me, &me, 1);
      dorehash = 0;
    }

  if (CurrentTime >= nextfdlistcheck) {
      fdlist_check();
      nextfdlistcheck = CurrentTime + 3;
  }
}

static inline void read_things(void) {
  static time_t lasttime=0;
  static unsigned char LAGGED=0;

  read_message(FDL_SERVER);
  read_message(FDL_BUSY);

  if (!(HTMMAX & HM_FUCK_IDLE) && (LAGGED>1))
      read_message(FDL_ALL);
  else read_message(FDL_SERVER);

  if (LIFESUX)
    {
      read_message(FDL_SERVER);
      if (LIFESUX > 9)
        {       /* life really sucks */
          read_message(FDL_BUSY);
          read_message(FDL_SERVER);
        }
    }

  if ((!(HTMMAX & HM_FUCK_IDLE) && (lasttime < CurrentTime))
  || ((HTMMAX & HM_FUCK_IDLE) && (lasttime+3 < CurrentTime)))
  {
      read_message(FDL_ALL);
      if ((CurrentTime - lasttime) > 1) {
          sendto_realops_flags(FLAGS_DEBUG, "delta: %d",
              CurrentTime - lasttime);
          LAGGED++;
      } else LAGGED=0;
      lasttime = CurrentTime;
  }
}

#ifdef HLC
void resetHLC(void) 
{
  MAXCLIENTS = MAX_CLIENTS;
  sentping = 0;
  if (LIFESUX == 10 && LCF == 600)
    {
      LIFESUX = 0;
      LCF = LOADCFREQ;
    }
}
#endif
/*
 * initalialize_global_set_options
 *
 * inputs       - none
 * output       - none
 * side effects - This sets all global set options needed 
 */

static void initialize_global_set_options(void)
{
  memset( &GlobalSetOptions, 0, sizeof(GlobalSetOptions));

  MAXCLIENTS = MAX_CLIENTS;
  NOISYHTM = NOISY_HTM;
  GlobalSetOptions.autoconn = 1;

#ifdef FLUD
  FLUDNUM = FLUD_NUM;
  FLUDTIME = FLUD_TIME;
  FLUDBLOCK = FLUD_BLOCK;
#endif

#ifdef IDLE_CHECK
  IDLETIME = MIN_IDLETIME;
#endif

#ifdef ANTI_SPAMBOT
  SPAMTIME = MIN_JOIN_LEAVE_TIME;
  SPAMNUM = MAX_JOIN_LEAVE_COUNT;
#endif

#ifdef ANTI_DRONE_FLOOD
  DRONETIME = DEFAULT_DRONE_TIME;
  DRONECOUNT = DEFAULT_DRONE_COUNT;
#endif

#ifdef NEED_SPLITCODE
 SPLITDELAY = (DEFAULT_SERVER_SPLIT_RECOVERY_TIME * 60);
 SPLITNUM = SPLIT_SMALLNET_SIZE;
 SPLITUSERS = SPLIT_SMALLNET_USER_SIZE;
 server_split_time = CurrentTime;
#endif

 /* End of global set options */

}

/*
 * initialize_message_files
 *
 * inputs       - none
 * output       - none
 * side effects - Set up all message files needed, motd etc.
 */

static void initialize_message_files(void)
  {
  InitMessageFile( USER_MOTD, MPATH, &ConfigFileEntry.motd );
  InitMessageFile( OPER_MOTD, OPATH, &ConfigFileEntry.opermotd );
  InitMessageFile( USER_MOTD, WPATH, &ConfigFileEntry.wmotd );

  ReadMessageFile( &ConfigFileEntry.motd );
  ReadMessageFile( &ConfigFileEntry.opermotd );
  ReadMessageFile( &ConfigFileEntry.wmotd );
  }

/*
 * write_pidfile
 *
 * inputs       - none
 * output       - none
 * side effects - write the pid of the ircd to PPATH
 */

static void write_pidfile(void)
{
  int fd;
  char buff[20];
  if ((fd = open(PPATH, O_CREAT|O_WRONLY, 0600))>=0)
    {
      ircsprintf(buff,"%d\n", (int)getpid());
      if (write(fd, buff, strlen(buff)) == -1)
        ircd_log(L_ERROR,"Error writing to pid file %s", PPATH);
      close(fd);
      return;
    }
  else
    ircd_log(L_ERROR, "Error opening pid file %s", PPATH);
}


/* check_logsize
 *
 * inputs       - nothing
 * output       - nothing
 * side effects - checks logfile if >100k log rotation will occur 
 *                
 * - Lamego
 */
static void check_logsize(void)
{
  struct stat finfo;
  if(stat(LPATH,&finfo)==0) /* successful */
	{
	  if (finfo.st_size > 100000) /* > 100k ? */
		{
		  printf("Rotating "LPATH"...\n");
		  rename(LPATH".2",LPATH".3");
		  rename(LPATH".1",LPATH".2");
		  rename(LPATH,LPATH".1");		  
		}
	}
}  

/*
 * check_pidfile
 *
 * inputs       - nothing
 * output       - nothing
 * side effects - reads pid from pidfile and checks if ircd is in process
 *                list. if it is, gracefully exits
 * -kre
 */
static void check_pidfile(void)
{
  int fd;
  char buff[20];
  pid_t pidfromfile;

  if ((fd = open(PPATH, O_RDONLY)) >= 0 )
  {
    if (read(fd, buff, sizeof(buff)) == -1)
    {
      /* printf("NOTICE: problem reading from %s (%s)\n", PPATH,
          strerror(errno)); */
    }
    else
    {
      pidfromfile = atoi(buff);
      if (pidfromfile != (int)getpid() && !kill(pidfromfile, 0))
      {
        printf("ERROR: daemon is already running with pid=%i\n", pidfromfile);
        exit(-1);
      }
    }
    close(fd);
  }
  else if(errno != ENOENT)
  {
    printf("WARNING: problem opening %s: %s\n", PPATH, strerror(errno));
  }

}

/*
 * setup_corefile
 *
 * inputs       - nothing
 * output       - nothing
 * side effects - setups corefile to system limits.
 * -kre
 */
static void setup_corefile(void)
{
  struct rlimit rlim; /* resource limits */

  /* Set corefilesize to maximum */
 if (!getrlimit(RLIMIT_CORE, &rlim))
  {
    rlim.rlim_cur = rlim.rlim_max;
    setrlimit(RLIMIT_CORE, &rlim);
  }
}




int main(int argc, char *argv[])
{
  uid_t       uid;
  uid_t       euid;
  aConfItem*  aconf;


  /*
   * save server boot time right away, so getrusage works correctly
   */
  if ((CurrentTime = time(0)) == -1)
    {
      fprintf(stderr, "ERROR: Clock Failure: %s\n", strerror(errno));
      exit(errno);
    }
    

  /*
   * Setup corefile size immediately after boot
   */
  setup_corefile();
  
  /* 
   * set initialVMTop before we allocate any memory
   */
  initialVMTop = get_vm_top();
  
  /*
   * Get ircd binary absolute path
   */
  ircdpath[0] = '\0';
  if (argv[0][0] != '/')
	{
	  if ( getcwd(ircdpath, 255)==NULL )
		{
		  fprintf(stderr, "ERROR getting current directory %s\n", strerror(errno));
    	  exit(errno);		
		}
	  strcat(ircdpath,"/");		
	}

  strncat(ircdpath, argv[0], 255-strlen(ircdpath));  
    
  ServerRunning = 0;
  memset(&me, 0, sizeof(me));
  GlobalClientList = &me;       /* Pointer to beginning of Client list */
  cold_start = YES;             /* set when server first starts up */

  memset(&Count, 0, sizeof(Count));
  Count.server = 1;     /* us */

  initialize_global_set_options();

#ifdef REJECT_HOLD
  reject_held_fds = 0;
#endif

/* this code by mika@cs.caltech.edu */
/* 
 * it is intended to keep the ircd from being swapped out. BSD swapping
 * criteria do not match the requirements of ircd 
 */
#ifdef SETUID_ROOT
  if (setuid(IRC_UID) < 0)
    exit(-1); /* blah.. this should be done better */
#endif


  uid = getuid();
  euid = geteuid();

  ConfigFileEntry.dpath = DPATH;

  ConfigFileEntry.configfile = CPATH;   /* Server configuration file */

#ifdef KPATH
  ConfigFileEntry.klinefile = KPATH;         /* Server kline file */
#else
  ConfigFileEntry.klinefile = CPATH;
#endif /* KPATH */

#ifdef DLPATH
  ConfigFileEntry.dlinefile = DLPATH;
#else
  ConfigFileEntry.dlinefile = CPATH;
#endif /* DLPATH */

#ifdef  CHROOTDIR
  if (chdir(DPATH))
    {
      perror("chdir " DPATH );
      exit(-1);
    }

  if (chroot(DPATH))
    {
      fprintf(stderr,"ERROR:  Cannot chdir/chroot\n");
      exit(5);
    }
#endif /*CHROOTDIR*/

  myargv = argv;
  umask(077);                /* better safe than sorry --SRB */

  setuid(uid);
  
#ifdef  ZIP_LINKSa
  /* Make sure the include files match the library version number. */
  /* configure takes care of looking for zlib and zlibVersion(). */  
  if (strcmp(zlibVersion(), ZLIB_VERSION) != 0)
  {
    fprintf(stderr, "WARNING: zlib include files differ from library.\n");
    fprintf(stderr, "WARNING: ZIPLINKS may fail!\n");
    fprintf(stderr, "WARNING: library %s, include files %s\n",
            zlibVersion(), ZLIB_VERSION);
  }
#endif

  parse_command_line(argc, argv); 

  
#ifndef CHROOT
  if (chdir(ConfigFileEntry.dpath))
    {
      perror("chdir");
      exit(-1);
    }
#endif
  /*
   * Just check dconf for errors
   */
  if(dconf_check)
    {
      if ((conf_read("save.dconf", 0) == -1) )	/* first check for saved settings */
	    {	  
		  if ((conf_read("main.dconf", 0) == -1) )	/* read our local settings */
			{
				ircd_log(L_ERROR, "Error opening main.dconf");
		      return -1;
		    }  
	    }

      return !conf_check(-1);
	}
      
  /*
   * Check if daemon is already running
   */
  check_pidfile();  

  /*
   * Check if log rotation is needed
   */  
  check_logsize();

#if 0
#if !defined(IRC_UID)
  if ((uid != euid) && !euid)
    {
      fprintf(stderr,
              "ERROR: do not run ircd setuid root. " \
              "Make it setuid a normal user.\n");
      exit(-1);
    }
#endif
#endif

#if !defined(CHROOTDIR) || (defined(IRC_UID) && defined(IRC_GID))

  setuid(euid);

  if (getuid() == 0)
    {
# if defined(IRC_UID) && defined(IRC_GID)

      /* run as a specified user */
      fprintf(stderr,"WARNING: running ircd with uid = %d\n", IRC_UID);
      fprintf(stderr,"         changing to gid %d.\n",IRC_GID);

      /* setgid/setuid previous usage noted unsafe by ficus@neptho.net
       */

      if (setgid(IRC_GID) < 0)
        {
          fprintf(stderr,"ERROR: can't setgid(%d)\n", IRC_GID);
          exit(-1);
        }

      if(setuid(IRC_UID) < 0)
        {
          fprintf(stderr,"ERROR: can't setuid(%d)\n", IRC_UID);
          exit(-1);
        }

#else
#if 0
      /* check for setuid root as usual */
      fprintf(stderr,
              "ERROR: do not run ircd setuid root. " \
              "Make it setuid a normal user.\n");
      return -1;
#endif
# endif
            } 
#endif /*CHROOTDIR/UID/GID*/

  if(!silent_start)
	{
		printf("Starting BraSmIRCd IRCd version %s\n",ircdversion);
	}

  setup_signals();

  init_sys(bootDaemon);
  init_log(logFileName);

  ircd_log(L_NOTICE, "Starting ircd from %s", ircdpath);
  
  initialize_message_files();

  dbuf_init();  /* set up some dbuf stuff to control paging */
  init_hash();

#ifdef HAVE_SSL 
  init_ssl();
#endif


  clear_scache_hash_table();    /* server cache name table */
  clear_ip_hash_table();        /* client host ip hash table */
  clear_Dline_table();          /* d line tree */
  initlists();
  initclass();
  initwhowas();
  init_stats();
  init_tree_parse(msgtab);      /* tree parse code (orabidoo) */

  fdlist_init();
  init_netio();

  if ((conf_read("save.dconf", 0) == -1) )	/* first check for saved settings */
	{	  
		if ((conf_read("main.dconf", 0) == -1) )	/* read our local settings */
			{
				ircd_log(L_ERROR, "Error opening main.dconf");
			return -1;
		}  
	}

  if( !conf_check(0) )
	  return -1;

  if (TimeZone) 
	{
#ifdef USE_SETENV
	  setenv("TZ",TimeZone,1);
  	  tzset();  
#else
#warning TimeZone setting is not supported on this system
	  ircd_log(L_WARN, "ignoring unsupported TimeZone setting");
#endif
	}
	
  read_conf_files(YES);         /* cold start init conf files */
  
  aconf = find_me();
  if (EmptyString(me.name))
    strncpy_irc(me.name, aconf->host, HOSTLEN);
  strncpy_irc(me.host, aconf->host, HOSTLEN);
  me.fd = -1;
  me.from = &me;
  me.servptr = &me;
  SetMe(&me);
  SetServer(&me);
  make_server(&me);
  me.serv->up = me.name;
  strncpy_irc(me.serv->version, ircdversion, HOSTLEN);
  me.lasttime = me.since = me.firsttime = CurrentTime;
  add_to_client_hash_table(me.name, &me);
  check_class();
  write_pidfile();

  spam_words_init(SpamWords);
  virus_words_init(VirusWords);
  init_new_cmodes();
  init_local_log_channels(&me);  
  
  help_load(UserHelpFile , &user_help);	
  help_load(OperHelpFile , &oper_help);	
  help_load(AdminHelpFile , &admin_help);	
  
  ircd_log(L_NOTICE, "Server Ready");

  ServerRunning = 1;

  LIFESUX = 1;
  LCF = 120;

  while (ServerRunning)
    {
      int i;
#ifdef USE_ADNS
      do_adns_io();
#endif
      io_loop();
      for (i = 0; i<=3; i++) {
          if (!LIFESUX)
                usleep(100000);
          read_things();
      }

    }
  return 0;
}
