Source to ./dynamips.c


Enter a symbol's name here to quickly find it.

/*
 * Cisco router simulation platform.
 * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
 *
 * Many thanks to Nicolas Szalay for his patch
 * for the command line parsing and virtual machine 
 * settings (RAM, ROM, NVRAM, ...)
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <signal.h>
#include <fcntl.h>
#include <assert.h>
#include <getopt.h>

#include "dynamips.h"
#include "gen_uuid.h"
#include "cpu.h"
#include "vm.h"
#include "tcb.h"
#include "mips64_exec.h"
#include "mips64_jit.h"
#include "ppc32_exec.h"
#include "ppc32_jit.h"
#include "dev_c7200.h"
#include "dev_c3600.h"
#include "dev_c2691.h"
#include "dev_c3725.h"
#include "dev_c3745.h"
#include "dev_c2600.h"
#include "dev_c1700.h"
#include "dev_c6msfc1.h"
#include "dev_c6sup1.h"
#include "ppc32_vmtest.h"
#include "dev_vtty.h"
#include "ptask.h"
#include "timer.h"
#include "plugin.h"
#include "registry.h"
#include "hypervisor.h"
#include "net_io.h"
#include "net_io_bridge.h"
#include "net_io_filter.h"
#include "crc.h"
#include "atm.h"
#include "atm_bridge.h"
#include "frame_relay.h"
#include "eth_switch.h"
#ifdef GEN_ETH
#include "gen_eth.h"
#endif
#ifdef PROFILE
#include "profiler.h"
#endif

/* Default name for logfile */
#define LOGFILE_DEFAULT_NAME  "dynamips_log.txt"

/* Operating system name */
const char *os_name = STRINGIFY(OSNAME);

/* Software version */
const char *sw_version = DYNAMIPS_VERSION"-"JIT_ARCH;

/* Software version tag */
const char *sw_version_tag = "2008040100";

/* Hypervisor */
int hypervisor_mode = 0;
int hypervisor_tcp_port = 0;
char *hypervisor_ip_address = NULL;

/* Log file */
char *log_file_name = NULL;
FILE *log_file = NULL;

/* VM flags */
volatile int vm_save_state = 0;

/* Default platform */
static char *default_platform = "7200";

/* Generic signal handler */
void signal_gen_handler(int sig)
{
   switch(sig) {
      case SIGHUP:
         /* For future use */
         break;

      case SIGQUIT:
         /* save VM context */
         vm_save_state = TRUE;
         break;

      case SIGINT:
         /* CTRL+C has been pressed */
         if (hypervisor_mode)
            hypervisor_stopsig();
         else {
            /* In theory, this shouldn't happen thanks to VTTY settings */
            vm_instance_t *vm;

            if ((vm = vm_acquire("default")) != NULL) {
               /* Only forward ctrl-c if user has requested local terminal */
               if (vm->vtty_con_type == VTTY_TYPE_TERM) {
                  vtty_store_ctrlc(vm->vtty_con);
               } else {
                  vm_stop(vm);
               }
               vm_release(vm);
            } else {
               fprintf(stderr,"Error: Cannot acquire instance handle.\n");
            }
         }
         break;

      default:
         fprintf(stderr,"Unhandled signal %d\n",sig);
   }
}

/* Setups signals */
static void setup_signals(void)
{
   struct sigaction act;

   memset(&act,0,sizeof(act));
   act.sa_handler = signal_gen_handler;
   act.sa_flags = SA_RESTART;
   sigaction(SIGHUP,&act,NULL);
   sigaction(SIGQUIT,&act,NULL);
   sigaction(SIGINT,&act,NULL);
}

/* Create general log file */
static void create_log_file(void)
{
   /* Set the default value of the log file name */
   if (!log_file_name) {
      if (!(log_file_name = strdup(LOGFILE_DEFAULT_NAME))) {
         fprintf(stderr,"Unable to set log file name.\n");
         exit(EXIT_FAILURE);
      }
   }

   if (!(log_file = fopen(log_file_name,"w"))) {
      fprintf(stderr,"Unable to create log file (%s).\n",strerror(errno));
      exit(EXIT_FAILURE);
   }
}

