 /*
 * $Id: main.c,v 1.45 1999/10/18 18:41:22 labovit Exp $
 * originally Id: main.c,v 1.57 1998/08/03 17:29:08 gerald Exp 
 */

#include <stdio.h>
#include <string.h>
#ifdef HAVE_STROPTS_H
#include <stropts.h>  /* need for OSF? */
#endif /* HAVE_STROPTS_H */
#include <ctype.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <sys/ioctl.h>
#ifndef SETPGRP_VOID
#include <sys/termios.h>
#endif
#include "mrt.h"
#include "trace.h"
#include "config_file.h"
#include "irrd.h"

trace_t *default_trace;
irr_t IRR;

/* local functions */
void init_irrd_commands ();
static void daemonize ();

int main (int argc, char *argv[])
{
    char *name = argv[0];
    int c;
    extern char *optarg;	/* getopt stuff */
    extern int optind;		/* getopt stuff */
    int errors = 0;
    int daemon = 1;
    char *port = "mrt";
    irr_database_t tmp1;	/* just used to build linked list */
    irr_connection_t tmp2;	/* just used to build linked list */
    char *password = NULL;
    char *usage = "Usage: %s [-f config_file] [-p uii_port ] [-d irr_directory] [-v] [-s]\n";
    char *config_file = "/etc/irrd.conf";
    int UNPRIV_FLAG = 0;

    default_trace = New_Trace ();
    IRR.submit_trace = New_Trace ();
    set_trace (default_trace, TRACE_FLAGS, NORM, 0);

    /*
     * set some defaults 
     */
    IRR.max_connections = 25;
    IRR.mirror_interval = 60*10; /* mirror every ten minutes */
    IRR.irr_port = IRR_DEFAULT_PORT;
    IRR.database_dir = strdup ("/var/spool/database/");
    IRR.use_cache = 0; /* cache for "!gas queries */
    IRR.database_syntax = EMPTY_DB; /* determine syntax when loading db's */
    IRR.rebuild_indicies = 0; /* don't rebuild disk indicies -- use what we have */
#if (defined(USE_GDBM) || defined(USE_DB1))
    IRR.use_disk = 1;
#endif /* USE_GDBM || USE_DB1 */


    while ((c = getopt (argc, argv, "w:mrhnkvf:p:ud:s:")) != -1)
	switch (c) {
	case 'v':		/* verbose */
	    set_trace (default_trace, TRACE_FLAGS, TR_ALL,
		       TRACE_LOGFILE, "stdout",
		       NULL);
	    daemon = 0;
	    break;
	case 'f':		/* config file */
	    config_file = optarg;
	    break;
	case 'n':		/* do not daemonize */
	  daemon = 0;
	  break;
	case 'p':		/* uii port number */
	    port = (char *) optarg;
	    break;
	case 'm':	/* use only memory */
	  IRR.use_disk = 0;
	  break;
	case 'u':
	  /* don't allow privlidged commands -- we're a public server */
	  UNPRIV_FLAG = 1; 
	  break;
	case 'd':	/* database directory */
	  IRR.database_dir = optarg;
	  break;
	case 'r':	/* by default, we use gdbm files already on disk */
	  IRR.rebuild_indicies = 1;
	  break;
	case 'w':
	  IRR.irr_port = (u_short) atol (optarg);
	  break;
	case 's':
	  password = strdup ((char *) optarg);
	  break;
	case 'h':
	default:
	    errors++;
	    break;
	}


    if (errors) {
	fprintf (stderr, usage, name);
	printf ("\nIRRd %s compiled on %s\n",
		MRT_VERSION, __DATE__);
	exit (1);
    }

    init_trace (name, daemon);
    set_trace(default_trace, TRACE_PREPEND_STRING, "IRRD", 0);

    init_mrt (default_trace);
    init_mrt_reboot (argc, argv);
    init_uii (default_trace);
 
    if (UNPRIV_FLAG)
      UII->initial_state = 1;
    else 
      UII->initial_state = 0;

    uii_add_command2 (UII_UNPREV, COMMAND_MATCH_FIRST, "", uii_check_passwd,
		      "Password");
    set_uii (UII, UII_PROMPT, 0, "password> ", 0);
    set_uii (UII, UII_PROMPT, 1, "IRRd> ", 0);

    init_irrd_commands (UNPRIV_FLAG);

    /* default trace flag should be set by here */
    IRR.ll_database = LL_Create (LL_Intrusive, True, 
				 LL_NextOffset, LL_Offset (&tmp1, &tmp1.next),
				 LL_PrevOffset, LL_Offset (&tmp1, &tmp1.prev),
				 0);
    IRR.ll_connections = LL_Create (LL_Intrusive, True, 
				    LL_NextOffset, LL_Offset (&tmp2, &tmp2.next),
				    LL_PrevOffset, LL_Offset (&tmp2, &tmp2.prev),
				    0);

    /* add mutex lock for routines wishing to lock all database */
    pthread_mutex_init (&IRR.lock_all_mutex_lock, NULL);

    /* initialize the mutex */
    pthread_mutex_init (&IRR.connections_mutex_lock, NULL);
  
    /*
     * read configuration here
     */
    if (config_from_file (default_trace, config_file) < 0) {
      config_create_default ();
    }

    if (password != NULL) 
      UII->password = password;

    /* first time access -- no password */
    if (UII->password == NULL) {
      UII->initial_state = 1;
      set_uii (UII, UII_PROMPT, UII_NORMAL, "IRRd--Setup> ", 0);
    }
    else 
      set_uii (UII, UII_PROMPT, UII_NORMAL, "IRRd> ", 0);

    set_uii (UII, UII_PROMPT, UII_CONFIG, "Config> ", 0);

    if (daemon) {
	/*
	 * Now going into daemon mode 
	 */
	MRT->daemon_mode = 1;
	daemonize ();
    }

    /* listen for UII commands */
    if (listen_uii2 ("irrd") < 0) {
      fprintf (stderr, "**** Error could not bind to UII port. Is another irrd running?\n");
      exit (-1);
    }

    /* so UII works now */
#ifdef HAVE_LIBPTHREAD
    start_select ();
#endif /* HAVE_LIBPTHREAD */

    irr_load_data ();

    /* listen for whois queries */
    if ((IRR.sockfd = listen_telnet (IRR.irr_port)) < 0) {
      fprintf (stderr, 
	       "**** Error could not bind to port %d. Is another irrd running?\n",
	       IRR.irr_port);
      exit (-1);
    }

    mrt_main_loop ();
    /* NOT REACHED */
    return(-1);
}

