#!/usr/share/sysman/bin/sysmanshc
#
# ID:      $Id: tsport,v 1.8 2003/09/26 15:11:54 dand Exp $
# Project: AMS 
#
# Description:
#   This utility helps with the initialization and verification of a 
#   terminal server.  Commands are provided to initialize a terminal
#   server, verify that a terminal server port is listening and 
#   connected to something on the back end, look at the users of the
#   terminal server ports, and clear current users of a port.
#
#   This code is written to try a direct connection or a connection
#   through cmfd.  The code doesn't tell the user which it is using.
#   The utility tries a direct connection to the terminal server first.  
#   If this fails, for any reason, then the utility tries to find a 
#   corresponding console in cmf.conf, and if the console is found, 
#   tries to open it.  Error messages are regularized.
#
# Notes:
#   If later versions of the AMS software include expect,
#   then this utility should be rewritten to use expect.
#
#   Another good idea would be use socket -async, and to use 
#   socket -error, fblocked, fileevents and other stuff to 
#   shorten the timeout when attempting a connection.
#
#   If the port is "locked exclusively" within cmfd, then another
#   cmfd user has the port.  tsport -users could be enhanced to list
#   direct and external users.  This will only be useful if tsport
#   isn't wrapped in a user interface.
#
# Log: 
#   $Log: tsport,v $
#   Revision 1.8  2003/09/26 15:11:54  dand
#   Changes so that tsport can make a console connection even if the port
#   is managed by cmfd.  tsport first tries a direct connection, and then
#   a connection through cmfd.
#
#   Revision 1.7  2003/09/22 22:22:46  dand
#   Add -reset command to tsport.
#
#   Revision 1.1  2003/09/19 21:51:03  dand
#   A utility to work with a terminal server to show users of the ports,
#   disconnect them, etc.
# 
#   $EndLog$
#

#------------------------------------------------------------------------------
#                           GLOBAL VARIABLES
#------------------------------------------------------------------------------

# Default terminal server passwords
set apasswd "access"
set spasswd "system"

# debug
set debug 0

# command?
set command ""

# command parameters
set address ""
set port 23
set nport 8
set from "default"

# list of commands requiring access login
set mgmtcmds { "-init" "-clear" "-users" "-reset" }

# list of commands requiring privileged login
set privcmds { "-init" "-clear" "-reset" }

#
# Although there are no variables for exit codes, the following
# conventions are followed for exit codes:
#
#   0 - success
#   1 - parameter error (usage)
#   2 - network error (invalid hostname, connection refused, socket error)
#   3 - invalid terminal server password
#   4 - terminal server returned an error (checkerr)
#   5 - terminal server timeout when a response was expected
#   6 - error parsing internal data
#

#------------------------------------------------------------------------------
# Class:       ExpectSocket
# Description: 
#   Connection knows how to send commands and get replies that are 
#   terminated by matching expected expressions.    In this way, 
#    connection is like a mini expect.
#------------------------------------------------------------------------------

# telnet option constants
set topts(IAC)   255
set topts(DONT)  254
set topts(DO)    253
set topts(WONT)  252
set topts(WILL)  251
set topts(SB)    250
set topts(SE)    240
set topts(TTYPE)  24
set topts(SGA)     3
set topts(ECHO)    1
set topts(SEND)    1

set tcmdwithopt { 254 253 252 251 }

# help for debugging: ain't tcl grand
set topts(254)   DONT
set topts(253)   DO
set topts(252)   WONT
set topts(251)   WILL
set topts(3)     SGA
set topts(1)     ECHO

Class ExpectSocket

ExpectSocket instproc init { address_ port_ } {
    $self instvar address port sock error
    set address $address_
    set port $port_

    if { [catch {set sock [socket $address_ $port_]} error] } {
        set sock -1
        return
    }

    $self setup
}

ExpectSocket instproc setup {} {
    global debug wakeup
    $self instvar sock timeout buffer toptstate
    $self instvar address port

    set timeout 10

    #
    # don't buffer, use asynchronous I/O and events, and 
    # disable eof, lineending translation, and text encoding
    #
    fconfigure $sock -buffering none
    fconfigure $sock -blocking false
    fconfigure $sock -translation binary

    # This global variable will be used for vwait for this
    # object.  The variable will be incremented when there
    # is any I/O or timeout
    set wakeup($self) 0

    if $debug {
        puts stderr "Debug: ExpectSocket $self created for $address:$port"
    }

    # create instance variables
    set buffer ""
    set toptstate EXPECT_IAC

    # This file event will put data into the buffer whenever
    # the socket is readable
    fileevent $sock readable "$self readable"

    # Wait for either a telnet option to come in or for half-a-second
    set id [after 500 "$self timeout"]
    vwait wakeup($self)
    after cancel $id
}