/* Close general log file */
static void close_log_file(void)
{
   if (log_file) fclose(log_file);
   free(log_file_name);

   log_file = NULL;
   log_file_name = NULL;
}

/* Display the command line use */
static void show_usage(vm_instance_t *vm,int argc,char *argv[])
{
   printf("Usage: %s [options] <ios_image>\n\n",argv[0]);
   
   printf("Available options:\n"
          "  -H [<ip_address>:]<tcp_port> : Run in hypervisor mode\n\n"
          "  -P <platform>      : Platform to emulate (7200, 3600, "
          "2691, 3725, 3745, 2600 or 1700) "
          "(default: 7200)\n\n"
          "  -l <log_file>      : Set logging file (default is %s)\n"
          "  -j                 : Disable the JIT compiler, very slow\n"
          "  --idle-pc <pc>     : Set the idle PC (default: disabled)\n"
          "  --timer-itv <val>  : Timer IRQ interval check (default: %u)\n"
          "\n"
          "  -i <instance>      : Set instance ID\n"
          "  -r <ram_size>      : Set the virtual RAM size (default: %u Mb)\n"
          "  -o <rom_size>      : Set the virtual ROM size (default: %u Mb)\n"
          "  -n <nvram_size>    : Set the NVRAM size (default: %d Kb)\n"
          "  -c <conf_reg>      : Set the configuration register "
          "(default: 0x%04x)\n"
          "  -m <mac_addr>      : Set the MAC address of the chassis\n"
          "                       (default: automatically generated)\n"
          "  -C <cfg_file>      : Import an IOS configuration file "
          "into NVRAM\n"
          "  -X                 : Do not use a file to simulate RAM (faster)\n"
          "  -G <ghost_file>    : Use a ghost file to simulate RAM\n"
          "  -g <ghost_file>    : Generate a ghost RAM file\n"
          "  --sparse-mem       : Use sparse memory\n"
          "  -R <rom_file>      : Load an alternate ROM (default: embedded)\n"
          "  -k <clock_div>     : Set the clock divisor (default: %d)\n"
          "\n"
          "  -T <port>          : Console is on TCP <port>\n"
          "  -U <si_desc>       : Console in on serial interface <si_desc>\n"
          "                       (default is on the terminal)\n"
          "\n"
          "  -A <port>          : AUX is on TCP <port>\n"
          "  -B <si_desc>       : AUX is on serial interface <si_desc>\n"
          "                       (default is no AUX port)\n"
          "\n"
          "  --disk0 <size>     : Set PCMCIA ATA disk0: size "
          "(default: %u Mb)\n"
          "  --disk1 <size>     : Set PCMCIA ATA disk1: size "
          "(default: %u Mb)\n"
          "\n",
          LOGFILE_DEFAULT_NAME,VM_TIMER_IRQ_CHECK_ITV,
          vm->ram_size,vm->rom_size,vm->nvram_size,vm->conf_reg_setup,
          vm->clock_divisor,vm->pcmcia_disk_size[0],vm->pcmcia_disk_size[1]);

   if (vm->platform->cli_show_options != NULL)
      vm->platform->cli_show_options(vm);

   printf("\n"
#if DEBUG_SYM_TREE
          "  -S <sym_file>      : Load a symbol file\n"
#endif
          "  -a <cfg_file>      : Virtual ATM switch configuration file\n"
          "  -f <cfg_file>      : Virtual Frame-Relay switch configuration "
          "file\n"
          "  -E <cfg_file>      : Virtual Ethernet switch configuration file\n"
          "  -b <cfg_file>      : Virtual bridge configuration file\n"
          "  -e                 : Show network device list of the "
          "host machine\n"
          "\n");

   printf("<si_desc> format:\n"
          "   \"device{:baudrate{:databits{:parity{:stopbits{:hwflow}}}}}}\"\n"
          "\n");

   switch(vm->slots_type) {
      case CISCO_CARD_TYPE_PA:
         printf("<pa_desc> format:\n"
                "   \"slot:sub_slot:pa_driver\"\n"
                "\n");

         printf("<pa_nio> format:\n"
                "   \"slot:port:netio_type{:netio_parameters}\"\n"
                "\n");
         break;

      case CISCO_CARD_TYPE_NM:
         printf("<nm_desc> format:\n"
                "   \"slot:sub_slot:nm_driver\"\n"
                "\n");

         printf("<nm_nio> format:\n"
                "   \"slot:port:netio_type{:netio_parameters}\"\n"
                "\n");
         break;

      case CISCO_CARD_TYPE_WIC:
         printf("<wic_desc> format:\n"
                "   \"slot:wic_driver\"\n"
                "\n");

         printf("<wic_nio> format:\n"
                "   \"slot:port:netio_type{:netio_parameters}\"\n"
                "\n");
         break;
   }

   if (vm->platform->show_spec_drivers != NULL)
      vm->platform->show_spec_drivers();

   /* Show possible slot drivers */
   vm_slot_show_drivers(vm);
   
   /* Show the possible NETIO types */
   netio_show_types();
}