static void daemonize ()
{
    int pid, t;
    int time_left;

    /* alarm's time may not inherited by fork */
    time_left  = alarm (0);

    if ((pid = fork ()) == -1) {
	perror ("fork");
	exit (1);
    }
    else if (pid != 0) {	/* parent */
	exit (0);
    }


#ifdef HAVE_SETSID
    (void) setsid();
#else
#ifdef SETPGRP_VOID
    if ((t = setpgrp ()) < 0) {
	perror ("setpgrp");
	exit (1);
    }

    signal (SIGHUP, SIG_IGN);

    /* fork again so that not being a process group leader */
    if ((pid = fork ()) == -1) {
	perror ("fork");
	exit (1);
    }
    else if (pid != 0) {	/* parent */
	exit (0);
    }

#else /* !SETPGRP_VOID */
    if ((t = setpgrp (0, getpid ())) < 0) {
	perror ("setpgrp");
	exit (1);
    }

    /* Remove our association with a controling tty */
    if ((t = open ("/dev/tty", O_RDWR, 0)) >= 0) {
      if (ioctl (t, TIOCNOTTY, NULL) < 0) {
	    perror ("TIOCNOTTY");
	    exit (1);
	}
	(void) close (t);
    }
#endif /* SETPGRP_VOID */

#ifdef notdef
    /* Close all open files --- XXX need to check for logfiles */
    for (t = 0; t < 255; t++)
	(void) close (t);
#endif
#endif /* HAVE_SETSID */

    /*  chdir ("/"); code rewrite needed in some places */
    umask (022);
    if (time_left)
      alarm (time_left);
}