ExpectSocket instproc readable {} {
    global debug wakeup topts IAC
    $self instvar sock buffer toptstate

    set err [fconfigure $sock -error]
    if {[string length $err] != 0 } {
        puts stderr "Received an error $err from the socket"
        exit 2
    }
    set locbuf [read $sock 512]
    set nread [string length $locbuf]

    # remove non-printable and handle telnet options
    set locokay ""
    for {set i 0} {$i < $nread} {incr i} {
        set ch [cindex $locbuf $i]
        set chcode [ctype ord $ch]
        # If we get IAC or are in the middle of a telnet command
        set expectIac [string equal $toptstate EXPECT_IAC]
        if { $expectIac == 0 || $chcode == $topts(IAC) } {
            $self processTelnetOption $chcode
        } else {
            append locokay $ch
        }
    }
    set nread [string length $locokay]

    append buffer $locokay
    if $nread {
        if $debug {
            puts stderr "Debug: $self added $nread chars to the buffer:"
            set cr [string last $locokay "\r"]
            if {$cr != -1} {
                set locokay [string replace $locokay $cr $cr "\\r"]
            }
            puts stderr "\"$locokay\""
        }
        incr wakeup($self)
    }
}

ExpectSocket instproc processTelnetOption { chcode } {
    global debug topts tcmdwithopt
    $self instvar toptstate tcmd
    switch -- $toptstate {
        EXPECT_IAC {
            set toptstate EXPECT_CMD
        }
        EXPECT_CMD {
            set tcmd $chcode
            if {[lsearch $tcmdwithopt $chcode] != -1} {
                set toptstate EXPECT_OPT
            } else {
                if $debug {
                    set tscmd $tcmd
                    if [info exists topts($tcmd)] {
                        set tscmd $topts($tcmd)
                    }    
                    puts stderr "Debug: $self rcvd telnet option: $tscmd"
                }
                # TODO: process the command (we don't receive any of these yet)
                set toptstate EXPECT_IAC
            }
        }
        EXPECT_OPT {
            set topt $chcode
            if $debug {
                set tscmd $tcmd
                if [info exists topts($tcmd)] {
                    set tscmd $topts($tcmd)
                }
                set tsopt $topt
                if [info exists topts($topt)] {
                    set tsopt $topts($topt)
                }
                puts stderr "Debug: $self rcvd telnet option: $tscmd $tsopt"
            }

            # We have to have the actual numbers because we can't substitute
            # due to how tcl handles braces.
            switch -- $topt {
                1 { # ECHO-case
                    if { $tcmd == $topts(WILL) } {
                        $self sendTelnetOption $topts(DONT) $topt
                    }
                }
                3 { # SGA-case
                    if { $tcmd == $topts(DO) } {
                        $self sendTelnetOption $topts(WILL) $topt
                    } elseif { $tcmd == $topts(WILL) } {
                        $self sendTelnetOption $topts(DO) $topt
                    }
                }
                default {
                    if { $tcmd == $topts(DO) } {
                        $self sendTelnetOption $topts(WONT) $topt
                    } elseif { $tcmd == $topts(WILL) } {
                        $self sendTelnetOption $topts(DONT) $topt
                    }
                }
            }
            set toptstate EXPECT_IAC
        }
        default {
            error "Invalid telnet command processing state"
        }
    }
}

ExpectSocket instproc sendTelnetOption { tcmd topt } {
    global debug topts
    $self instvar sock address port
    puts -nonewline $sock [binary format ccc $topts(IAC) $tcmd $topt]
    if $debug {
        if [info exists topts($tcmd)] {
            set tcmd $topts($tcmd)
        }
        if [info exists topts($topt)] {
            set topt $topts($topt)
        }
        puts stderr "Debug: $self sent telnet option: $tcmd $topt"
    }
}

ExpectSocket instproc timeout {} {
    global debug wakeup
    $self instvar istimeout
    set istimeout 1
    if $debug {
        puts stderr "Debug: $self timeout"
    }
    incr wakeup($self)
}

ExpectSocket instproc send { value } {
    global debug
    $self instvar sock address port
    puts -nonewline $sock $value
    if $debug {
        puts stderr "Debug: $self sent [string length $value] chars:"
        set cr [string last $value "\r"]
        if {$cr != -1} {
            set value [string replace $value $cr $cr "\\r"]
        }
        puts stderr "\"$value\""
    }
}