/* Find an option in the command line */
static char *cli_find_option(int argc,char *argv[],char *opt)
{
   int i;

   for(i=1;i<argc;i++) {
      if (!strncmp(argv[i],opt,2)) {
         if (argv[i][2] != 0)
            return(&argv[i][2]);
         else {
            if (argv[i+1] != NULL)
               return(argv[i+1]);
            else {
               fprintf(stderr,"Error: option '%s': no argument specified.\n",
                       opt);
               exit(EXIT_FAILURE);
            }
         }
      }
   }

   return NULL;
}

/* Load plugins */
static void cli_load_plugins(int argc,char *argv[])
{
   char *str;
   int i;

   for(i=1;i<argc;i++) {
      if (!strncmp(argv[i],"-L",2)) {
         if (argv[i][2] != 0)
            str = &argv[i][2];
         else {
            if (argv[i+1] != NULL)
               str = argv[i+1];
            else {
               fprintf(stderr,"Plugin error: no argument specified.\n");
               exit(EXIT_FAILURE);
            }
         }

         if (!plugin_load(str))
            fprintf(stderr,"Unable to load plugin '%s'!\n",str);
      }
   }
}

/* Determine the platform (Cisco 3600, 7200). Default is Cisco 7200 */
static vm_platform_t *cli_get_platform_type(int argc,char *argv[])
{
   vm_platform_t *platform;
   char *str;

   if (!(str = cli_find_option(argc,argv,"-P")))
      str = default_platform;

   if (!(platform = vm_platform_find_cli_name(str)))
         fprintf(stderr,"Invalid platform type '%s'\n",str);

   return platform;
}

static struct option cmd_line_lopts[] = {
   { "disk0"      , 1, NULL, OPT_DISK0_SIZE },
   { "disk1"      , 1, NULL, OPT_DISK1_SIZE },
   { "idle-pc"    , 1, NULL, OPT_IDLE_PC },
   { "timer-itv"  , 1, NULL, OPT_TIMER_ITV },
   { "vm-debug"   , 1, NULL, OPT_VM_DEBUG },
   { "iomem-size" , 1, NULL, OPT_IOMEM_SIZE },
   { "sparse-mem" , 0, NULL, OPT_SPARSE_MEM },
   { NULL         , 0, NULL, 0 },
};

/* Create a router instance */
static vm_instance_t *cli_create_instance(char *name,char *platform_name,
                                          int instance_id)
{
   vm_instance_t *vm;

   vm = vm_create_instance(name,instance_id,platform_name);
  
   if (vm == NULL) {
      fprintf(stderr,"%s: unable to create instance %s!\n",platform_name,name);
      return NULL;
   }

   return vm;
}

