#!/bin/sh
# the next line restarts using tclsh \
   exec ibmssh "$0" "$@"

#--
# Copyright (c) 2004 Mellanox Technologies LTD. All rights reserved.
#
# This software is available to you under a choice of one of two
# licenses.  You may choose to be licensed under the terms of the GNU
# General Public License (GPL) Version 2, available from the file
# COPYING in the main directory of this source tree, or the
# OpenIB.org BSD license below:
#
#     Redistribution and use in source and binary forms, with or
#     without modification, are permitted provided that the following
#     conditions are met:
#
#      - Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      - Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials
#        provided with the distribution.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
#############################################################################
# Id ..........$Id$
# Revision.....$Revision: 1.6 $
# Date.........$Date: 2005/06/22 06:13:51 $
# Author.......$Author: eitan $
#EOH#########################################################################

set ToolUsage {[-h] [-V mod-verb-pair-list] [-l log-file] [-p server-port] [-w num-threads] [-f flow-tcl-file] [-s seed] -t topology}

set ToolUsage "Usage: $argv0 $ToolUsage"

set version "1.0"

proc Usage {} {
   global ToolUsage

   puts "$ToolUsage"
}

##############################################################################

proc Help {} {
   global ToolUsage
   global version

   puts "
        Infiniband Management Simulator
       ---------------------------------

 $ToolUsage

 Simulates the Fabric as defined by the given topology file and
 start a server to handle MAD requests from clients like OpenSM.

 Command Line Arguments (required):
   -t topology = a topology file describing the fabric.

 Command Line Options:
   -s seed = the seed to be used for random number generation.
   -f flow-tcl-file = a tcl file to be sourced after the fabric is setup.
   -l log-file = set the log file of the simulator (default is /tmp/ibsim.log)
   -p server-port = set the port number the server attaches to (default 46517)
   -w num-threads = the number of threads processing the mads
   -V <module-verbosity-pair-list> = Provides a fine grain control over
      the log file verbosity. Given list of pairs of simulator software
      module names and their verbosity levels - every module is separatly
      controlled. The first value (missing module name) is controlling the
      default verbosity for all un-explicitly set modules.
      Current Verbosity Levels:            Software Modules:
      MsgShowFatal  = 0x01;                client, server
      MsgShowError  = 0x02;                dispatcher, node, sma
      MsgShowWarning= 0x04;                simnode, lftMad, mftMad,
      MsgShowInfo   = 0x08;                sl2VlMad, vlArbMad, pKeyMad,
      MsgShowVerbose= 0x10;                portInfoMad
      MsgShowContext= 0x20;
      MsgShowSource = 0x40;
      MsgShowTime   = 0x80;
      MsgShowModule = 0x100;
      MsgShowMads   = 0x200;
      MsgShowFrames = 0x400;
      MsgShowAll    = 0xffff;
      MsgDefault    = 0x62f;

     Examples -V flag values might be:
     -V 0xffff = Full verbosity on all modules
     -V '0x3 server 0x6f2 sma 0xffff' = Only fatal and error for all modules
        full verbosity for the SMA module and default verbosity for the Server
        module.

   -h - help.

$version

  "
}

##############################################################################
#
# Get a TS port guid string of the format 0000:ffff:1111:2222
#
proc getPortGuidStr {guid} {
   set guidRex \
      "0x(\[0-9a-fA-F\]{4})(\[0-9a-fA-F\]{4})(\[0-9a-fA-F\]{4})(\[0-9a-fA-F\]{4})"
   if {![regexp $guidRex $guid d1 n1 n2 n3 n4]} {
      error "Bad guid format:$guid"
   }
   return "$n1:$n2:$n3:$n4"
}