void init_irrd_commands (int UNPRIV_FLAG) { 
  /* if unpriv flag NOT set, allow privlidged commands */
  if (!UNPRIV_FLAG) { 
    UII->initial_state = 0;
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "config", 
		      (int (*)()) start_config, 
		      "Configure IRRd options");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM,
		      "show config", show_config, 
		      "Display current configuration");
    uii_add_command2 (UII_CONFIG, COMMAND_NORM,
		      "show config", show_config, 
		      "Display current configuration");
    
    uii_add_command2 (UII_NORMAL, COMMAND_NORM,
		      "show timers", uii_show_timers, "Display timer information");
    
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "write", (int (*)()) config_write, 
		      "Save configuration to disk");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "reload %s", 
		      (int (*)()) uii_irr_reload, 
		      "Reload an IRR database file");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "mirror %s", 
		      (int (*)()) uii_irr_mirror_last,
		      "Synchronize database with remote server");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "mirror %s %d", 
		      (int (*)()) uii_irr_mirror_serial,
		      "Synchronize database with remote server, supply LAST");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "dbclean %s", 
		      (int (*)()) uii_irr_sync,
		      "Clean database -- remove deleted entries on disk");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "set serial %s %d", 
		      (int (*)()) uii_set_serial,
		      "Set the serial number of a database");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "reboot", (int (*)()) mrt_reboot,
		      "Reboot IRRd");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "export %s",
		      (int (*)()) uii_export_database, "Create copy of database for ftp");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "kill (for debugging)", 
		      (int (*)()) exit,
		      "Kill off the IRRd daemon");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "read %s database %s", 
		      (int (*)()) uii_read_update_file,
		      "Read an add/deleted update file (for debugging)");
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "delete route %sdatabase %m %dAS", 
		      (int (*)()) uii_delete_route,
		      "Delete a route");
  }
    
  uii_add_command2 (UII_NORMAL, COMMAND_NORM, "show connections", 
		    (int (*)()) show_connections,
		    "Show current IRR connections");

  uii_add_command2 (UII_NORMAL, COMMAND_NORM, "show database", 
		    (int (*)()) show_database,
		    "Show database status");
  uii_add_command2 (UII_NORMAL, COMMAND_NORM, "show ip %p [less|more]", 
		    (int (*)()) uii_show_ip,
		    "Show exact, less or more specific routes");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_directory %s", 
		    (int (*)()) config_irr_directory,
		    "Where the IRR database files live");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "ftp directory %s", 
		    (int (*)()) config_export_directory,
		    "Where we put the database for FTP");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "irr_database %s export %d [%s]", 
		    (int (*)()) config_irr_database_export,
		    "Configure the database for ftp export");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s", 
		    (int (*)()) config_irr_database,
		    "The IRR database files to use");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "no irr_database %s", 
		    (int (*)()) no_config_irr_database,
		    "Remove a IRR Database");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s mirror_host %s %d", 
		    (int (*)()) config_irr_database_mirror,
		    "The IRR database mirroring");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s authoritative", 
		    (int (*)()) config_irr_database_authoritative,
		    "Allow write updates for IRR database");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "no irr_database %s authoritative", 
		    (int (*)()) no_config_irr_database_authoritative,
		    "Disable authoritative for an IRR database");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s write-access %d", 
		    (int (*)()) config_irr_database_access_write,
		    "Access list for updates for IRR database");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s  access %d", 
		    (int (*)()) config_irr_database_access,
		    "Access list for IRR database");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s mirror-access %d", 
		    (int (*)()) config_irr_database_mirror_access,
		    "Access list for IRR mirroring");
  
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s filter %s", 
		    (int (*)()) config_irr_database_filter,
		    "Filter object from database");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s no-clean", 
		    (int (*)()) config_irr_database_no_clean,
		    "Turn off database cleaning");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_database %s clean %d", 
		    (int (*)()) config_irr_database_clean,
		    "Set database cleaning time");
     
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_port %d", 
		    (int (*)()) config_irr_port,
		    "The \"RAWhoisd\" telnet interface port");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_mirror_interval %d", 
		    (int (*)()) config_irr_mirror_interval,
		    "How often (seconds) between mirror updates");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_max_connections %d", 
		    (int (*)()) config_irr_max_con,
		    "The maximum number of simultaneous connections");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "no debug server", 
		    no_config_debug_server, "Turn off server logging");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "debug server file-name %s", 
		    config_debug_server_file, "Configure logging information");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "debug server file-max-size %d", 
		    config_debug_server_size, "Limit the size of the logfile");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "debug server syslog", 
		    config_debug_server_syslog, 
		    "Send log information to syslog");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "debug server verbose", 
		    config_debug_server_verbose, 
		    "Turn on verbose logging");
  
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "no debug submission", 
		    no_config_debug_submission, "Turn off submision logging");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "debug submission file-name %s", 
		    config_debug_submission_file, 
		    "Configure submission logging");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "debug submission verbose", 
		    config_debug_submission_verbose, 
		    "Turn on verbose submission logging");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "debug submission file-max-size %d", 
		    config_debug_submission_maxsize, 
		    "Maximum size of email/tcp submission log");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "debug submission syslog", 
		    config_debug_submission_syslog, 
		    "Use syslog for submission logging");

  
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "tmp directory %s", 
		    config_tmp_directory, 
		    "Configure a tmp/cache directory");

  /* pass through commands -- use by pipeline, but not us */
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "override_cryptpw %s",
		    (int (*)()) config_override,
		    "The override password");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "pgp_dir %s",
		    (int (*)()) config_pgpdir,
		    "Where to find the PGP keyring");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_server %s",
		    (int (*)()) config_irr_host,
		    "IRRd server where we send submissions");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "db_admin %s",
		    (int (*)()) config_dbadmin,
		    "DB-admin email address");
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "response_footer %S",
		    (int (*)()) config_response_footer,
		    "Text at bottom of email responses");

}