/* Parse the command line */
static int parse_std_cmd_line(int argc,char *argv[])
{
   char *options_list = 
      "r:o:n:c:m:l:C:i:jt:p:s:k:T:U:A:B:a:f:E:b:S:R:M:eXP:N:G:g:L:";
   vm_platform_t *platform;
   vm_instance_t *vm;
   int instance_id;
   int option;
   char *str;

   /* Get the instance ID */
   instance_id = 0;

   /* Use the old VM file naming type */
   vm_file_naming_type = 1;

   cli_load_plugins(argc,argv);

   if ((str = cli_find_option(argc,argv,"-i"))) {
      instance_id = atoi(str);
      printf("Instance ID set to %d.\n",instance_id);
   }

   if ((str = cli_find_option(argc,argv,"-N")))
      vm_file_naming_type = atoi(str);

   /* Get the platform type */
   if (!(platform = cli_get_platform_type(argc,argv)))
      exit(EXIT_FAILURE);

   /* Create the default instance */
   if (!(vm = cli_create_instance("default",platform->name,instance_id)))
      exit(EXIT_FAILURE);

   opterr = 0;

   while((option = getopt_long(argc,argv,options_list,
                               cmd_line_lopts,NULL)) != -1) 
   {
      switch(option)
      {
         /* Instance ID (already managed) */
         case 'i':
            break;

         /* Platform (already managed) */
         case 'P':
            break;

         /* RAM size */
         case 'r':
            vm->ram_size = strtol(optarg, NULL, 10);
            printf("Virtual RAM size set to %d MB.\n",vm->ram_size);
            break;

         /* ROM size */
         case 'o':
            vm->rom_size = strtol(optarg, NULL, 10);
            printf("Virtual ROM size set to %d MB.\n",vm->rom_size);
            break;

         /* NVRAM size */
         case 'n':
            vm->nvram_size = strtol(optarg, NULL, 10);
            printf("NVRAM size set to %d KB.\n",vm->nvram_size);
            break;

         /* PCMCIA disk0 size */
         case OPT_DISK0_SIZE:
            vm->pcmcia_disk_size[0] = atoi(optarg);
            printf("PCMCIA ATA disk0 size set to %u MB.\n",
                   vm->pcmcia_disk_size[0]);
            break;

         /* PCMCIA disk1 size */
         case OPT_DISK1_SIZE:
            vm->pcmcia_disk_size[1] = atoi(optarg);
            printf("PCMCIA ATA disk1 size set to %u MB.\n",
                   vm->pcmcia_disk_size[1]);
            break;

         /* Config Register */
         case 'c':
            vm->conf_reg_setup = strtol(optarg, NULL, 0);
            printf("Config. Register set to 0x%x.\n",vm->conf_reg_setup);
            break;

         /* IOS configuration file */
         case 'C':
            vm_ios_set_config(vm,optarg);
            break;

         /* Use physical memory to emulate RAM (no-mapped file) */
         case 'X':
            vm->ram_mmap = 0;
            break;

         /* Use a ghost file to simulate RAM */           
         case 'G':
            vm->ghost_ram_filename = strdup(optarg);
            vm->ghost_status = VM_GHOST_RAM_USE;
            break;

         /* Generate a ghost RAM image */
         case 'g':
            vm->ghost_ram_filename = strdup(optarg);
            vm->ghost_status = VM_GHOST_RAM_GENERATE;
            break;

         /* Use sparse memory */
         case OPT_SPARSE_MEM:
            vm->sparse_mem = TRUE;
            break;

         /* Alternate ROM */
         case 'R':
            vm->rom_filename = optarg;
            break;

         /* Idle PC */
         case OPT_IDLE_PC:
            vm->idle_pc = strtoull(optarg,NULL,0);
            printf("Idle PC set to 0x%llx.\n",vm->idle_pc);
            break;

         /* Timer IRQ check interval */
         case OPT_TIMER_ITV:
            vm->timer_irq_check_itv = atoi(optarg);
            break;

         /* Clock divisor */
         case 'k':
            vm->clock_divisor = atoi(optarg);

            if (!vm->clock_divisor) {
               fprintf(stderr,"Invalid Clock Divisor specified!\n");
               exit(EXIT_FAILURE);
            }

            printf("Using a clock divisor of %d.\n",vm->clock_divisor);
            break;

         /* Disable JIT */
         case 'j':
            vm->jit_use = FALSE;
            break;

         /* VM debug level */
         case OPT_VM_DEBUG:
            vm->debug_level = atoi(optarg);
            break;

         /* Log file */
         case 'l':
            if (!(log_file_name = strdup(optarg))) {
               fprintf(stderr,"Unable to set log file name.\n");
               exit(EXIT_FAILURE);
            }
            printf("Log file: writing to %s\n",log_file_name);
            break;

#if DEBUG_SYM_TREE
         /* Symbol file */
         case 'S':
            vm->sym_filename = strdup(optarg);
            break;
#endif

         /* TCP server for Console Port */
         case 'T':
            vm->vtty_con_type = VTTY_TYPE_TCP;
            vm->vtty_con_tcp_port = atoi(optarg);
            break;

         /* Serial interface for Console port */
         case 'U':
            vm->vtty_con_type = VTTY_TYPE_SERIAL;
            if (vtty_parse_serial_option(&vm->vtty_con_serial_option,optarg)) {
               fprintf(stderr,
                       "Invalid Console serial interface descriptor!\n");
               exit(EXIT_FAILURE);
            }
            break;

         /* TCP server for AUX Port */
         case 'A':
            vm->vtty_aux_type = VTTY_TYPE_TCP;
            vm->vtty_aux_tcp_port = atoi(optarg);
            break;

         /* Serial interface for AUX port */
         case 'B':
            vm->vtty_aux_type = VTTY_TYPE_SERIAL;
            if (vtty_parse_serial_option(&vm->vtty_aux_serial_option,optarg)) {
               fprintf(stderr,"Invalid AUX serial interface descriptor!\n");
               exit(EXIT_FAILURE);
            }
            break;

         /* Port settings */
         case 'p':
            vm_slot_cmd_create(vm,optarg);
            break;

         /* NIO settings */
         case 's':
            vm_slot_cmd_add_nio(vm,optarg);
            break;

         /* Virtual ATM switch */
         case 'a':
            if (atmsw_start(optarg) == -1)
               exit(EXIT_FAILURE);
            break;

         /* Virtual ATM bridge */
         case 'M':
            if (atm_bridge_start(optarg) == -1)
               exit(EXIT_FAILURE);
            break;

         /* Virtual Frame-Relay switch */
         case 'f':
            if (frsw_start(optarg) == -1)
               exit(EXIT_FAILURE);
            break;

         /* Virtual Ethernet switch */
         case 'E':
            if (ethsw_start(optarg) == -1)
               exit(EXIT_FAILURE);
            break;

         /* Virtual bridge */
         case 'b':
            if (netio_bridge_start(optarg) == -1)
               exit(EXIT_FAILURE);
            break;

#ifdef GEN_ETH
         /* Ethernet device list */
         case 'e':
            gen_eth_show_dev_list();
            exit(EXIT_SUCCESS);           
#endif            

         /* Load plugin (already handled) */
         case 'L':
            break;

         /* Oops ! */
         case '?':
            show_usage(vm,argc,argv);
            exit(EXIT_FAILURE);

         /* Parse options specific to the platform */
         default:
            if (vm->platform->cli_parse_options != NULL)
               if (vm->platform->cli_parse_options(vm,option) == -1)
                  exit(EXIT_FAILURE);
      }
   }

   /* Last argument, this is the IOS filename */
   if (optind == (argc - 1)) {
      /* setting IOS image file	*/
      vm_ios_set_image(vm,argv[optind]);
      printf("IOS image file: %s\n\n",vm->ios_image);
   } else { 
      /* IOS missing */
      fprintf(stderr,"Please specify an IOS image filename\n");
      show_usage(vm,argc,argv);
      exit(EXIT_FAILURE);
   }

   vm_release(vm);
   return(0);
}

