/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2007
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    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, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    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
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* init.c :: Midnight Code library based system initialiser		*/


#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <linux/wait.h>
#include <sys/wait.h>
#include <sys/reboot.h>
#include <sys/mount.h>
#include <signal.h>
#include <errno.h>
#include <syslog.h>

#include <libmidnightcode.h>
#include "init.h"


/* Global variables - allow ongoing system manipulation */

struct midnightcode_config_head_t * config_list_head_p;
struct midnightcode_disk_head_t * config_disk_list_head_p;
struct midnightcode_interface_head_t * config_interface_list_head_p;

struct midnightcode_socket_head_t * sockets;
struct midnightcode_disk_head_t * disk_list_head_p;
struct midnightcode_interface_head_t * interface_list_head_p;
struct midnightcode_route_head_t * route_list_head_p;
struct midnightcode_resolve_domain_head_t * nsdomain_list_head_p;
struct midnightcode_resolve_server_head_t * nsserver_list_head_p;
struct midnightcode_camera_head_t * camera_list_head_p;

struct midnightcode_interface_element_t * primary_interface;
struct midnightcode_disk_element_t * disk;
struct midnightcode_disk_element_t * old_disk;

int system_halt;
int system_restart;
int signal_continue;
int boot_device;
int boot_config;
char config_device[128];
char config_file[128];
int config_device_mounted;