#
# The expect method attempts to mimic the function of the expect
# command from expect.  It is not exactly right because the only
# form of expect command supported is:
#   expect {
#     pattern action
#     pattern action
#     pattern action
#   }
# This is equivalent to the following expect code:
#   expect {
#     -re pattern action
#     -re pattern action
#     -re pattern action
#   }
# The simple form, expect "string", is not supported.
#
# Timeout is currently handled as for expect, but that means
# there's no super-clean way to really expect the string timeout.
#
# We don't handle arbitrarily complex subexpressions, expect only
#   expect_out(buffer)
#   expect_out(0,string)
#   expect_out(1,string)
#   expect_out(2,string)
#   expect_out(3,string)
# 

ExpectSocket instproc expect { value } {
    global debug wakeup
    $self instvar sock address port
    $self instvar buffer timeout istimeout
    upvar expect_out expect_out

    set timeoutIndex [lsearch -exact $value timeout]
    if { $timeoutIndex != -1 } {
        set timeoutAction [lindex $value [expr $timeoutIndex + 1]]
        set value [lreplace $value $timeoutIndex [expr $timeoutIndex + 1]]
    }

    set nargs [llength $value]
    if $debug {
        puts stderr "Debug: expecting $nargs from $address $port: "
        for {set i 0} {$i < $nargs} {incr i 2} {
            set pattern [lindex $value $i]
            puts stderr "Debug: pattern 1: $pattern"
        }
    }
    
    # Keep getting data from the socket until you match the value or
    # there's a timeout.  This is done using asynchronous sockets,
    # the readable fileevent, and the after command

    set istimeout 0
    set hasmatch 0
    set timerid [after [expr $timeout * 1000] "$self timeout"]
    while { $istimeout == 0 && $hasmatch == 0 } {
        # For each pattern action 
        for {set i 0} {$i < $nargs} {incr i 2} {
            set pattern [lindex $value $i]
            if {[regexp -indices $pattern $buffer match s(1) s(2) s(3)] == 1 } {
                after cancel $timerid
                set hasmatch 1
                set start [lindex $match 1]
                set end [lindex $match 1]

                # expect_out is upvar so it will be available
                set expect_out(buffer) [string range $buffer 0 $end]
                set expect_out(0,string) [string range $buffer $start $end]
                for {set j 1} {$j <= 3} {incr j} {
                    set ss [lindex $s($j) 0]
                    if { $ss != -1 } {
                        set se [lindex $s($j) 1]
                        set expect_out($j,string) [string range $buffer $ss $se]
                    }
                }

                # remove up until then in the buffer
                set buffer [string range $buffer [expr $end + 1] end]

                if $debug {
                    puts stderr "Debug: matched [string length $expect_out(buffer)]"
                    puts stderr "Debug: leaving [string length $buffer]"
                }

                # Evaluate the action associated with the pattern in the 
                # parent function
                set j [expr $i + 1]
                if {$j < $nargs} {
                    uplevel [lindex $value $j]
                }
            }
        }
        if { $istimeout == 0 && $hasmatch == 0 } {
            vwait wakeup($self)
        }
    }
    after cancel $timerid
    if $istimeout {
        if $debug {
            puts stderr "Debug: command timed out"
        }
        if { $timeoutIndex != -1 } {
            uplevel $timeoutAction
        }
    }
    return $istimeout
}

#------------------------------------------------------------------------------
# Class:       CmfExpectSocket
# Description: 
#   This class knows how to build 
#   terminated by matching expected expressions.    In this way, 
#    connection is like a mini expect.
#------------------------------------------------------------------------------

Class CmfExpectSocket -superclass ExpectSocket

CmfExpectSocket instproc init { address_ port_ } {
    global debug
    $self instvar error sock amsuser
    $self next $address_ $port_
    if {$sock == -1} {
        set console [$self getconsole]
        if $debug {
            puts "Debug: Use console \"$console\" for $address_:$port_"
        }

        if {[string equal $console ""] == 1} {
            puts stderr [$self errormsg]
            exit 2
        }

        set cookie [$self readcookie]
        if $debug {
            puts "Debug: Trying cmfd with cookie $cookie"
        }

        $self openconsole $console $cookie
        if { $sock == -1 } {
            puts stderr [$self errormsg $console]
            exit 2
        }
    }
}