/* 
 * Run in hypervisor mode with a config file if the "-H" option 
 * is present in command line.
 */
static int run_hypervisor(int argc,char *argv[])
{
   char *options_list = "H:l:hN:L:";
   int i,option;
   char *index;
   size_t len;

   for(i=1;i<argc;i++)
      if (!strcmp(argv[i],"-H")) {
         hypervisor_mode = 1;
         break;
      }

   /* standard mode with one instance */
   if (!hypervisor_mode)
      return(FALSE);

   cli_load_plugins(argc,argv);

   opterr = 0;
   while((option = getopt(argc,argv,options_list)) != -1) {
      switch(option)
      {
         /* Hypervisor TCP port */
         case 'H':
            index = strrchr(optarg,':');

            if (!index) {
               hypervisor_tcp_port = atoi(optarg);
            } else {
               len = index - optarg;
               hypervisor_ip_address = malloc(len + 1);

               if (!hypervisor_ip_address) {
                  fprintf(stderr,"Unable to set hypervisor IP address!\n");
                  exit(EXIT_FAILURE);
               }

               memcpy(hypervisor_ip_address,optarg,len);
               hypervisor_ip_address[len] = '\0';
            }
            break;

         /* Log file */
         case 'l':
            if (!(log_file_name = malloc(strlen(optarg)+1))) {
               fprintf(stderr,"Unable to set log file name!\n");
               exit(EXIT_FAILURE);
            }
            strcpy(log_file_name, optarg);
            printf("Log file: writing to %s\n",log_file_name);
            break;

         /* VM file naming type */
         case 'N':
            vm_file_naming_type = atoi(optarg);
            break;

         /* Load plugin (already handled) */
         case 'L':
            break;

         /* Oops ! */
         case '?':
            //show_usage(argc,argv,VM_TYPE_C7200);
            exit(EXIT_FAILURE);
      }
   }

   return(TRUE);
}