int
init_system(int runlevel) {
  int terminal_fd;
  int ret_i;


  if(getuid() != 0)
    return -1;
  if(getpid() != 1)
    return -1;
  if(runlevel < 0 || runlevel > 6)
    return -1;


  switch(runlevel) {


    case MIDNIGHTCODE_INIT_RUNLEVEL_RESTART:

      midnightcode_terminal_console_usurp(&terminal_fd);

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Rebooting %s", project);

      sleep(1);
      sync();

      /*
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Suspending openMosix;");
      midnightcode_init_sysctl(
        "Block foreign migrating processes", "hpc:admin:block", "1");
      midnightcode_init_sysctl(
        "Bring home migrated processes", "hpc:admin:bring", "1");
      midnightcode_init_sysctl(
        "Expel foreign migrated processes", "hpc:admin:expel", "1");
      midnightcode_init_sysctl(
        "Local stay forced on home processes", "hpc:admin:lstay", "1");
      midnightcode_init_sysctl(
        "Stay forced on auto process migrator", "hpc:admin:stay", "1");
      sleep(4);
      midnightcode_init_process(
        "Disable openMosix", "/sbin/setpe -off",
        MIDNIGHTCODE_PROCESS_OPTION_WAITTERM|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      */

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Stopping system services;");
      midnightcode_init_process(
        "Stopping custom services", "/etc/rc.local stop",
        MIDNIGHTCODE_PROCESS_OPTION_WAITTERM|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);

      // umount all

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Final call;");
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Restarting system");
      if(reboot(RB_AUTOBOOT)) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      }

      return 0;


    case MIDNIGHTCODE_INIT_RUNLEVEL_GUI:

      return 1;


    case MIDNIGHTCODE_INIT_RUNLEVEL_VOID:

      return 1;


    case MIDNIGHTCODE_INIT_RUNLEVEL_FULL:

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Initialising %s", project);

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Enabling kernel interfaces;");
      midnightcode_init_mountfs(MIDNIGHTCODE_INIT_FS_PROC |
        MIDNIGHTCODE_INIT_FS_SYS | MIDNIGHTCODE_INIT_FS_PTS);
      midnightcode_init_process(
        "Starting system dev service", "/sbin/udevd -d",
        MIDNIGHTCODE_PROCESS_OPTION_WAITTERM|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      midnightcode_init_process(
        "Initialise system dev service", "/sbin/udevstart -g",
        MIDNIGHTCODE_PROCESS_OPTION_WAITTERM|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      midnightcode_init_sysctl(
        "Set watchdog restart value", "sys:kernel:panic", "3");
      midnightcode_init_process(
        "Starting system log service", "/sbin/syslogd -C256",
        MIDNIGHTCODE_PROCESS_OPTION_WAITTERM|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      midnightcode_init_process(
        "Starting kernel log service", "/sbin/klogd -c 4",
        MIDNIGHTCODE_PROCESS_OPTION_WAITTERM|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      midnightcode_init_process(
        "Starting PCMCIA service", "/sbin/pcmcia-socket-startup",
        MIDNIGHTCODE_PROCESS_OPTION_WAITTERM|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      sync();

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Probing system hardware;");
      midnightcode_init_hardware(disk_list_head_p, interface_list_head_p);
      midnightcode_init_peripherals(camera_list_head_p);
      midnightcode_init_networking(interface_list_head_p, &primary_interface);


      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Retrieving system configuration;");
      memset(config_file, 0, sizeof(config_file));
      memset(config_device, 0, sizeof(config_device));
      boot_config = 0;
      switch(boot_device) {

        case MIDNIGHTCODE_INIT_BOOTDEV_USB:
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
            MIDNIGHTCODE_STATUS_STYLE_NORMAL,
            "Boot source is USB");
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
          if(midnightcode_init_usbmedia(&disk_list_head_p, project,
            &disk, 20) == 1) {
            boot_config = 1;
            snprintf(config_device, sizeof(config_device) - 1,
              "%s2", disk->device);
            if(access(config_device, F_OK) != 0) {
              midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                "Configuration device is invalid");
              midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
              boot_config = 0;
            }
            snprintf(config_file, sizeof(config_file) - 1,
              "%s", "/mnt/config/device.cfg");
          }
          break;

        case MIDNIGHTCODE_INIT_BOOTDEV_PXE:
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
            MIDNIGHTCODE_STATUS_STYLE_NORMAL,
            "Boot source is PXE");
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
          boot_config = 1;
          /* TODO: TFTP Config file */
          break;

        case MIDNIGHTCODE_INIT_BOOTDEV_DISC:
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
            MIDNIGHTCODE_STATUS_STYLE_NORMAL,
            "Boot source is CDROM");
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
          /* TODO: Assumed no config file .. ? */
          boot_config = 0;
          if(midnightcode_init_cdmedia(disk_list_head_p, project,
            &disk) == 1) {
            midnightcode_disk_trayopen(disk);
          }
          break;

        case MIDNIGHTCODE_INIT_BOOTDEV_DISK:
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
            MIDNIGHTCODE_STATUS_STYLE_NORMAL,
            "Boot source is Disk");
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
          boot_config = 0;
          break;

        default:
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
            MIDNIGHTCODE_STATUS_STYLE_NORMAL,
            "Boot source is unknown");
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
            MIDNIGHTCODE_STATUS_STYLE_NORMAL,
            "Configuration device/file is unknown");
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      }
      if(boot_config) {
        if(strlen(config_device) == 0 && strlen(config_file) == 0) {
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
            MIDNIGHTCODE_STATUS_STYLE_NORMAL,
            "Configuration device/file is unknown");
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
          boot_config = 0;
        } else {
          if(boot_config && strlen(config_device) > 0) {
            midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
              MIDNIGHTCODE_STATUS_STYLE_NORMAL,
              "Mounting configuration device");
            if(mount(config_device, "/mnt/config", "vfat",
              MS_MGC_VAL|MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_SYNCHRONOUS,
              NULL) != 0) {
              midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
              boot_config = 0;
            } else {
              midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
              config_device_mounted = 1;
            }
          }
          if(boot_config && strlen(config_file) > 0) {
            if(midnightcode_init_config_load(config_file,
              config_list_head_p, 0) != 0) {
              boot_config = 0;
            }
          }
          if(config_device_mounted) {
            midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
              MIDNIGHTCODE_STATUS_STYLE_NORMAL,
              "Unmounting configuration device");
            if(umount("/mnt/config") != 0) {
              midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
            } else {
              midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
              config_device_mounted = 0;
            }
          }
        }
      }
      if(!boot_config) {
        memset(config_file, 0, sizeof(config_file));
        snprintf(config_file, sizeof(config_file) - 1,
          "%s", "/etc/default.cfg");
        if(midnightcode_init_config_load(config_file,
          config_list_head_p, 1) != 0) {
          /* NOTE: That would be bad */
        }
      }


      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Establishing system-wide settings;");
      midnightcode_init_config_retrieve(config_disk_list_head_p,
        config_interface_list_head_p, route_list_head_p,
        nsdomain_list_head_p, nsserver_list_head_p,
        config_list_head_p);

    
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Preset network subsystem options;");
      midnightcode_init_sysctl(
        "Set receive socket queue length",
        "sys:net:core:netdev_max_backlog", "2000");
      midnightcode_init_sysctl(
        "Disable IPv4 source routing",
        "sys:net:ipv4:conf:default:accept_source_route", "0");
      midnightcode_init_sysctl(
        "Enable IPv4 reverse path filtering",
        "sys:net:ipv4:conf:default:rp_filter", "1");
      midnightcode_init_sysctl(
        "Ignore IPv4 ICMP Redirect messages",
        "sys:net:ipv4:conf:all:accept_redirects", "0");
      midnightcode_init_sysctl(
        "Ignore IPv4 ICMP ECHO broadcasts",
        "sys:net:ipv4:icmp_echo_ignore_broadcasts", "1");
      midnightcode_init_sysctl(
        "Ignore IPv4 bogus ICMP errors",
        "sys:net:ipv4:icmp_ignore_bogus_error_responses", "1");
      midnightcode_init_sysctl(
        "Disable IPv4 TCP selective acknowledgements",
        "sys:net:ipv4:tcp_sack", "0");
      midnightcode_init_sysctl(
        "Enable IPv4 TCP SYN-cookies", "sys:net:ipv4:tcp_syncookies", "1");
      midnightcode_init_sysctl(
        "Set IPv4 TCP FIN timeout", "sys:net:ipv4:tcp_fin_timeout", "30");
      midnightcode_init_sysctl(
        "Set IPv4 TCP keep-alive time",
        "sys:net:ipv4:tcp_keepalive_time", "600");
      midnightcode_init_sysctl(
        "Disable IPv4 TCP time-stamps", "sys:net:ipv4:tcp_timestamps", "0");

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Enabling network subsystem;");
      midnightcode_init_nic_lo(interface_list_head_p);
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Marry probed interfaces with configured interfaces");
      if(!boot_config) {
        ret_i = midnightcode_interface_marry(interface_list_head_p,
          config_interface_list_head_p, MIDNIGHTCODE_INTERFACE_MARRY_BASIC|
          MIDNIGHTCODE_INTERFACE_MARRY_ONNAME);
      } else {
        ret_i = midnightcode_interface_marry(interface_list_head_p,
          config_interface_list_head_p, MIDNIGHTCODE_INTERFACE_MARRY_DEFAULT);
      }
      if(ret_i < 0) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        if(ret_i == 0) {
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_WARN);
        } else {
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
        }
      }

      // plumb eth*
      // -- start dhcp
      // -- find routed eth*
      // unplumb eth*

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Writing hosts system file");
      if(midnightcode_interface_writehosts("/etc/hosts",
        interface_list_head_p) < 0) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Writing resolver system file");
      if(midnightcode_resolve_writeconf("/etc/resolv.conf",
        nsdomain_list_head_p, nsserver_list_head_p) < 0) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Enabling disk subsystem;");

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Starting system services;");
      // ala config file ?
      if(access("/proc/acpi/event", R_OK) == 0) {
        midnightcode_init_process(
          "Starting power management service", "/sbin/acpid",
          MIDNIGHTCODE_PROCESS_OPTION_BACKGROUND|
          MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
          MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      }
      midnightcode_init_process(
        "Starting admin web service", "/sbin/thttpd -C /etc/httpd/adm.conf",
        MIDNIGHTCODE_PROCESS_OPTION_BACKGROUND|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      midnightcode_init_process(
        "Starting time service", "/sbin/ntpd -g",
        MIDNIGHTCODE_PROCESS_OPTION_BACKGROUND|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      midnightcode_init_process(
        "Starting user web service", "/sbin/thttpd -C /etc/httpd/usr.conf",
        MIDNIGHTCODE_PROCESS_OPTION_BACKGROUND|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      midnightcode_init_process(
        "Starting secure shell service", "/sbin/init-sshd all",
        MIDNIGHTCODE_PROCESS_OPTION_BACKGROUND|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);
      midnightcode_init_process(
        "Starting custom services", "/etc/rc.local start",
        MIDNIGHTCODE_PROCESS_OPTION_BACKGROUND|
        MIDNIGHTCODE_PROCESS_OPTION_NULLIO,
        MIDNIGHTCODE_TERMINAL_VIRTUAL_NULL);

      /*
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Enabling openMosix;");
      midnightcode_init_sysctl(
        "Permit foreign migrating processes", "hpc:admin:block", "0");
      midnightcode_init_sysctl(
        "Forward home migrated processes", "hpc:admin:bring", "0");
      midnightcode_init_sysctl(
        "Permit foreign migrated processes", "hpc:admin:expel", "0");
      midnightcode_init_sysctl(
        "Local stay disabled on home processes", "hpc:admin:lstay", "0");
      midnightcode_init_sysctl(
        "Stay disabled on auto process migrator", "hpc:admin:stay", "0");
      */

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Adding routes to config");
      if(midnightcode_route_to_config(route_list_head_p,
        config_list_head_p) != 0) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Adding interfaces to config");
      if(midnightcode_interface_to_config(interface_list_head_p,
        config_list_head_p) != 0) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Adding disks to config");
      if(midnightcode_disk_to_config(disk_list_head_p,
        config_list_head_p) != 0) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Config write to file");
      if(midnightcode_config_write_file("/tmp/out.cfg",
        config_list_head_p, MIDNIGHTCODE_CONFIG_TYPE_NEWSET, 0) != 0) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Initialised %s", project);

      return  0;


    case MIDNIGHTCODE_INIT_RUNLEVEL_NONET:

      return 1;


    case MIDNIGHTCODE_INIT_RUNLEVEL_REPAIR:

      return 1;


    case MIDNIGHTCODE_INIT_RUNLEVEL_HALT:

      midnightcode_terminal_console_usurp(&terminal_fd);

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL, "Halting %s", project);

      sleep(1);
      sync();

      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H2,
        MIDNIGHTCODE_STATUS_STYLE_SOFT,
        "Final call;");
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
        MIDNIGHTCODE_STATUS_STYLE_NORMAL,
        "Halting system");
      if(reboot(RB_POWER_OFF)) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      }

      return 0;



    default:
      return 1;

  }


  return 0;

}