CmfExpectSocket instproc getconsole {} {
    global debug error
    $self instvar address port amsuser

    # Match all the addresses of the host
    if {[catch {set addrlist [host_info addresses $address]} luperr]} {
        # Note, the error will hold the host is unreachable error due
        # to a failure to open a socket connection.
        return ""
    }

    # the match
    set consolename ""
    set looking 1

    if {[catch {set cmfconf [open "/usr/opt/ams/config/cmf.conf" "r"]} err]} {
       set amsuser 0
       return ""
    }
    set amsuser 1

    while {$looking && [eof $cmfconf] == 0} {
        gets $cmfconf line
        set cmfName [lindex $line 0]
        set cmfAddr [lindex $line 1]
        set cmfPort [lindex $line 2]
        if $debug {
            puts stderr "Debug: from cmf.conf: $cmfName $cmfAddr $cmfPort"
        }
        if {[lsearch $addrlist $cmfAddr] != -1 && $cmfPort == $port} {
            set consolename $cmfName
            set looking 0
        }
    }
    close $cmfconf

    # return the consolename
    return $consolename
}

CmfExpectSocket instproc readcookie {} {
    $self instvar amsuser
    if {[catch {set cookiejar [open "/usr/opt/ams/config/.cookie" "r"]} err]} {
        set amsuser 0
        return ""
    }
    set amsuser 1
    set cookie [string trim [gets $cookiejar]]
    close $cookiejar
    return $cookie
}

CmfExpectSocket instproc openconsole {console cookie} {
    $self instvar sock error timeout
    if { [catch {set sock [socket 127.0.0.1 6500]} error] } {
        set sock -1
        return
    }
    $self setup

    # send the token line and wait for error or timeout (success)
    set timeout 2
    $self send "token $cookie $console user=[id user] mode=ex\r"
    $self expect {
        {[0-9]+ - [^\.]*\.} {
            set error $expect_out(buffer)
            close $sock
            set sock -1
        }
        timeout {}
    }

    # reset the timeout
    set timeout 10
}

CmfExpectSocket instproc errormsg { {console ""} } {
    $self instvar error address port
    switch -regexp -- $error {
        {001} {
            # Authentication failure - this is a program error
            set error "Error: program error communicating with cmfd"
        }
        {002} {
            # Invalid argument - this is a program error
            set error "Error: program error communicating with cmfd"
        }
        {003} {
            # Timed out - what does this mean again?
            set error "Error: cmfd timed out"
        }
        {004} {
            # Console not found - this is a program error
            set error "Error: program error communicating with cmfd"
        }
        {005} {
            # Console is not accesible (why, what is its actual state?)
            set error "Error: port $port on host $address is inaccessible both directly and through cmfd"
        }
        {006} {
            # Console is locked exclusively (note: 
            set error "Error: another cmfd client is using console $console"
        }
        {007} {
            # Cannot grant exclusive access.  Console in use.
            set error "Error: another cmfd client is using console $console"
        }
        {008} {
            # Read error - this is a network error
            set error "Error: network error communicating with cmfd"
        }
        {009} {
            # Insufficient data - huh?
            set error "Error: program error communicating with cmfd"
        }
        {011} {
            # Console has been deleted (race condition?)
            set error "Error: program error communicating with cmfd"
        }
        {012} {
            # Console is disabled (so, we tried it directly - someone
            # else is probably using it at the terminal server.)
            set error "Error: port $port on host $address: connection refused"
        }
        {013} {
            # Out of memory
            set error "Error: cmfd is out of memory, please restart cmfd"
        }
        {014} {
            # Not an authorized user.  We should never reach this point because
            # there will be an error trying to open the cookie file for reading
            set error "Error: program error communicating with cmfd"
        }
        {015} {
            # Port number not mapped to a console.  Not a mapped port.
            set error "Error: program error communicating with cmfd"
        }
        {016} {
            # Too many pending client connections
            set error "Error: the port is not directly accessible, but cmfd is busy now."
        }
        {connection refused} {
            set error "Error: port $port on host $address: connection refused"
        }
        {host is unreachable} {
            set error "Error: address $address: invalid host or address"
        }
        {connection timed out} {
            set error "Error: port $port on host $address: connection timed out"
        }
        default {
            if [string equal $console ""] {
                set error "Error: direct: $error"
            } else {
                set error "Error: cmfd: $error"
            }
        }
    }
}

#------------------------------------------------------------------------------
#                             GLOBAL PROCEDURES
#------------------------------------------------------------------------------