/* Delete all objects */
void dynamips_reset(void)
{
   printf("Shutdown in progress...\n");

   /* Delete all virtual router instances */
   vm_delete_all_instances();

   /* Delete ATM and Frame-Relay switches + bridges */
   netio_bridge_delete_all();
   atmsw_delete_all();
   atm_bridge_delete_all();
   frsw_delete_all();
   ethsw_delete_all();

   /* Delete all NIO descriptors */
   netio_delete_all();

   m_log("GENERAL","reset done.\n");

   printf("Shutdown completed.\n");
}

/* Default platforms */
static int (*platform_register[])(void) = {
   c7200_platform_register,
   c3600_platform_register,
   c3725_platform_register,
   c3745_platform_register,
   c2691_platform_register,
   c2600_platform_register,
   c1700_platform_register,
   c6sup1_platform_register,
   c6msfc1_platform_register,
   ppc32_vmtest_platform_register,
   NULL,
};

/* Register default platforms */
static void register_default_platforms(void)
{
   int i;

   for(i=0;platform_register[i];i++)
      platform_register[i]();
}

int main(int argc,char *argv[])
{
   vm_instance_t *vm;

#ifdef PROFILE
   atexit(profiler_savestat);
#endif

   printf("Cisco Router Simulation Platform (version %s/%s)\n",
          sw_version,os_name);
   printf("Copyright (c) 2005-2008 Christophe Fillot.\n");
   printf("Build date: %s %s\n\n",__DATE__,__TIME__);

   gen_uuid_init();

   /* Register platforms */
   register_default_platforms();

   /* Initialize timers */
   timer_init();

   /* Initialize object registry */
   registry_init();
   
   /* Initialize ATM module (for HEC checksums) */
   atm_init();

   /* Initialize CRC functions */
   crc_init();

   /* Initialize NetIO code */
   netio_rxl_init();

   /* Initialize NetIO packet filters */
   netio_filter_load_all();

   /* Initialize VTTY code */
   vtty_init();
   
   /* Parse standard command line */
   if (!run_hypervisor(argc,argv))
      parse_std_cmd_line(argc,argv);

   /* Create general log file */
   create_log_file();

   /* Periodic tasks initialization */
   if (ptask_init(0) == -1)
      exit(EXIT_FAILURE);

   /* Create instruction lookup tables */
   mips64_jit_create_ilt();
   mips64_exec_create_ilt();
   ppc32_jit_create_ilt();
   ppc32_exec_create_ilt();
   
   setup_signals();

   if (!hypervisor_mode) {
      /* Initialize the default instance */
      vm = vm_acquire("default");
      assert(vm != NULL);

      if (vm_init_instance(vm) == -1) {
         fprintf(stderr,"Unable to initialize router instance.\n");
         exit(EXIT_FAILURE);
      }

#if (DEBUG_INSN_PERF_CNT > 0) || (DEBUG_BLOCK_PERF_CNT > 0)
      {
         m_uint32_t counter,prev = 0,delta;
         while(vm->status == VM_STATUS_RUNNING) {
            counter = cpu_get_perf_counter(vm->boot_cpu);
            delta = counter - prev;
            prev = counter;
            printf("delta = %u\n",delta);
            sleep(1);
         }
      }
#else
      /* Start instance monitoring */
      vm_monitor(vm);
#endif

      /* Free resources used by instance */
      vm_release(vm);
   } else {
      hypervisor_tcp_server(hypervisor_ip_address,hypervisor_tcp_port);
   }

   dynamips_reset();
   close_log_file();
   return(0);
}