##############################################################################
#
# Update Node Information file for the given node
#
proc updateProcFSForNode {fabric simDir nodeName smNodeName smPortNum} {
   # obtain the node
   set node [IBFabric_getNode $fabric $nodeName]
   if {$node == ""} {
      puts "-E- Fail to find node $nodeName"
      return 1
   }

   if {[IBNode_type_get $node] == 1} {
      puts "-E- Can not create proc file for switch node:$nodeName"
      return 1
   }

   # obtain the SM port
   set smNode [IBFabric_getNode $fabric $smNodeName]
   if {$smNode == ""} {
      puts "-E- Fail to find SM node $smNodeName"
      return 1
   }

   set smPort [IBNode_getPort $smNode $smPortNum]
   if {$smPort == ""} {
      puts "-E- Fail to find SM node $smNodeName port $smPortNum"
      return 1
   }

   if {![regexp {^([^/]+)/U([0-9]+)$} $nodeName d1 sysName caNum]} {
      puts "-E- Node name too complex $nodeName"
      return 1
   }

   set caName "ca$caNum"
   set ng [IBNode_guid_get $node]
   set nodeGuidStr [getPortGuidStr $ng]
   set smLid [IBPort_base_lid_get $smPort]

   # Create the node dir
   file mkdir [file join $simDir $sysName $caName]

   # Node Info
   set of [open [file join $simDir $sysName $caName info] w]
   puts $of "name:          InfiniHost0
provider:      tavor
node GUID:     $nodeGuidStr
ports:         2
vendor ID:     0x2c9
device ID:     0x5a44
HW revision:   0xa1
FW revision:   0x300020002
"
   close $of

   # do the ports:
   foreach pn {1 2} {
      set port [IBNode_getPort $node $pn]
      if {$pn != ""} {
         set pg [IBPort_guid_get $port]

         # the port guid we need should look like 0000:0000:0000:0000
         set portGuidStr [getPortGuidStr $pg]
         set lid [IBPort_base_lid_get $port]

         # Port1
         file mkdir [file join $simDir $sysName $caName port$pn]

         # port info
         set of [open [file join $simDir $sysName $caName port$pn info] w]
         puts $of "state:         ACTIVE
LID:           [format 0x%04x $lid]
LMC:           0x0000
SM LID:        [format 0x%04x $smLid]
SM SL:         0x0000
Capabilities:  IsTrapSupported
               IsAutomaticMigrationSupported
               IsSLMappingSupported
               IsLEDInfoSupported
               IsSystemImageGUIDSupported
               IsVendorClassSupported
               IsCapabilityMaskNoticeSupported
"

         close $of

         set of [open [file join $simDir $sysName $caName port$pn pkey_table] w]
         puts $of {[  0] ffff}
         close $of

         set of [open [file join $simDir $sysName $caName port$pn gid_table] w]
         puts $of "\[  0\] fe80:0000:0000:0000:$portGuidStr"
         close $of
      }
   }
   return 0
}

##############################################################################
#
# Start Control Server = allowing TCL evaluation in the simulator shell.
#

# Procedure called whenever a new connection is made by a client.
proc on_connect {newsock clientAddress clientPort} {
   global endOfVwait

   puts "-I- Connecting: $newsock $clientAddress $clientPort"
   fconfigure $newsock -blocking 0 -buffering line
   fileevent $newsock readable [list handleInput $newsock]
}

# Procedure called whenever input arrives on a connection.
proc handleInput {f} {
   global endOfVwait
   global errorInfo
   if {[eof $f]} {
      fileevent $f readable {}
      close $f
      return
   }

   set i [read $f]
   puts -nonewline $i
   if {[catch {set res [uplevel $i]} e]} {
      puts "-E- $e"
      set res "error: $errorInfo"
   }
   puts $f $res
}

proc startControlServer {} {
   global env
   set fn [file join $env(IBMGTSIM_DIR) ibmgtsim.ctrl.server]

   if {[catch {set f [open $fn "w"]} e]} {
      puts "-E- $e"
      return 1
   }

   # invent a port
   set num [expr int(rand()*(65535-1024)+1024)]
   set counter 0
   while {[catch {socket -server on_connect $num}]} {
      set num [expr int(rand()*(65535-1024)+1024)]
      incr counter
      if {$counter > 10 } {
         puts "can't create a server"
         close $f
         return 1
      }
   }
   set hostName [info hostname]
   puts "-I- Started server: $hostName port:$num"
   puts $f "$hostName $num"
   close $f
}

##############################################################################
#
# Get Opt
#
set optind 0