void
sighandler_active(int signal) {
  int child_pid;
  int child_status;


  switch(signal) {

    case SIGCHLD:
      while((child_pid = waitpid(-1, &child_status, WNOHANG)) != 0) {
        if(errno == ECHILD)
          break;
      }
      break;

    case SIGCONT:
      signal_continue = 1;
      break;
 
    case SIGSTOP:
    case SIGTSTP:
      signal_continue = 0;
      while(!signal_continue)
        pause();
      break;

    case SIGTERM:
    case SIGPWR:
      syslog(LOG_CRIT, "Received SIGTERM/SIGPWR [halt=%d,rest=%d]",
        system_halt, system_restart);
      if(system_restart == 0)
        system_halt = 1;
      break;

    case SIGQUIT:
    case SIGINT:
      syslog(LOG_CRIT, "Received SIGQUIT/SIGINT [halt=%d,rest=%d]",
        system_halt, system_restart);
      if(system_halt == 0)
        system_restart = 1;
      break;

    default:
      break;

  }


  return;
}



int
main(int argc, char **argv) {
  int console_fd;
  int signal_n;
  struct sigaction sigact;
  void * tmp_void_p;
  char * environment;
  int ret_i;
  int help = 0;

  struct midnightcode_switch_t switches[] = {
    { "h", "help", "Display this help/usage screen",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &help },
    { 0 }
  };


  /* NOTE: Initialise permanent structures */
  midnightcode_list_init(socket_struct_head, midnightcode_socket_element_t);
  tmp_void_p = (void *)&socket_struct_head;
  sockets = tmp_void_p;

  midnightcode_list_init(config_struct_head, midnightcode_config_element_t);
  tmp_void_p = (void *)&config_struct_head;
  config_list_head_p = tmp_void_p;

  midnightcode_list_init(config_disk_struct_head, midnightcode_disk_element_t);
  tmp_void_p = (void *)&config_disk_struct_head;
  config_disk_list_head_p = tmp_void_p;

  midnightcode_list_init(config_interface_struct_head,
    midnightcode_interface_element_t);
  tmp_void_p = (void *)&config_interface_struct_head;
  config_interface_list_head_p = tmp_void_p;

  midnightcode_list_init(disk_struct_head, midnightcode_disk_element_t);
  tmp_void_p = (void *)&disk_struct_head;
  disk_list_head_p = tmp_void_p;

  midnightcode_list_init(interface_struct_head,
    midnightcode_interface_element_t);
  tmp_void_p = (void *)&interface_struct_head;
  interface_list_head_p = tmp_void_p;

  midnightcode_list_init(route_struct_head, midnightcode_route_element_t);
  tmp_void_p = (void *)&route_struct_head;
  route_list_head_p = tmp_void_p;

  midnightcode_list_init(nsdomain_struct_head, midnightcode_domain_element_t);
  tmp_void_p = (void *)&nsdomain_struct_head;
  nsdomain_list_head_p = tmp_void_p;

  midnightcode_list_init(nsserver_struct_head, midnightcode_server_element_t);
  tmp_void_p = (void *)&nsserver_struct_head;
  nsserver_list_head_p = tmp_void_p;

  midnightcode_list_init(camera_struct_head, midnightcode_camera_element_t);
  tmp_void_p = (void *)&camera_struct_head;
  camera_list_head_p = tmp_void_p;


  midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);

  if((ret_i = midnightcode_switch_process(argc, argv, switches)) != 0) {
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    fprintf(stdout, "    Error in switches, switch \"%s\" is unkown at"
      " parameter number %d.\n\n", argv[ret_i], ret_i);
    midnightcode_switch_usage(MIDNIGHTCODE_SWITCH_STYLE_SOFT,
      argv[0], switches);
    fflush(stdout);
    exit(1);
  }
  if(help) {
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    midnightcode_switch_usage(MIDNIGHTCODE_SWITCH_STYLE_SOFT,
      argv[0], switches);
    fflush(stdout);
    exit(1);
  }

  midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
    MIDNIGHTCODE_LICENSE_TEXT_ALL);
  fflush(stdout);

  if(getpid() == 1) {

    config_device_mounted = 0;

    boot_device = MIDNIGHTCODE_INIT_BOOTDEV_UNKNOWN;
    if((environment = getenv("BOOTDEV")) != NULL) {
      boot_device = atoi(environment);
      if(boot_device < MIDNIGHTCODE_INIT_BOOTDEV_USB ||
        boot_device > MIDNIGHTCODE_INIT_BOOTDEV_DISK)
        boot_device = MIDNIGHTCODE_INIT_BOOTDEV_UNKNOWN;
    }

    midnightcode_init_boot(MIDNIGHTCODE_INIT_BOOT_SOFT);
    
    for(signal_n = 1; signal_n <= NSIG; signal_n++)
      SETSIG(sigact, signal_n, SIG_IGN, SA_RESTART);

    SETSIG(sigact, SIGALRM,  sighandler_active, 0);
    SETSIG(sigact, SIGCHLD,  sighandler_active, SA_RESTART);
    SETSIG(sigact, SIGCONT,  sighandler_active, SA_RESTART);
    SETSIG(sigact, SIGHUP,   sighandler_active, SA_RESTART);
    SETSIG(sigact, SIGINT,   sighandler_active, 0);
    SETSIG(sigact, SIGPWR,   sighandler_active, 0);
    SETSIG(sigact, SIGQUIT,  sighandler_active, 0);
    SETSIG(sigact, SIGSTOP,  sighandler_active, SA_RESTART);
    SETSIG(sigact, SIGTERM,  sighandler_active, 0);
    SETSIG(sigact, SIGTSTP,  sighandler_active, SA_RESTART);
    SETSIG(sigact, SIGUSR1,  sighandler_active, 0);
    SETSIG(sigact, SIGUSR2,  sighandler_active, 0);
    SETSIG(sigact, SIGWINCH, sighandler_active, SA_RESTART);
    /*
    SETSIG(sigact, SIGSEGV,  (void (*)(int))segv_handler, SA_RESTART);
    */

    midnightcode_terminal_console_close();
    midnightcode_terminal_console_usurp(&console_fd);
    midnightcode_terminal_environment(MIDNIGHTCODE_TERMINAL_ENVIRONMENT_PATH);

    init_system(MIDNIGHTCODE_INIT_RUNLEVEL_FULL);
    fflush(stdout);

    midnightcode_terminal_console_close();
    midnightcode_terminal_virtuals_open(4,
      MIDNIGHTCODE_TERMINAL_DISPLAY_LOGO |
      MIDNIGHTCODE_TERMINAL_DISPLAY_ISSUE |
      MIDNIGHTCODE_TERMINAL_DISPLAY_SHELL);

    while(1) {

      system_halt = 0;
      system_restart = 0;

      pause();

      if(system_halt == 1) {
        init_system(MIDNIGHTCODE_INIT_RUNLEVEL_HALT);
        exit(MIDNIGHTCODE_INIT_RUNLEVEL_HALT);
      }

      if(system_restart == 1) {
        init_system(MIDNIGHTCODE_INIT_RUNLEVEL_RESTART);
        exit(MIDNIGHTCODE_INIT_RUNLEVEL_RESTART);
      }

    }

  } else {

    if(argc == 2) {

      argv++;

      if(atoi(*argv) == MIDNIGHTCODE_INIT_RUNLEVEL_RESTART) {
        kill(1, SIGINT);
        exit(0);
      }

      if(atoi(*argv) == MIDNIGHTCODE_INIT_RUNLEVEL_HALT) {
        kill(1, SIGTERM);
        exit(0);
      }

    }

    exit(1);

  }


  return 0;
}