proc usage {} {
    puts stderr "Usage: tsport \[ -access *passwd* \] \[ -priv *passwd* \]"
    puts stderr "        \{ -init *address* \[ *nport* \] |"
    puts stderr "           -verify *address* *port* |"
    puts stderr "           -users *address* |"
    puts stderr "           -clear *address* *port* |"
    puts stderr "           -reset *address* \[-from \{network|flashram\} \] \}"
    exit 1
}

# This should really be in a subclass that deals only with DEC terminal servers
proc sendcmd { sock cmd { echo 1 } } {
    upvar reply reply
    if $echo {
        puts "  $cmd"
    }
    $sock send "$cmd\r"
    $sock expect { 
        "Local>" {}
        timeout { 
            puts "Timeout waiting for prompt 'Local>' after command '$cmd'"
            exit 5
        } 
    }
    set reply $expect_out(buffer)
}

proc checkerr { } {
    upvar reply reply
    if [regexp -indices {Local -[0-9]+-} $reply err] {
        set start [lindex $err 0]
        set end [string first "\r" $reply $start]
        set err [string range $reply $start $end]
        puts "The terminal server reported an error:"
        puts "  $err"
        exit 4
    }
}


#------------------------------------------------------------------------------
#                             THE MAIN PROGRAM
#------------------------------------------------------------------------------

# Parse arguments
set argc [llength $argv]
for {set i 0} {$i < $argc} {incr i} {
    set arg [lindex $argv $i]
    switch -glob -- $arg {
        "-access" {
            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must include the access password."
                usage
            }
            set apasswd [lindex $argv $i]
        }
        "-priv" {
            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must include the privilege password."
                usage
            }
            set spasswd [lindex $argv $i]
        }
        "-from" {
            incr i
            if { $i >= $argc } {
                puts stderr "Error: The -from option requires a parameter."
                usage
            }
            set from [lindex $argv $i]
            if {[lsearch "flashram network" $from] == -1} {
                puts stderr "Error: The parameter must be 'network' or 'flashram'."
                usage
            }
        }
        "-debug" {
            set debug 1
        }
        "-init" {
            set command $arg
            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must include the hostname or IP address"
                usage
            }
            set address [lindex $argv $i]
            incr i
            if { $i < $argc } {
                set temp [lindex $argv $i]
                if {[string first "-" $temp] == 0} {
                    # it's an option
                    incr i -1
                } else {
                    # it's the number of ports
                    set nport $temp
                }
            }
        }
        "-users" {
            set command $arg
            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must include the hostname or IP address"
                usage
            }
            set address [lindex $argv $i]
        }
        "-verify" {
            set command $arg

            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must include the hostname or IP address"
                usage
            }
            set address [lindex $argv $i]

            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must specify the port number"
                usage
            }
            set port [lindex $argv $i]
            if { $port < 16 } {
                set port [expr $port + 2000]
            }
        }
        "-clear" {
            set command $arg

            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must include the hostname or IP address"
                usage
            }
            set address [lindex $argv $i]

            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must specify the port number"
                usage
            }
            set port [lindex $argv $i]
        }
        "-reset" {
            set command $arg

            incr i
            if { $i >= $argc } {
                puts stderr "Error: You must include the hostname or IP address"
                usage
            }
            set address [lindex $argv $i]
        }
        default {
            usage
        }
    }
}
if {[string length $command] == 0} {
    usage
}
if {[string equal $command "-reset"] == 0 && 
    [string equal $from "default"] == 0} {
    puts stderr "Error: -from option may only be specified with -reset"
    usage
}

# Debug parsing of arguments
if $debug {
    switch -- $command {
        "-init" {
            puts "Debug: initialize terminal server at $address to $nport ports"
        }
        "-users" {
            puts "Debug: get the users of terminal server at $address"
        }
        "-clear" {
            puts "Debug: clear port $port on the terminal server at $address"
        }
        "-verify" {
            puts "Debug: verify port $port on the terminal server at $address"
        }
        "-reset" {
            puts "Debug: reset terminal server $address from $from"
        }
    } 
}

# Create object socket