proc getopt { argslist optstring optret argret } {
   global optind optindc
   upvar $optret retvar
   upvar $argret optarg

   # default settings for a normal return
   set optarg ""
   set retvar ""
   set retval 0

   # check if we are in a single char mode or support long
   # option string
   if {[string match "* *" $optstring]} {
      set longOptMode 1
   } else {
      set longOptMode 0
   }

   # check if we're past the end of the args list
   if { $optind < [ llength $argslist ] } then {

      # if we got -- or an option that doesn't begin with -, return (skipping
      # the --).  otherwise process the option arg.
      switch -glob -- [ set arg [ lindex $argslist $optind ]] {
         "--" {
            incr optind
         }

         "-*" {
            # opt needs to return the name of the option
            regexp -- {-([^:]+):?} $arg d1 opt
            incr optind

            if {$longOptMode} {
               # options are defined as words optionaly containing ":"
               if { [ lsearch -regexp $optstring "^$opt:?\$" ] >= 0 } then {
                  set retvar $opt
                  set retval 1
                  if { [ lsearch -regexp $optstring "^$opt:\$" ] >= 0 } then {
                     if { $optind < [ llength $argslist ] } then {
                        set optarg [lindex $argslist $optind]
                        incr optind
                     } else {
                        set optarg "Option requires an argument -- $opt"
                        set retvar $optarg
                        set retval -1
                     }
                  }
               } else {
                  set optarg "Illegal option -- $opt"
                  set retvar $optarg
                  set retval -1
               }
            } else {
               # traditional single char options
               if { [ string match "*$opt*" $optstring ] } then {
                  set retvar $opt
                  set retval 1
                  if { [ string match "*$opt:*" $optstring ] } then {
                     if { $optind < [ llength $argslist ] } then {
                        set optarg [lindex $argslist $optind]
                        incr optind
                     } else {
                        set optarg "Option requires an argument -- $opt"
                        set retvar $optarg
                        set retval -1
                     }
                  }
               } else {
                  set optarg "Illegal option -- $opt"
                  set retvar $optarg
                  set retval -1
               }
            }
         }
      }
   }
   return $retval
}

##############################################################################
#
# MAIN FLOW:
#

set moduleVerbosityList {top 0xA7}
set logFileName /tmp/ibsim.log
set numWorkerThreads 1
set serverPortNum 46517
set simulationFlowFile ""
set topologyFile ""
set randomSeed [expr int(rand() * 100000)]

while { [ set err [ getopt $argv "hvV:l:p:w:f:t:s:" opt arg ]] } {
   if { $err < 0 } then {
      Usage
      exit 2
   } else {
      switch -exact $opt {
         h {Help; exit 0}
         v {puts "";puts $version; puts ""; exit 0}
         V {
            set moduleVerbosityList "top $arg"
         }
         p {set serverPortNum $arg}
         l {set logFileName $arg}
         w {set numWorkerThreads $arg}
         f {set simulationFlowFile $arg}
         t {set topologyFile $arg}
         s {set randomSeed $arg}
         default {
            puts "-E- Unsupported option:$opt"
            puts $ToolUsage
            exit 1
         }
      }
   }
}

# make sure we got a topology:
if {$topologyFile == ""} {
   puts "-E- Missing mandatory flag -t. Topology file was not defined."
   Usage
   exit
}

# handle extra args
set left_args [ lrange $argv $optind end ]
if {[llength $left_args]} {
   puts "-E- illegal parameter(s) used : $left_args"
   Usage
   exit 1
}

# We can start ...
MsgMgr setLogFile $logFileName

puts "-I- Using random seed:$randomSeed"
rmSeed $randomSeed

# Handle Module Verbosity:
foreach {mod verb} $moduleVerbosityList {
   # check if second item is a number
   if {[catch {format %x $verb}]} {
      puts "-E- Provided verbosity:$verb for module: $mod is not a number."
   } else {
      if {$mod == "top"} {
         MsgMgr setVerbLevel $verb
      } else {
         MsgMgr setVerbLevel $verb $mod
      }
   }
}

# Init the simulator
if {[catch {IBMgtSimulator init $topologyFile $serverPortNum $numWorkerThreads} e]} {
   puts "-E- Fail to initialize the simulator:$e"
   exit
}

set fabric [IBMgtSimulator getFabric]
puts "-I- Init fabric: $fabric"
# start the control server:
startControlServer

puts "-I- Ready to serve"

# Source any code that might be provided
if {$simulationFlowFile != ""} {
   if {[catch {source $simulationFlowFile} e]} {
      puts "-E- Error loading $simulationFlowFile"
      puts "    $e"
      exit
   }
}

set done 0
catch {vwait done}
while {!$done} {
   after 30000
}

