/*
 * $Id: main.c,v 1.37 2002/10/17 20:02:31 ljb 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 <pwd.h>
#include <grp.h>
#include <unistd.h>
#ifndef NT 
#include <sys/ioctl.h>
#ifndef SETPGRP_VOID
#include <sys/termios.h>
#endif /* SETPGRP_VOID */
#endif /* NT */
#include "mrt.h"
#include "trace.h"
#include "config_file.h"
#include "irrd.h"

trace_t *default_trace;
irr_t IRR = {0};

/* JW this is a hack and should be removed later
 * when atomic transactions are the default 
 */
int atomic_trans;

/* local functions */
void init_irrd_commands ();
void init_rps_regexes();
#ifndef NT
static void daemonize ();
#endif /* NT */

int main (int argc, char *argv[])
{
    char *name = argv[0];
    int c, x_flag = 1, v_flag = 0;
    int default_cache = 0;
    char *s;
    extern char *optarg;	/* getopt stuff */
    extern int optind;		/* getopt stuff */
    int errors = 0;
    int daemon = 1;
    irr_database_t tmp1;	/* just used to build linked list */
    irr_connection_t tmp2;	/* just used to build linked list */
    rollback_t rb_ll;           /* atomic transaction support */
    struct passwd *pw;
    struct group *gr;
    int group_id = 0, user_id = 0;
    char *user_name = NULL;
    char *group_name = NULL;
    char *password = NULL;
    char *usage = 
"Usage: %s\n"
"   [-a turn on atomic transaction mode]\n"
"   [-d <irr_directory>]\n"
"   [-f <irrd.conf file>]\n"
"   [-g <groupname>]\n"
"   [-l <username>]\n"
"   [-n do not daemonize]\n"
"   [-s <password>]\n"
"   [-u don't allow privileged commands]\n"
"   [-v verbose mode]\n"
"   [-w <irr_port>]\n"
"   [-x cancel bootstrap missing DB auto-fetch]\n";

#ifdef NT
    char *config_file = "C:/irrd.conf";
#else
    char *config_file = "/usr/local/etc/irrd.conf";
#endif /* NT */
    int UNPRIV_FLAG = 0;

#ifdef NT
    WSADATA WinsockData;
	
    if (WSAStartup(MAKEWORD(2, 2), &WinsockData) != 0) {
        printf ("Failed to find Winsock 2.2!\n"); 
        return -1;
    }
#endif /* NT */

    default_trace = New_Trace2 ("irrd");
    IRR.submit_trace = New_Trace2 ("irrd");
    set_trace (default_trace, TRACE_FLAGS, NORM, 0);
    atomic_trans = 0; /* default to off */

    /*
     * set some defaults 
     */
    IRR.max_connections = 25;
    IRR.mirror_interval = 60*10; /* mirror every ten minutes */
    IRR.irr_port = IRR_DEFAULT_PORT;
    IRR.database_syntax = EMPTY_DB; /* determine syntax when loading db's */
    IRR.tmp_dir = IRR_TMP_DIR;
    IRR.path = NULL;
    IRR.statusfile = NULL;

    while ((c = getopt (argc, argv, "w:axmrHhnkvf:ud:g:l:s:")) != -1)
	switch (c) {
	case 'a':
	  atomic_trans = 1;
	  break;
	case 'd':	/* database directory */
	  IRR.database_dir = optarg;
	  break;
	case 'f':		/* config file */
	    config_file = optarg;
	    break;
	case 'g':	/* setgid to this group -- dropping priv's is good */
	  group_name = optarg;
	  if (group_name == NULL) {
	    fprintf(stderr, "null group defined\n");
	    exit(-1);
	  }
	  gr = getgrnam(group_name);
	  if (gr == NULL) {
	      fprintf(stderr, "unknown group: \"%s\"\n", group_name);
	      exit(-1);
	  }
	  group_id = gr->gr_gid;
	  break;
	case 'l':	/* setuid to this user -- dropping priv's is good */
	  user_name = optarg;
	  if (user_name == NULL) {
	    fprintf(stderr, "null user defined\n");
	    exit(-1);
	  }
	  pw = getpwnam(user_name);
	  if (pw == NULL) {
	      fprintf(stderr, "unknown user: \"%s\"\n", user_name);
              exit(-1);
 	  }
	  user_id = pw->pw_uid;
	  break;
	case 'n':		/* do not daemonize */
	  daemon = 0;
	  break;
	case 's':
	  password = strdup ((char *) optarg);
	  break;
	case 'u':
	  /* don't allow privlidged commands -- we're a public server */
	  UNPRIV_FLAG = 1; 
	  break;
	case 'v':		/* verbose */
	    set_trace (default_trace, TRACE_FLAGS, TR_ALL,
		       TRACE_LOGFILE, "stdout",
		       NULL);
	    daemon = 0;
	    v_flag = 1;
	    break;
	case 'w':
	  IRR.irr_port = (u_short) atol (optarg);
	  break;
	case 'x':
	  x_flag = 0;
	  break;
	case 'm':	/* old command line options -- no longer do anything */
	case 'r':
	  break;
	case 'H':
	case 'h':
	default:
	  errors++;
	  break;
	}

    if (errors) {
	fprintf (stderr, usage, name);
	printf ("\nIRRd %s compiled on %s\n",
		IRRD_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);

    set_uii (UII, UII_PROMPT, UII_UNPREV, "Password: ", 0);
    set_uii (UII, UII_PROMPT, UII_NORMAL, "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_database_alphabetized = LL_Create (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 ();
    }

    /* default the cache if the user does not specify */
    /* should also set default statusfile here? */
    if (IRR.database_dir == NULL) {
      IRR.database_dir = strdup (DEF_DBCACHE);
      default_cache = 1;
    }

    if (IRR.statusfile == NULL) {
      char statusfilename[512];

      *statusfilename = 0;
      strcat(statusfilename, IRR.database_dir);
      strcat(statusfilename, "/IRRD_STATUS");
      IRR.statusfile = InitStatusFile(statusfilename);
    }
    
    /* sort the databases alphabetically */
    irr_sort_database ();
  
    if (password != NULL) 
      UII->password = password;

    /* first time access -- no password  
    if (UII->password == NULL) {
      UII->initial_state = UII_NORMAL;
      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);
	*/
	set_uii (UII, UII_PROMPT, UII_UNPREV, "Password: ", 0);
    set_uii (UII, UII_PROMPT, UII_NORMAL, "IRRd> ", 0);
    set_uii (UII, UII_PROMPT, UII_ENABLE, "IRRd# ", 0);
    set_uii (UII, UII_PROMPT, UII_CONFIG, "Config> ", 0);
    set_uii (UII, UII_PROMPT, UII_CONFIG_INTERFACE, "Interface> ", 0);
    set_uii (UII, UII_PROMPT, UII_CONFIG_LINE, "Line> ", 0);

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

    /* 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);
    }

    /* did the user fail to specify a DB cache? */
    if (v_flag &&
	default_cache) {
	printf ("WARNING: No DB cache specified\n");
	printf ("WARNING: This means you have no data and all queries will return NULL\n");
	interactive_io ("Do you want to continue?");
    }
      
    /* DB cache checks.
     *
     * if the DB cache does not exist or there are system problems write
     * them to log.  Also try to create the cache should it
     * not exist */
    if ((s = dir_chks (IRR.database_dir, 1)) != NULL) {
      trace (ERROR, default_trace, "WARNING: %s\n", s);
      if (v_flag) {
	printf ("WARNING: %s\n", s);
	free (s);
	interactive_io ("Do you want to continue?");
      }
    }

    /* check for any transactions that did not complete */
    if (atomic_trans) {
      rb_ll.first = rb_ll.last = NULL;
      /* control process of rolling back any DB's which did not complete */
      if (!rollback_check (&rb_ll)) {
	trace (ERROR, default_trace, "Error occured during bootstrap transaction "
	       "check.  Need DB admin to assist.  exit (0)\n");
	fprintf (stderr, "Error occured during bootstrap transaction "
		 "check.  Need DB admin to assist.  exit (0)\n");
	exit (0);
      }
    }

    /* load the DB's and build the indexes */
    c = irr_load_data (x_flag, v_flag);

    /* re-apply any transactions that didn't complete */
    if (atomic_trans        && 
	rb_ll.first != NULL &&
	!reapply_transaction (&rb_ll)) {
      trace (ERROR, default_trace, "Error occured during bootstrap transaction "
	     "reapplication.  Need DB admin to assist.  exit (0)\n");
      fprintf (stderr, "Error occured during bootstrap transaction "
	       "reapplication.  Need DB admin to assist.  exit (0)\n");
      exit (0);
    }
    
    /* Checks for the newbie
     *
     *  -warn if no DB's were config'd
     *  -warn if there are empty DB's
     */
    if (v_flag) {
      /* no configured DB's found in irrd.conf file */
      switch (c) {
      case -1: /* No DB's config'd */
	printf ("WARNING: No DB's configured\n");
	interactive_io ("Do you want to continue?");
	break;
      case 0:  /* No problems */
	break;
      default: /* 1..N empty DB's */
	printf ( 
	       "WARNING: (%d) DB's were found empty on bootstrap and "
	       "could not be fetched\n", c);
	printf ("WARNING: These DB's will return NULL results on queries.\n");
	interactive_io ("Do you want to continue?");
	break;
      }
    }

    /* 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);
    }

    /* drop privileges */
    if (group_name != NULL)
            if (setgid(group_id) < 0) {
               fprintf(stderr, "setuid(%d): %s", (int)group_id,
                 strerror(errno));
               exit (-1);
            }
 
 
    if (user_name != NULL) {
            if (getuid() == 0 && initgroups(user_name, group_id) < 0) {
                fprintf(stderr, "initgroups(%s, %d): %s",
                  user_name, (int)group_id, strerror(errno));
                exit (-1);
            }
 
            endgrent();
            endpwent();
            if (setuid(user_id) < 0) {
               fprintf(stderr, "setuid(%d): %s", user_id, strerror(errno));
               exit (-1);
            }
    }

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

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

    t = 0; /* This gets rid of a warning when t is #ifdef'd out of existance */

    /* 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);
}
#endif /* NT */

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");

#ifndef NT
    uii_add_command2 (UII_NORMAL, COMMAND_NORM, "irrdcacher %s", 
		      (int (*)()) uii_irr_irrdcacher, 
		      "Fetch a remote database and reload using irrdcacher");
#endif /* NT */

    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_clean,
		      "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", 
		      (int (*)()) kill_irrd,
		      "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_NORMAL, COMMAND_NORM, "show mirror-status %s",
                    (int (*)()) uii_mirrorstatus_db,
		    "Get Database Mirror Status");

  uii_add_command2 (UII_NORMAL, COMMAND_NORM, "show statusfile",
                    (int (*)()) uii_show_statusfile,
		    "Show statusfile location.");

  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");

#ifndef NT
  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "irr_database %s remote_ftp_url %s", 
		    (int (*)()) config_irr_remote_ftp_url,
		    "Configure remote the ftp URL for irrdcacher fetches");

  /* JW: probably will take out at a later time.  For now consoladating
     into a single command.

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "irr_database %s repos_hexid %s", 
		    (int (*)()) config_irr_repos_hexid,
		    "Repository hex ID of the PGP key associated with this DB");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "irr_database %s pgppass %s", 
		    (int (*)()) config_irr_pgppass,
		    "PGP password for this DB used to sign floods");
  */