if {[lsearch $mgmtcmds $command] != -1} {
    CmfExpectSocket sock $address 23
    puts "Connected to port 23 on terminal server $address"

    sock send "\r" 
    sock expect {
        "\#" {}
        timeout {
            puts "Timeout waiting for access password prompt"
            puts "Is this a DEC terminal server?"
            exit 5
        }
    }

    sock send "$apasswd\r"
    sock expect {
        "Enter username>" {}
        "\#" {
            puts "Invalid terminal server access password"
            puts "Use -access to supply the correct password"
            exit 3
        }
        timeout {
            puts "Timeout waiting for access password prompt"
            puts "Is this a DEC terminal server?"
            exit 5
        }
    }
    sendcmd sock "AMS" 0

    if {[lsearch $privcmds $command] != -1} {
        sock send "SET PRIV\r" 
        sock expect {
            "Password>" {}
            timeout {
                puts "Timeout waiting for privileged password prompt"
                puts "Is this a DEC terminal server?"
                exit 5
            }
        }

        sock send "$spasswd\r"
        sock expect {
            "Local>" {}
            "Password>" {
                puts "Invalid terminal server privileged password"
                puts "Use -priv to supply the correct password"
                exit 3
            }
            timeout {
                puts "Timeout waiting for access password prompt"
                puts "Is this a DEC terminal server?"
                exit 5
            }
        }
    }
} else {
    CmfExpectSocket sock $address $port
    puts "Connected to port $port on terminal server $address"
}

switch -- $command {
    "-init" {
        puts "Sending commands to the terminal server:"
        for {set i 1} {$i <= $nport} {incr i} {
            set port [expr 2000 + $i]
            sendcmd sock "CHANGE TELNET LISTENER $port PORT $i ENABLED"
            sendcmd sock "CHANGE TELNET LISTENER $port CONNECTIONS ENABLED"
        }
        sendcmd sock "DEFINE PORT ALL ACCESS REMOTE"
        sendcmd sock "CHANGE PORT ALL SPEED 9600"
        sendcmd sock "CHANGE PORT ALL SESSION LIMIT 1"
        sendcmd sock "CHANGE PORT ALL AUTOBAUD DISABLE"
        sendcmd sock "CHANGE PORT ALL AUTOPROMPT DISABLE"
        sendcmd sock "CHANGE PORT ALL BROADCAST DISABLE"
        sendcmd sock "CHANGE PORT ALL FAILOVER DISABLE"
        sendcmd sock "LOGOUT PORT ALL"
    }
    "-users" {
        puts "Sending commands to the terminal server:"
        sendcmd sock "SHOW USERS"
        checkerr
        if {[regexp -indices "Port" $reply match] == 0} {
            puts "error: cannot find beginning of user list"
            exit 6
        }
        set start [lindex $match 0]

        if {[regexp -indices "Local>" $reply match] == 0} {
            puts "error: cannot find end of user list"
            exit 6
        }
        set end [expr [lindex $match 0] - 1]
        set reply [string trim [string range $reply $start $end]]
        puts "\n$reply"
    }
    "-clear" {
        puts "Sending commands to the terminal server:"
        # Allow either port 5 or 2005
        if { $port > 2000 } {
            set port [expr $port - 2000]
        }
        sendcmd sock "LOGOUT PORT $port"
        checkerr
    }
    "-reset" {
        puts "Sending commands to the terminal server:"
        switch -- $from {
            default {
                sendcmd sock "INITIALIZE"
            }
            flashram {
                sendcmd sock "INITIALIZE FROM FLASHRAM"
            }
            network {
                sendcmd sock "INITIALIZE FROM ETHERNET"
            }
        }
    }
    "-verify" {
        set iswildfire 0
        set prompt ""

        # Try to get a prompt without sending any escape sequence
        sock set timeout 2
        sock send "\r"
        sock expect {
            {S[LC][VM]_E([0-9a-fA-F])>} { 
                set iswildfire 1 
                scan $expect_out(1,string) %x drawer
            }
            timeout {
                sock expect ".*"
                set prompt [string trim $expect_out(buffer)]
                set lastnl [string last "\n" $prompt]
                if { $lastnl != -1 } {
                    set prompt [string range $prompt [expr $lastnl + 1] end]
                }
            }
        }

        # Try again, escaping to the SCM 
        if { $iswildfire == 0 } {
            sock send "\033\033scm\r"
            sock expect {
                {S[LC][VM]_E([0-9a-fA-F])>} { 
                    set iswildfire 1 
                    scan $expect_out(1,string) %x drawer
                    sock send "quit\r"
                }
            }
        }

        if { $iswildfire == 1 } {
            puts "Port $port is connected with PCI Drawer $drawer"
        } elseif { [string equal $prompt ""] != 1 } {
            puts "Port $port has the following prompt: $prompt"
        } else {
            puts "Port $port is not responding"
        }
    }
}
# Done
exit 0