#endif /* NT */

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "rpsdist_database %s",
		    (int (*)()) config_rpsdist_database,
		    "rpsdist database files to use");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "rpsdist_database %s trusted %s %d",
		    (int (*)()) config_rpsdist_database_trusted,
		    "Trusted rpsdist database configuration");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "rpsdist_database %s authoritative %s"
		    , (int (*)()) config_rpsdist_database_authoritative,
		    "Authoritative rpsdist database configuration");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "rpsdist accept %s",
		    (int (*)()) config_rpsdist_accept,
		    "Accept rpsdist connections from host");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "rpsdist %s accept %s",
		    (int (*)()) config_rpsdist_accept_database,
		    "Accept rpsdist connections for database from host");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, 
		    "irr_database %s no-default",
		    (int (*)()) config_irr_database_nodefault,
		    "Do not include this database by default when responding to queries");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_path %s", 
		    (int (*)()) config_irr_path,
		    "Add a component(s) to your PATH environment variable");

  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 an 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_2,
		    "The \"IRRd\" whois interface query port");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "irr_port %d access %d", 
		    (int (*)()) config_irr_port,
		    "The \"IRRd\" whois interface query 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 server verbose", 
		    config_no_debug_server_verbose, 
		    "Turn off verbose logging");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "statusfile %s",
                    config_statusfile, "set statusfile name");
  
  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");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "response_notify_header %S",
		    (int (*)()) config_response_notify_header,
		    "Text at top of email responses");

  uii_add_command2 (UII_CONFIG, COMMAND_NORM, "response_forward_header %S",
		    (int (*)()) config_response_forward_header,
		    "Text at top of forwarded email responses");

}



syntax highlighted by Code2HTML, v. 0.9.1