#!/usr/bin/expect --
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html

log_user 0 
set stm 1

#
# PROCEDURE
#
# Declare procedure to write status/error messages
# We do it this way so that if /var is full (or some other error occurs)
# we can trap it in a common section of code.
#
proc nc_msg { msg } {
    global verbose

    if { $verbose == 1 } {
        send_user $msg
    }
}

#
# PROCEDURE
#
# Define the procedure to connect to the IVM
#
proc connect {} {

    global hcp
    global ssh_spawn_id
    global prompt
    global userid
    global passwd
    global PROGRAM

    if [ catch { spawn ssh $userid@$hcp} ssh_pid ] {
        send_user "$PROGRAM: Unable to spawn ssh connection to $hcp\n"
        exit 1
    }
    set ssh_spawn_id $spawn_id
    set timeout 10
    set pwd_sent 0
    #
    # Look for password prompt, and reply
    #
    expect {
        -i $ssh_spawn_id \
        -re $prompt {

        }
        -re "ast login.*" {
            return
        }
        -re "assword: $" {
             if { $pwd_sent != 1 } {
                 set pwd_sent 1
                 send -i $ssh_spawn_id "$passwd\r"
                 exp_continue
             }
             send_user "$PROGRAM: Incorrect login\n"
             exit 1
        }
        timeout {
            send_user "$PROGRAM: Timeout waiting for password prompt\n"
            exit 1
        }
        eof {
            send_user "$PROGRAM: ssh connection terminated unexpectedly\n"
            exit 1
        }
    }
}


#
# PROCEDURE
#
# Procedure to run the rpower command
# test its return code and capture its output 
#
proc run_lssyscfg {} {
    global DSPMSG
    global PROGRAM
    global ssh_spawn_id
    global env
    global manage
    global msg
    global node
    global profile
    global rc
    global prompt
    global tmp
    global BINPATH

    set timeout 20
         
    set runcmd [list exec $BINPATH/rpower $node state]
    set rc [catch $runcmd msg] 
    nc_msg "$PROGRAM Status: run_lssyscg : partition status : $msg\n"
    regexp "$node:\[ ]*(.*)" $msg tmp1 tmp
    if { $tmp != "" } {
        set msg $tmp
    }
    nc_msg "####msg:$msg#########\n"


    # Slow down the requests speed to hcp, so that hcp will not busy on
    # query.  Instead, hcp should put more time on other activities.
    # Another reason to set this sleep is giving some time to the lpars
    # to be more stable.
    sleep 4 

    if { $rc } {
        send_user "$PROGRAM: Unable to determine machine state\n"
        nc_msg "$PROGRAM Status: error from rpower command\n"


        send_user "Error : $msg\n"
        exit 1
    }
}

#
# PROCEDURE
#
# Declare procedure to write usage message and exit
#
proc usage {} {
    global PROGRAM

    send_user "Usage: Install partition \
            \n\t$PROGRAM \[-v\] \[-x\] \[-f\] \[-w set_boot_order\] \[-A -D | \[-D\] | \[-D\] -m macaddress\] -t ent -s speed -d duplex \
            \n\t\t-S server -G gateway -C client hostname profile managed_system lparid remote_host\
            \n \
            \nUsage: Return macaddress \
            \n\t$PROGRAM -M -n \[-v\] -t ent \[-f] \[-x] \[-D -s speed -d duplex -S server -G gateway -C client\] hostname profile managed_system lparid remote_host\
            \n \
            \n\t-n\tDo not boot partition \
            \n\t-t\tSpecifies network type ent \
            \n\t-D\tPerform ping test, use adapter that successfully ping the server \
            \n\t-s\tNetwork adapter speed \
            \n\t-d\tNetwork adapter duplex \
            \n\t-S\tServer IP address \
            \n\t-G\tGateway IP address \
            \n\t-C\tClient IP address \
            \n\t-m\tMAC Address \
            \n\t-v\tVerbose output \
            \n\t-x\tDebug output \
            \n\t-f\tForce close virtual terminal session \
            \n\t-w\tSet boot device order \
            \n\t\t\t0: Don't set boot device order \
            \n\t\t\t1: Set network as boot device \
            \n\t\t\t2: Set network as 1st boot device, disk as 2nd boot device \
            \n\t\t\t3: Set disk as 1st boot device, network as 2nd boot device \
            \n\t\t\t4: set disk as boot device \
            \n\t-M\tDiscovery ethernet adapter mac address and location code \
            \n\t--help\tPrints this help\n"
    exit 1
}

#
# PROCEDURE
#
# Check command line arguments
#
proc ck_args {} {
    global adap_speed
    global adap_duplex
    global client_ip
    global server_ip
    global gateway_ip
    global node
    global manage
    global profile
    global hcp 
    global lparid 
    global PROGRAM
    global discover_macaddr
    global extra_args
    global macaddress
    global phys_loc
    global discover_all
    global discovery
    global noboot
    global filename

    if { $discovery && ( $adap_speed == "" || $adap_duplex == "" ) } {
        send_user "$PROGRAM: Speed and duplex required\n"
        usage 
    }

    if { $discovery && $client_ip == "" } {
        send_user "$PROGRAM: Client IP is required\n"
        usage
    }

    if { $discovery && $server_ip == "" } { 
        send_user "$PROGRAM: Server IP is required\n"
        usage
    }

    if { $discovery && $gateway_ip == "" } {
        send_user "$PROGRAM: Gateway IP is required\n"
        usage
    }

    if { $node == "" } {
        send_user "$PROGRAM: Node is required\n"
        usage
    } else {
        nc_msg "$PROGRAM Status: node $node\n"
    }

    if { $manage == "" } {
        send_user "$PROGRAM: Managed system is required\n"
        usage
    } else {
        nc_msg "$PROGRAM Status: managed system $manage\n"
    }

    if { $hcp == "" } {
        send_user "$PROGRAM: Hardware control point address is required\n"
        usage
    } else {
        nc_msg "$PROGRAM Status: Hardware control point $hcp\n"
    }
    if { $lparid == "" } {
        send_user "$PROGRAM: LPAR Id is required\n"
        usage
    } else {
        nc_msg "$PROGRAM Status: LPAR Id is $lparid\n"
    }
    if { $profile == "" } {
        send_user "$PROGRAM: Profile is required\n"
        usage
    } else {
        nc_msg "$PROGRAM Status: profile $profile\n"
    }

    if { $discover_macaddr && $extra_args != "" } {
        send_user "$PROGRAM: Can not specify -M and -g flags together.\n"
	usage
    }

    if { $discover_macaddr && ( $macaddress != "" || $phys_loc != "" ) } {
	send_user "$PROGRAM: Can not specify -M and -l or -m flags together.\n"
        usage
    }

    if { $macaddress != "" && $phys_loc != "" } {
        send_user "$PROGRAM: Can not specify -l and -m flags together.\n"
        usage
    }

    if { $discover_all && ( $macaddress != "" || $phys_loc != "" ) } {
        send_user "$PROGRAM: Can not specify -A and -m or -l flags together.\n"
        usage
    }
   
    if { $discover_all && !$discovery && !$noboot } {
        send_user "$PROGRAM: Flag -A must be specify with flag -D for booting.\n"
        usage
    }

    if { $discover_macaddr && $discovery && 
         ( $server_ip == "" || $gateway_ip == "" || $client_ip == "" || $adap_speed == "" || $adap_duplex == "" ) } {
        send_user "$PROGRAM: Flag -M with -D require arguments for -C, -S, -G, -s and -d.\n"
        usage
    }
   
    if { $discover_macaddr && !$discovery && 
         ( $server_ip != "" || $gateway_ip != "" || $client_ip != "" || $adap_speed != "" || $adap_duplex != "" ) } {
        send_user "$PROGRAM: Flag -M with arguments for -C, -S, -G, -s and -d require -D flag.\n"
        usage
    }

    if { $discover_macaddr && !$noboot } {
        send_user "$PROGRAM: -M flag requires -n.\n"
        usage
    }

    if { [regexp "(\[ ]+)-" $node ] } {
        send_user "$PROGRAM: Error : $node\n"
        send_user "$PROGRAM: Node is required\n"
        exit 1
    }

    if { [regexp "(\[ ]+)-" $manage ] } {
        send_user "$PROGRAM: Error : $managed\n"
        send_user "$PROGRAM: Managed system is required\n"
        exit 1
    }

    if { [regexp "(\[ ]+)-" $profile ] } {
        send_user "$PROGRAM: Error : $profile\n"
        send_user "$PROGRAM: Profile is required\n"
        exit 1
    }
}

#
# PROCEDURE
# 
# Declare procedure to send commands slowly. This is needed because
# some bytes are missed by the service processor when sent at top speed. 
# The sleep was needed because a command was sent sometimes before the 
# results of the previous command had been received.
#
# The Open Firmware is constrained on how quickly it can process input streams.
# The following code causes expect to send 10 characters and then wait 1 second
# before sending another 10 bytes.
#
proc send_command {} {

    global command 
    global spawn_id_rconsole

    # sleep so that any error messages from previous command are cleared up
    exec sleep 1
    send -i $spawn_id_rconsole -- $command
}

#
# PROCEDURE
#
# Declare procedure to parse the full device tree that is displayed as a result
# of an ls command. The information needed is the phandle and full device name
# of a supported network card found in the device tree. The phandle is used in
# other procedures to get and change properties of the network card. The full
# device name is used to network boot from the network adapter.
# 
proc get_phandle {} {

    global spawn_id_rconsole
    global expect_out
    global phandle_array
    global full_path_name_array
    global rc
    global command
    global adapter_found
    global adap_type
    global dev_count
    global dev_pat
    global dev_type
    global DSPMSG
    global PROGRAM
    global NODENAME

    nc_msg "$PROGRAM Status: Starting to get full device name and phandle\n"

    # This is the first procedure entered after getting to the ok prompt. On entry
    # the current device is not root. The command 'dev /' is sent to get to the
    # root of the device tree. There is no output from the dev command. The expected
    # output is the ok prompt ('>').
    #
    # The pwd command can be used to determine what the current device is. 
    #
    set timeout 30     ;# shouldn't take long
 
    nc_msg "$PROGRAM Status: sending dev / command\n"
    set command  "dev /\r"
    send_command
    expect {
        -i $spawn_id_rconsole
        -re "dev(.*)ok(.*)0 >" {
            nc_msg "$PROGRAM Status: at root\n"
        }
        -re "]" {
            send_user "$PROGRAM: Unexpected prompt\n"
            set rc 1
            return
        }
        timeout {
            send_user "$PROGRAM: Timeout\n"
            set rc 1
            return
        }
        eof {
            send_user "$PROGRAM: Cannot connect to $NODENAME\n"
            set rc 1
            return
        }
    }

    # Next, the 'ls' command is sent. The result is a display of the entire 
    # device tree. The code then looks at the 
    # output from the ls command one line at a time, trying to match it with the
    # regexp pattern in dev_pat, an array that contains all the supported network
    # adapters. When found, the adapter type, the phandle and path name are saved 
    # in array variables. 
    #
    # The complicated part is that the full path name may be spread over more than
    # one line. Each line contains information about a node. If the supported 
    # network adapter is found on an nth level node, the full path name is the
    # concatenation of the node information from the 0th level to the nth level.
    # Hence, the path name from each level of the device tree needs to be saved.
    #
    # The pattern "\n(\[^\r]*)\r" is worth a second look. It took
    # many hours of debug and reading the expect book to get it right. When more
    # than one line of data is returned to expect at once, it is tricky getting
    # exactly one line of data to look at. This pattern works because it looks
    # for a newline(\n), any character other than a carriage return(\[^\r]*), and
    # then for a carriage return. This causes expect to match a single line.
    # If (.*) is used instead of (\[^\r]*), multiple lines are matched. (that was
    # attempt number 1)         
    #
    # Once a single line is found, it tries to determine what level in the device
    # tree this line is.  
    # searching through subsequent lines and subsequent levels until an 
    # adapter is found.  
    # The level of the current line, which
    # is calculated based on the assumption of "Level = (Leading Spaces - 1)/2".
    # Leading Spaces is the number of spaces between the first colon ':' and the
    # first non-space character of each line.
    #
    # Using the -d flag helped a lot in finding the correct pattern.  
    #
    nc_msg "$PROGRAM Status: sending ls command\n"
    set command  "ls \r"   
    send_command

    set adapter_found 0
    set done 0

    set timeout 60     ;# shouldn't take more than a few minutes
    while { ! $done } {
        # this expect call isolates single lines
        # This code uses the tcl regexp to parse the single line 
        # isolated by expect. 
        #
        # When the ok prompt ('>') is matched, this indicates the end of
        # the ls output, at which point the done variable is set, to break
        # out of the loop.
        #
        # All other lines are ignored.
        #  
        expect {
            -re "(\n)(\[^\r]*)(\r)" {
                if { [regexp "\n(\[^\r]*):(\[ ]+)/(\[^\r]*)\r" $expect_out(0,string) x1 x2 x3 x4] } {

                    # Each level is inspected for a match
                    set level [expr ([string length $x3]-1)/2]
                    set path($level) $x4
                    for {set j 0} {$j < $dev_count} {incr j 1} {
                        if {[regexp $dev_pat($j) $x4]} {
                            if {[regexp "hfi-ethernet" $x4] && $dev_pat($j) == "ethernet"} {
                                continue
                            }
                            incr adapter_found 1
                            for {set i 0} {$i <= $level} {incr i 1} {
                                append full_path_name_array($adapter_found) "/$path($i)"
                            }
                            set phandle_array($adapter_found) $x2
                            set adap_type($adapter_found) $dev_type($j) 
                            break
                        }
                    }
                }
            } 
            -re ">" {
                set done 1
            }
            timeout {
                send_user "$PROGRAM: Timeout isolating single line of ls output\n"
                set rc 1
                return
            }
            eof {
                send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                set rc 1
                return
            }
        }
    }
    # Did we find one or more adapters?
    if { $adapter_found > 0 } {
        set rc 0
    } else {
        send_user "$PROGRAM: No network adapters found\n"
        set rc -1
    }
}


#
# PROCEDURE
#
# Declare procedure to obtain the list of valid adapter connector properties
# from the adapter card.  Connector types can be rj45, sc, 9pin, aui,
# bnc, or mic.  Speeds can be 10, 100, or 1000.  Duplex can be half or
# full.  This procedure will use the "supported-network-types"
# argument to the get-package-property command to get the list of
# properties for the given adapter.
#

proc get_adap_prop { phandle } {

    global rc
    global spawn_id_rconsole
    global expect_out
    global command
    global adap_prop_list
    global PROGRAM
    global NODENAME

    set adap_prop_list {}
    set rc 0
    set timeout 120 
    set state 0
    nc_msg "$PROGRAM Status: get_adap_prop start\n"

    # state 0, stack count 0
    set done(0) 0
    set cmd(0) "\" supported-network-types\" $phandle get-package-property\r"
    set msg(0) "$PROGRAM Status: rc and all supported network types now on stack\n"
    set pattern(0) "(.*)3 >(.*)"
    set newstate(0) 1

    # state 1, return code and string on stack
    set done(1) 0
    set cmd(1) ".\r"
    set msg(1) "$PROGRAM Status: All supported network types now on stack\n"
    set pattern(1) "(.*)2 >(.*)"
    set newstate(1) 2

    # state 2, data ready to decode
    set done(2) 0
    set cmd(2) "decode-string\r"
    set msg(2) "$PROGRAM Status: supported network type isolated on stack\n"
    set pattern(2) "(.*)ok(.*)4 >(.*)"
    set newstate(2) 3

    # state 3, decoded string on stack
    set done(3) 0
    set cmd(3) "dump\r"
    set msg(3) "$PROGRAM Status: supported network type off stack\n"
    set pattern(3) ".*:.*:(.*):.*:.*:(.*):.*(2 >)(.*)"
    set newstate(3) 4

    # state 4, need to check for more data to decode
    set done(4) 0
    set cmd(4) ".s\r"
    set msg(4) "$PROGRAM Status: checking for more supported network types\n"
    set pattern(4) ".s (\[0-9a-f]* )(.*)>"
    set newstate(4) 5

    # state 5, done decoding string, clear stack
    set done(5) 0
    set cmd(5) ".\r"
    set msg(5) "$PROGRAM Status: one entry on stack cleared\n"
    set pattern(5) "(.*)ok(.*)1 >(.*)"
    set newstate(5) 6

    # state 6, finish clearing stack, choose correct adapter type
    set done(6) 0
    set cmd(6) ".\r"
    set msg(6) "$PROGRAM Status: finished clearing stack\n"
    set pattern(6) "(.*)ok(.*)0 >(.*)"
    set newstate(6) 7

    # state 7, done
    set done(7) 1

    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)
                set state $newstate($state)

                # After state 3, the network type is parsed and the connector
                # type extracted.  If the type hasn't been found, add it to
                # the list of supported connector types.
                if { $state == 4 } {
                    set nw_type $expect_out(1,string)$expect_out(2,string)

                    #  Build the adapter properties from the string
                    regexp .*,(.*),(.*),(.*) $nw_type dummy nw_speed nw_conn nw_duplex
                    set adap_prop "$nw_speed,$nw_conn,$nw_duplex"
                    nc_msg "$PROGRAM Status: Adapter properties are $adap_prop\n"

                    # if it's not in the list, add it, otherwise continue
                    if { [ lsearch adap_prop_list $adap_prop ] == -1 } {
                        lappend adap_prop_list $adap_prop
                        nc_msg "$PROGRAM Status: Adding adapter properties to list\n"
                    }
                }

                # After state 4, a test is done to see if all of the supported
                # network types have been decoded. If they have been, the
                # state variable is left alone. if not, the state variable is
                # set to 2, causing a loop back to the step where the
                # decode-string command is sent.
                if { $state == 5 } {
                    if { [ string index $expect_out(2,string) 0] != 0 } {
                        set state 2
                   }
                }

            }
            -re "]" {
                send_user "$PROGRAM: Unexpected prompt\n"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                send_user "$PROGRAM: Default catch error\n"
                set rc 1
                return
            }
            timeout {
                send_user "$PROGRAM: Timeout in getting adapter properpties\n"
                nc_msg "timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                set rc 1
                return
            }
        }
    }
    set rc 0
    return
}

#
# PROCEDURE
#
# Declare procedure to obtain the ethernet or mac address property from the    
# ethernet card.
# 
# 3 commands lines containing a total of 6 commands are used.
#
# The get-package-property command is an example of a command
# that takes it's arguments off the stack and puts the results back onto the 
# stack. Because of this, the arguments for the get-package-property command
# are in front of the command verb.
#
#
# The only reason this procedure is implemented in a loop is to avoid coding
# 3 expect commands. 

proc get_mac_addr { phandle } {

    global rc
    global spawn_id_rconsole
    global expect_out
    global command     
    global PROGRAM
    global NODENAME

    set rc 0
    nc_msg "$PROGRAM Status: get_mac_addr start\n"

    # cmd(0) could have been sent as 3 commands. " mac-address" (tcl forces
    # the use of \") is the first command on this line. The result of entering
    # " mac-address" is that 2 stack entries are created, and address and a length.
    #
    # The next command in cmd(0) is the phandle with no quotes. This results in
    # one stack entry because the phandle is an address.
    # 
    # the third command in cmd(0) is get-package-property. After this command, there
    # are 3 stack entries (return code, address and length of mac-address).
    # state 0, stack count 0, send command
    set done(0) 0
    set cmd(0) "\" local-mac-address\" $phandle get-package-property\r"
    set msg(0) "$PROGRAM Status: return code and mac-address now on stack\n"
    set pattern(0) "(.*)3 >(.*)"
    set newstate(0) 1

    # cmd(1) is a dot (.). This is a stack manipulation command that removes one 
    # thing from the stack. pattern(1) is looking for a prompt with the 2 indicating
    # that there are 2 things left on the stack.
    # state 1, return code and mac-address on stack
    set done(1) 0
    set cmd(1) ".\r"
    set msg(1) "$PROGRAM Status: mac-address now on stack\n"
    set pattern(1) "(.*)2 >(.*)"
    set newstate(1) 2

    # cmd(2) is the dump command. This takes an address and a length off the stack
    # and displays the contents of that storage in ascii and hex. The long pattern
    # puts the hex into the variable expect_out(3,string). The tcl verb 'join' is 
    # used to eliminate the spaces put in by the dump command.
    # state 2, mac-address on stack
    set done(2) 0
    set cmd(2) ": dump-mac ( prop-addr prop-len -- ) \
                cr  \
                dup decode-bytes  2swap 2drop   ( data-addr data-len ) \
                ( data-len ) 0 ?do \
                    dup c@ 2 u.r                  ( data-addr ) \
                    char+                        ( data-addr' ) \
                loop \
                drop \
                cr \
                ; \r"
    set msg(2) "$PROGRAM Status: set command\n"
    set pattern(2) "(.*)ok"
    set newstate(2) 3

    set done(3) 0
    set cmd(3) "dump-mac\r"
    set msg(3) "$PROGRAM Status: mac-address displayed, stack empty\n"
    set pattern(3) "dump-mac(.*)ok"
    set newstate(3) 4


    # state 4, all done
    set done(4) 1

    set state 0
    set timeout 60     ;# shouldn't take long
    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)  
                set state $newstate($state)
            }
            "1 > " {
                if { $state == 0 } {
                    # An error occurred while obtaining the mac address.  Log the error,
                    # but don't quit nodecond.  instead, return NA for the address
                    #
                    set command ".\r"
                    send_command
                    expect {
                        -i $spawn_id_rconsole
                        -re "(-*\[0-9\]*)  ok(.*)0 >(.*)" {
                            set mac_rc $expect_out(1,string)
                            nc_msg "$PROGRAM Status: Error getting MAC address for phandle=$phandle. RC=$mac_rc.\n"
                            send_user "# Could not obtain MAC address; setting MAX to NA\n" 
                            set rc 0
                            set mac_address "NA"
                            return $mac_address
                        }
                        timeout {
                            send_user "$PROGRAM: Timeout when getting mac address\n"
                            nc_msg "timeout state is $state\n"
                            set rc 1
                            return
                        }
                        eof {
                            send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                            set rc 1
                            return
                        }
                    }
                }
            }
            -re "]" {
                send_user "$PROGRAM: Unexpected prompt\n"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                send_user "$PROGRAM: Default catch error\n"
                set rc 1
                return
            }
            timeout {
                send_user "$PROGRAM: Timeout in getting mac address\n"
                nc_msg "timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                set rc 1
                return
            }
        }
    }
    # if the state is 0, 1, or 2, an error occurred and the join will fail
    if { $state == 4 } {
        set mac_address [ join $expect_out(1,string) "" ]
        set rc 0
        return $mac_address
    } else {
        set rc -1
    }
}

#
# PROCEDURE
#
# Declare procedure to obtain the list of ethernet adapters, their physical
# location codes and MAC addresses.
# 
# The get-package-property command is an example of a command
# that takes it's arguments off the stack and puts the results back onto the 
# stack. Because of this, the arguments for the get-package-property command
# are in front of the command verb.
#
# The only reason this procedure is implemented in a loop is to avoid coding
# 3 expect commands. 

proc get_adaptr_loc { phandle } {

    global rc
    global spawn_id_rconsole
    global expect_out
    global command     
    global PROGRAM
    global NODENAME

    set rc 0
    nc_msg "$PROGRAM Status: get_adaptr_loc start\n"

    # cmd(0) could have been sent as 3 commands. " ibm,loc-code" (tcl forces
    # the use of \") is the first command on this line. The result of entering
    # " ibm,loc-code" is that 2 stack entries are created, and address and a length.
    #
    # The next command in cmd(0) is the phandle with no quotes. This results in
    # one stack entry because the phandle is an address.
    # 
    # the third command in cmd(0) is get-package-property. After this command, there
    # are 3 stack entries (return code, address and length of mac-address).
    # state 0, stack count 0, send command
    set done(0) 0
    set cmd(0) "\" ibm,loc-code\" $phandle get-package-property\r"
    set msg(0) "$PROGRAM Status: return code and loc-code now on stack\n"
    set pattern(0) "(.*)3 >(.*)"
    set newstate(0) 1

    # cmd(1) is a dot (.). This is a stack manipulation command that removes one 
    # thing from the stack. pattern(1) is looking for a prompt with the 2 indicating
    # that there are 2 things left on the stack.
    # state 1, return code and loc-code on stack
    set done(1) 0
    set cmd(1) ".\r"
    set msg(1) "$PROGRAM Status: loc-code now on stack\n"
    set pattern(1) "(.*)2 >(.*)"
    set newstate(1) 2

    # state 2, loc-code on stack
    set done(2) 0
    set cmd(2) "dump\r"
    set msg(2) "$PROGRAM Status: loc-code displayed, stack empty\n"
    set pattern(2) "(.*)(: )(.*)( :)(.*)(\.: ok)"
    set newstate(2) 3

    # state 3, all done
    set done(3) 1

    set state 0
    set timeout 60    ;# shouldn't take long
    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)  
                set state $newstate($state)
            }
            "1 > " {
                if { $state == 0 } {
                    set command ".\r"
                    send_command
                    expect {
                        -i $spawn_id_rconsole
                        -re "(-*\[0-9\]*)  ok(.*)0 >(.*)" {
                            set loc_rc $expect_out(1,string)
                            send_user "$PROGRAM: Error getting adapter physical location.\n"
                            nc_msg "$PROGRAM Status: Error getting physical location for phandle=$phandle. RC=$loc_rc.\n"
                            set rc 1
                            return
                        }
                        timeout {
                            send_user "$PROGRAM: Timeout when openning console\n"
                            set rc 1
                            return
                        }
                        eof {
                            send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                            set rc 1
                            return
                        }
                    }
                }
            }
            -re "]" {
                send_user "$PROGRAM: Unexpected prompt\n"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                send_user "$PROGRAM: Default catch error\n"
                set rc 1
                return
            }
            timeout {
                send_user "$PROGRAM: Timeout in openning console\n"
                nc_msg "$PROGRAM Status: timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                set rc 1
                return
            }
        }
    }
    if { $state == 3 } {
        set rc 0
        if { [regexp "(.*)(: )(.*)( :)(.*)(:)" $expect_out(1,string) v1 v2 v3 v4 v5 v6 ] } {
            return $v6$expect_out(5,string)
        } else {
            return $expect_out(5,string)
        }
    } else {
        set rc -1
    }
}

#
# PROCEDURE
#
# Declare procedure to ping the given server through the specified LAN adapter.
# The procedure will return 0 if the ping is successful, and 1 if the ping
# was unsuccessful.  The global rc variable will be set if there's an error.
#

proc ping_server { full_path_name phandle } {

    global spawn_id_rconsole
    global expect_out
    global server_ip
    global client_ip
    global gateway_ip
    global list_type
    global adap_speed
    global adap_duplex
    global command
    global adap_prop_list
    global rc
    global PROGRAM
    global NODENAME
    global env
    global mac_address

    nc_msg "$PROGRAM Status: ping_server start\n"

    if {[info exists env(FIRMWARE_DUMP)]} {
        Firmware_Dump $full_path_name $phandle
    }

    set j 0
    set tty_do_ping 0
    set stack_level 0
    set properties_matched 0
    set adap_conn ""
    set speed_list {}
    set duplex_list {}
    set adap_conn_list {}

    # If the adapter type chosen is ethernet, need to set the speed and duplex
    # of the adapter before we perform the ping.  If token ring or fddi,
    # this is not required, so begin with state 2.
    # 
    # cmd(0) sets the given adapter as active, to allow setting of speed
    # and duplex
    #
    # cmd(1) writes the settings to the current adapter
    #
    # cmd(2) selects the /packages/net node as the active package to access the
    # ping command.
    #
    # The next command in cmd(3) is the ping command. This places the return code
    # on the stack. A return code of 0 indicates success.
    #
    # state 0, set the current adapter
    set done(0) 0
    set cmd(0) "dev $full_path_name\r"
    set msg(0) "$PROGRAM Status: selected $full_path_name as the active adapter\n"
    set pattern(0) ".*dev(.*)0 >(.*)"
    set newstate(0) 1

    # state 1, send property command to set selected type
    set done(1) 0
    set cmd(1) "\" ethernet,$adap_speed,$adap_conn,$adap_duplex\" encode-string \" chosen-network-type\" property\r"
    set msg(1) "$PROGRAM Status: chosen network type set\n"
    set pattern(1) ".*ethernet(.*)0 >(.*)"
    set newstate(1) 2

    # state 2, activate /packages/net
    set done(2) 0
    set cmd(2) "dev /packages/net\r"
    set msg(2) "$PROGRAM Status: selected the /packages/net node as the active package\n"
    set pattern(2) ".*dev(.*)ok(.*)0 >(.*)"
    set newstate(2) 3

    # state 3, ping the server
    set done(3) 0
    set msg(3) "$PROGRAM Status: ping return code now on stack\n"
    set newstate(3) 4
    #IPv6
    if { [regexp ":" $server_ip]!=0 } {
        #::1, calculate link local address
        if { [string compare "$client_ip" "::1"] == 0  } {
            #set macaddr [get_mac_addr $phandle]
            set linklocal_ip [exec /opt/xcat/share/xcat/tools/mac2linklocal -m $mac_address]
        } else {
            set linklocal_ip $client_ip
        }
        set cmd(3) "ping $full_path_name:ipv6,$server_ip,$linklocal_ip,$gateway_ip\r"
    } else {
        set cmd(3) "ping $full_path_name:$server_ip,$client_ip,$gateway_ip\r"
    }
    set pattern(3) ".*ping(.*)ok(.*)0 >(.*)"
    
    # state 4, all done
    set done(4) 0
    set cmd(4) "0 to my-self\r"
    set msg(4) "$PROGRAM Status: resetting pointer\n"
    set pattern(4) "(.*)ok(.*)0 >(.*)"
    set newstate(4) 5
    
    # state 5, all done
    set done(5) 1

    # for ping, only need to set speed and duplex for ethernet adapters
    #
    if { $list_type == "ent" } {
        set state 0

        # Get the list of properties for this adapter
        #
        get_adap_prop $phandle
        if { $rc != 0 } {
            exit 1
        }

        if { [ llength $adap_prop_list ] == 0 } {
            send_user "$PROGRAM: No properties found for adapter '$full_path_name'\n"
            set rc 0
            return 1
        }

        # Now need to verify that the network params we were passed are valid for
        # the given adapter
        #
        foreach prop $adap_prop_list {
            regexp (.*),(.*),(.*) $prop dummy a_speed a_conn a_duplex
            if { ( $a_speed == $adap_speed ) && ( $a_duplex == $adap_duplex ) } {
                set properties_matched 1
                if { [ lsearch $adap_conn_list $a_conn ] == -1 } {
                    lappend adap_conn_list $a_conn
                }
            }
        }
        
        if { $properties_matched == 0 } {
            set adap_speed $a_speed
            set adap_duplex $a_duplex
            set properties_matched 1
            lappend adap_conn_list $a_conn
        }

        set i [ llength $adap_conn_list ]

        if { $properties_matched == 0 } {
            send_user "$PROGRAM: '$adap_speed/$adap_duplex' settings are not supported on this adapter\n"
            set rc 0
            return 1
        }
    } else {
        set state 2
    }

    set timeout 300
    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)

        send_command

        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)
                set state $newstate($state)

                if { $state == 1 } {
                    set adap_conn [ lindex $adap_conn_list $j ]
                    set cmd(1) "\" ethernet,$adap_speed,$adap_conn,$adap_duplex\" encode-string \" chosen-network-type\" property\r"
                    nc_msg "$PROGRAM Status: Trying connector type $adap_conn\n"
                    incr j 1
                }

		if { ( ($tty_do_ping == 1) && ($state == 4) ) || ($tty_do_ping != 1) && ($state == 3) } {
                    set ping_debug $expect_out(buffer)
                }


                if { ( ($tty_do_ping == 1) && ($state == 5) ) || ($tty_do_ping != 1) && ($state == 4) } {
                    if { ($tty_do_ping == 1) && ($state == 5) } {
                        set ping_rc $expect_out(1,string)
                        set stack_level $expect_out(3,string)
                    } elseif { ($state == 4) && ($tty_do_ping != 1) && ([regexp "PING SUCCESS" $expect_out(buffer)]) } {
                        set ping_rc 0
                    } elseif { [regexp "unknown word" $expect_out(buffer)] } {
                        nc_msg "$PROGRAM Status: try tty-do-ping.\n"
                        set ping_rc 1
                        set tty_do_ping 1
                        set state 3 
                        set cmd(3) "\" $full_path_name:$client_ip,$server_ip,$gateway_ip\" tty-do-ping\r"
                        set pattern(3) "(.*)ok(.*)(\[1-2\]) >(.*)"
                                    
                        # state 4, get the return code off the stack
                        set done(4) 0
                        set cmd(4) ".\r"
                        set msg(4) "$PROGRAM Status: return code displayed, stack empty\n"
                        set pattern(4) "(\[0-9\]*)  ok(.*)(\[0-1\]) >(.*)"
                        set newstate(4) 5
                                    
                        # this command is used to work around a default catch problem in open
                        # firmware.  Without it, a default catch occurs if we try to set
                        # adapter properties again after a ping
                        #           
                        # state 5, reset pointer
                        set done(5) 0
                        set cmd(5) "0 to my-self\r"
                        set msg(5) "$PROGRAM Status: resetting pointer\n" 
                        set pattern(5) "(.*)ok(.*)0 >(.*)"
                        set newstate(5) 6 
                                
                        # state 6, all done
                        set done(6) 1
                    } else {
                        set ping_rc 1
                    }

                    if { $ping_rc == 0 } {
                        send_user "# $full_path_name ping successful.\n"
                    } elseif { $ping_rc ==  1 } {
                        send_user "# $full_path_name ping unsuccessful.\n"
			nc_msg "# $full_path_name ping unsuccessful.\n"
                        nc_msg "$ping_debug\n"

                        # An unsuccessful return may leave another item on the stack to
                        # be removed.  Check for it, and remove if necessary
                        while { $stack_level != 0 } {
                            set command ".\r"
                            send_command
                            expect {
                                -i $spawn_id_rconsole
                                -re "(\[0-9\]*)  ok(.*)(\[0-1\]) >(.*)" {
                                    set stack_level $expect_out(3,string)
                                    nc_msg "$PROGRAM Status: stack_level is <$stack_level>\n"
                                }
                                -re "]" {
                                    send_user "$PROGRAM: Unexpected prompt\n"
                                    set rc 1
                                    return
                                }
                                -re "(.*)DEFAULT(.*)" {
                                    send_user "$PROGRAM: Default catch error\n"
                                    set 1
                                    return
                                }
                                timeout {
                                    send_user "$PROGRAM: Timeout in ping server\n"
                                    set 1
                                    return
                                }
                                eof {
                                    send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                                    set 1
                                    return
                                }
                            }
                        }
                        # Check if there are any more adapter connector types
                        # to try
                        #
                        if { ( $list_type == "ent" ) && ( $j < $i ) } {
                            set adap_conn [ lindex $adap_conn_list $j ]
                            nc_msg "$PROGRAM Status: Trying connector type $adap_conn\n"
                            incr j 1

                            # Need to work around a default catch problem in open
                            # firmware by sending a "0 to my-self" instruction
                            # following the ping.  To make sure this happens in
                            # this rare case where we have an adapter with multiple connectors,
                            # we have to force the instruction into the 0th slot in
                            # the array.  This is OK, since we only set the current
                            # adapter once, upon entering this procedure.
                            #
                            set done(0) 0
                            set cmd(0) "0 to my-self\r"
                            set msg(0) "$PROGRAM Status: resetting pointer\n"
                            set pattern(0) "(.*)ok(.*)0 >(.*)"
                            set newstate(0) 1

                            set state 0
                        }
                    } else {
                        send_user "$PROGRAM: Unexpected ping return code\n"
                        set rc 1
                        return
                    }
                }
            }
            -re "]" {
                send_user "$PROGRAM: Unexpected prompt\n"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                send_user "$PROGRAM: Default catch error\n"
                set rc 1
                return
            }
            timeout {
                send_user "$PROGRAM: Timeout when pinging server\n"
                set rc 1
                return
            }
            eof {
                send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                set rc 1
                return
            }
        }
    }
    set rc 0
    return $ping_rc
}

#   
# PROCEDURE
#
proc set_disk_boot {} {
    global rc
    global expect_out
    global spawn_id_rconsole
    global command
    global PROGRAM
    global NODENAME
    global phandle_array
    global full_path_name_array
    global adapter_found
    global adap_type
    global sms_dev_array_loc
    global sms_dev_array_name
    global sms_dev_array_os
    global sms_dev_array_os_ver
    global colon
    global list_physical
    global boot_device

    # state 0, get SMS screen
    set done(0) 0
    regexp "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Select Boot Options(\r)" $expect_out(0,string) x0 x1 x2 command
    set cmd(0) "$command\r"
    set msg(0) "$PROGRAM Status: sending return to repaint SMS screen\n"
    set pattern(0) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Configure Boot Device Order(\r)"
    set newstate(0) 1

    # state 1, Multiboot
    set done(1) 0
    set msg(1) "$PROGRAM Status: Multiboot\n"
    set pattern(1) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Select 1st Boot Device(\r)"
    set newstate(1) 2
    
    # state 2, Configure Boot Device Order
    set done(2) 0
    set msg(2) "$PROGRAM Status: Configure Boot Device Order"
    set pattern(2) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Hard Drive(.*)"
    set newstate(2) 3

    # state 3, Select Device Type
    set done(3) 0
    set msg(3) "$PROGRAM Status: Select Device Type"
    set pattern(3) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)SCSI(.*)"
    set newstate(3) 4

    # state 4, Select Media Type
    set done(4) 0
    set msg(4) "$PROGRAM Status: Select Media Type"
    set pattern(4) "(\n)(\[ ])(\[1])(\[.])(\[ ]+)(\\S+)(.*)"
    set newstate(4) 5 

    # state 5, Select Media Adapter 
    set done(5) 0
    set msg(5) "$PROGRAM Status: Select Media Adapter"
    set pattern(5) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)(\\S)(\[ ]+)SCSI (\[0-9]+) MB Harddisk(.*)loc=(.*)\[)]"
    set newstate(5) 6

    # state 6, Select Device
    set done(6) 0
    set msg(6) "$PROGRAM Status: Select Device"
    set pattern(6) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Set Boot Sequence(.*)"
    set newstate(6) 7

    # state 7, Select Task 
    set done(7) 0
    set msg(7) "$PROGRAM Status: Select Task"
    set pattern(7) "(.*)Current Boot Sequence(.*)"
    set newstate(7) 8 

    # state 8, Return to Main Menu
    set done(8) 0
    set cmd(8) "M"
    set msg(8) "$PROGRAM Status: Restored Default Setting.\n" 
    set pattern(8) "(.*)Navigation key(.*)"
    set newstate(8) 9

    # state 9, Getting to SMS Main Menu
    set done(9) 0
    set cmd(9) "0\r"
    set msg(9) "$PROGRAM Status: Getting to SMS Main Menu.\n"
    set pattern(9) "(.*)Exit SMS(.*)Prompt?(.*)"
    set newstate(9) 10 

    # state 10, Exiting SMS
    set done(10) 0
    set cmd(10) "Y"
    set msg(10) "$PROGRAM Status: Exiting SMS.\n"
    set pattern(10) "(.*)ok(.*)0 >(.*)"
    set newstate(10) 11

    # state 11, all done
    set done(11) 1

    set timeout 30
    set state 0
    while { $done($state) == 0 } {
	set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                if { $state == 4 } {
                    if { $expect_out(6,string) == "None" } {
                        set state  8
                    }
                }

                set state $newstate($state)
                if { ($state != 8) && ($state != 9) && ($state != 10) } {
			set cmd($state) "$expect_out(3,string)\r"
                }
            }
            
            -re "THE SELECTED DEVICES WERE NOT DETECTED IN THE SYSTEM" {
                send_user "$PROGRAM Status: THE hard disk WERE NOT DETECTED IN THE SYSTEM!\n"
                set rc 1
                #return
                exit 1
            }
                       

            timeout {
                send_user "$PROGRAM: Timeout in settin boot order\n"
                set rc 1
                return
            }
            eof {
                send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                set rc 1
                return
            }
	}
    }
}


###################################################################
#
# PROCEDURE
#
# Declare procedure to boot the system from the selected ethernet card.    
# 
# This routine does the following:
# 1. Initiates the boot across the network. (state 0 or 1)
# 
# state 99 is normal exit and state -1 is error exit.
###################################################################
proc boot_network {} {
    global rc
    global spawn_id_rconsole
    global full_path_name
    global command
    global speed
    global duplex
    global chosen_adap_type
    global server_ip
    global client_ip
    global gateway_ip
    global netmask
    global dump_target
    global dump_lun
    global dump_port
    global extra_args
    global PROGRAM
    global NODENAME
    global set_boot_order
    set rc 0
    set i 0
     

    nc_msg "$PROGRAM Status: boot_network start\n"

    ###################################################################
    # Variables associated with each of the commands sent by this routine
    # are defined below.
    #
    # The done variable is flag that is set to 1 to break out of the loop
    #
    # The cmd variable is the command to be sent to the chrp interface.
    #     In one case it set in the special processing code because the 
    #     ihandle is not available then this code is executes.
    #
    # The msg variable contains the message sent after a successful pattern match
    #
    # The pattern variable is the pattern passed to expect 
    #
    # The newstate variable indicates what command is to be issued next
    ###################################################################

    # If the install adapter is Ethernet or Token Ring, set the speed and 
    # duplex during boot.
    # state 0, stack count 0
    set done(0) 0
    if {$dump_target != ""} {
        set net_device(0) "$full_path_name:iscsi,ciaddr=$client_ip,subnet-mask=$netmask,itname=dummy,iport=$dump_port,ilun=$dump_lun,iname=$dump_target,siaddr=$server_ip,2"
        set pattern(0) "iSCSI"
    } else {
        if {$extra_args != "" } {
            if { [regexp ":" $server_ip]!=0 } { #ipv6
                set net_device(0) "$full_path_name:ipv6,speed=$speed,duplex=$duplex,siaddr=$server_ip,ciaddr=$client_ip,giaddr=$gateway_ip,filename=$NODENAME,$extra_args"
            } else {
                set net_device(0) "$full_path_name:speed=$speed,duplex=$duplex,bootp,$server_ip,,$client_ip,$gateway_ip $extra_args"
            }
        } else {
            if { [regexp ":" $server_ip]!=0 } { #ipv6
                set net_device(0) "$full_path_name:ipv6,speed=$speed,duplex=$duplex,siaddr=$server_ip,ciaddr=$client_ip,giaddr=$gateway_ip,filename=$NODENAME"
            } else {
                set net_device(0) "$full_path_name:speed=$speed,duplex=$duplex,bootp,$server_ip,,$client_ip,$gateway_ip"
            }
        }
        set pattern(0) "BOOTP"
    }
    set cmd(0) "boot $net_device(0)\r"
    set msg(0) "$PROGRAM Status: network boot initiated\n"
    set newstate(0) 99

    # If the install adapter is FDDI, don't set the speed and duplex
    # state 1
    set done(1) 0
    set net_device(1) "$full_path_name:bootp,$server_ip,,$client_ip,$gateway_ip"
    set cmd(1) "boot $net_device(1)\r"
    set msg(1) "$PROGRAM Status: network boot initiated\n"
    set pattern(1) "BOOTP"
    set newstate(1) 99

    # state 99, all done
    set done(99) 1

    # state -1, all done
    set done(-1) 1

    if { $chosen_adap_type ==  "fddi" } {
        set state 1
    } else {
        if { $speed == "" || $duplex == "" } {
            send_user "$PROGRAM: Cannot set speed or duplex for network boot\n"
            set rc 1
            return
        }
        set state 0
    }

    ##################################################################
    # Set the boot device order.
    ##################################################################
    if { $set_boot_order > 0 } {
        set done(2) 0
        set msg(2) "$PROGRAM Status: read original boot-device\n"
        set cmd(2) "printenv boot-device\r"
        set pattern(2) ".*boot-device\\s+(\\S+)(.*)ok(.*)"
        set newstate(2) 3

        set done(3) 0
        set msg(3) "$PROGRAM Status: set the environment variable boot-device\n"
        set pattern(3) "(.*)ok(.*)(\[0-9]) >(.*)"
        if {  $state == 0 } {
            set newstate(3) 0
        } else {
            set newstate(3) 1
        }
        set state 2
    }  

    set timeout 30     ;# shouldn't take long
    while { $done($state) == 0 } {
        set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -nocase
            -re $pattern($state) {
                if { $state == 2 } {
                    if { $set_boot_order == 1 } {
                        ########################################
                        # Set network as boot device
                        ########################################
                        set cmd(3) "setenv boot-device $net_device($newstate(3))\r"
                    } elseif { $set_boot_order == 2 } {
                        ########################################
                        # Set network as 1st boot device,disk as 2nd boot device
                        ########################################
                        set boot_device_bk $expect_out(1,string)
                        set cmd(3) "setenv boot-device $net_device($newstate(3)) $boot_device_bk\r"
                    } elseif { $set_boot_order == 3 } {
                        ########################################
                        # Set disk as 1st boot device,network as 2nd boot device
                        ########################################
                        set boot_device_bk $expect_out(1,string)
                        set cmd(3) "setenv boot-device $boot_device_bk $net_device($newstate(3))\r"
                    } elseif { $set_boot_order == 4 } {
                        ########################################
                        # set disk as boot device 
                        ########################################
                        set boot_device_bk $expect_out(1,string)
                        set cmd(3) "setenv boot-device $boot_device_bk\r"
                    }
                }
                nc_msg $msg($state)
                set state $newstate($state)
            }
            -re "----" {
                nc_msg $msg($state)
                set state $newstate($state)
            }
            -re "]" {
                send_user "$PROGRAM: Unexpected prompt\n"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                send_user "$PROGRAM: Default catch error\n"
                set rc 1
                return
            }
            timeout {
                send_user "$PROGRAM: Timeout in boot network\n"
                nc_msg "timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                set rc 1
                return
            }
        }
    }
}

#
# PROCEDURE
#
#
proc Boot {} {
    global rc
    global expect_out
    global spawn_id_rconsole
    global PROGRAM
    global NODENAME

    set rc 0

    nc_msg "$PROGRAM Status: waiting for the boot image to boot up.\n"

    set timeout 1200      ;# could take a while depending on configuration

    # check for bootp response error
    expect {
        -i $spawn_id_rconsole 
        -re "RESTART-CMD" {
            # If we see a "problem doing RESTART-CMD" message, we re-hit the OPEN-DEV
            # issue after firmware rebooted itself and we need to retry the netboot once more
	        send_user "$PROGRAM: The network boot ended in an error.\n"
            send_user "$PROGRAM: Error : $RESTART-CMD\n"
            set rc 5
            return
        }
        -nocase
        -re {!([0-9A-F]+)} {
            send_user "$PROGRAM: The network boot ended in an error.\n"
            nc_msg $expect_out(buffer)
            set rc 1
            return
        }
        -ex {BOOTP} {
            nc_msg "$PROGRAM: # Network boot proceeding, exiting.\n"
        }
        -ex {------} {
            nc_msg "$PROGRAM: # Network boot proceeding, exiting.\n"
        }
        -ex {iSCSI} {
            nc_msg "$PROGRAM: # Network boot proceeding, exiting.\n"
        }
        timeout {
            set mins [expr ($timeout/60)]
            send_user "$PROGRAM: Timeout waiting for the boot image to boot up. \
                  \n$PROGRAM waited '$mins' minutes for the boot image to boot. \
                  \nEither the boot up has taken longer than expected or \
                  \nthere is a problem with system boot.  Check the boot \
                  \nof the node to determine if there is a problem.\n"
            nc_msg $expect_out(buffer)
            set rc 1
            return
        }                                                                       
        eof {                                                                   
            send_user "$PROGRAM: Port closed waiting for boot image to boot.\n"                               
            set rc 1                                                            
            return                                                              
        }                                                                       
    }
}

###################################################################
#
# PROCEDURE
#
# Create multiple open-dev function in Open Firmware to try open
# a device.  The original problem is a firmware issue which fails
# to open a device.  This procedure will create multiple sub
# function in firmware to circumvent the problem.
#
###################################################################
proc multiple_open-dev {} {
    global rc
    global expect_out
    global spawn_id_rconsole
    global PROGRAM
    global NODENAME
    global comman
    global env
    global command

    set command  "dev /packages/net \r"
    send_command

    set command  "FALSE value OPEN-DEV_DEBUG \r"
    send_command

    if {[info exists env(OPEN_DEV_DEBUG)]} {
       set command "TRUE to OPEN-DEV_DEBUG \r"
       send_command
    }

    set command  ": new-open-dev ( str len -- true|false ) \
                  open-dev_debug if cr .\" NEW-OPEN-DEV: Entering, Device : \" 2dup type cr then \
                  { _str _len ; _n } \
                  0 -> _n \
                  get-msecs dup d# 60000 + ( start timeout ) \
                  begin \
                     ( start timeout ) get-msecs over > if \
                        open-dev_debug if \
                           ( start timeout ) drop get-msecs swap - \
                           cr .\" FAILED TO OPEN DEVICE\" \
                           cr .\" NUMBER OF TRIES \" _n .d \
                           cr .\" TIME ELAPSED \" ( time ) .d .\"  MSECONDS\" cr \
                        else \
                           ( start timout ) 2drop \
                        then \
                        false exit \
                     else \
                        true \
                     then \
                     while \
                        ( start timeout ) \
                        _n 1 + -> _n \
                        _str _len open-dev ( ihandle|false ) ?dup if \
                        -rot ( ihandle start timeout ) \
                        open-dev_debug if \
                           ( start timeout ) drop get-msecs swap - \
                           cr .\" SUCCESSFULLY OPENED DEVICE\" \
                           cr .\" NUMBER OF TRIES \" _n .d \
                           cr .\" TIME ELAPSED \" ( time ) .d .\" MSECONDS\" cr \
                        else \
                           ( start timeout ) 2drop \
                        then \
                        ( ihandle ) exit \
                 then \
                 ( start timeout ) \
                 repeat \
                 ; \r"
    send_command

    set command " patch new-open-dev open-dev net-ping \r"
    send_command

    set timeout 30
    expect {
        -i $spawn_id_rconsole
        -re "patch new-open-dev(.*)>" {
            nc_msg "$PROGRAM Status: at End of multiple_open-dev \n"
        }
        -re "]" {
            send_user "$PROGRAM: Unexpected prompt\n"
            set rc 1
            return
        }
        timeout {
            send_user "$PROGRAM: Timeout\n"
            set rc 1
            return
        }
        eof {
            send_user "$PROGRAM: Cannot connect to $NODENAME\n"
            set rc 1
            return
        }
   }
}

###################################################################
#  
# PROCEDURE
#  
# Declare procedure to get additional firmware debug statement.
#  
###################################################################
proc Firmware_Dump { device_path phandle } {
    global rc
    global expect_out
    global spawn_id_rconsole
    global PROGRAM
    global NODENAME
    global command

    nc_msg "$PROGRAM Status: Firmware_Dump start\n"

    # state 0
    set done(0) 0
    set cmd(0) "dev /packages/obp-tftp\r"
    set msg(0) "$PROGRAM Status: selected /packages/obp_tftp\n"
    set pattern(0) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(0) 1

    # state 1
    set done(1) 0
    set cmd(1) ": testing1 .\" OBP-TFTP entry\" cr init-nvram-adptr-parms ;\r"
    set msg(1) "$PROGRAM Status: running test - OBP-TFTP entry\n"
    set pattern(1) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(1) 2

    # state 2
    set done(2) 0
    set cmd(2) ": testing2 .\" OBP-TFTP exit, TRUE\" cr true ;\r"
    set msg(2) "$PROGRAM Status: running test - OBP-TFTP exit\n"
    set pattern(2) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(2) 3

    # state 3
    set done(3) 0
    set cmd(3) "patch testing1 init-nvram-adptr-parms open\r"
    set msg(3) "$PROGRAM Status: running test - patch testing1\n"
    set pattern(3) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(3) 4

    # state 4
    set done(4) 0
    set cmd(4) "patch testing2 true open\r"
    set msg(4) "$PROGRAM Status: running test - patch testing2\n"
    set pattern(4) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(4) 5

    # state 5
    set done(5) 0
    set cmd(5) "dev $device_path\r"
    set msg(5) "$PROGRAM Status: running test - dev $device_path\n"
    set pattern(5) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(5) 6

    # state 6
    set done(6) 0
    set cmd(6) "true to debug-init\r"
    set msg(6) "$PROGRAM Status: running test - true to debug-init\n"
    set pattern(6) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(6) 7

    # state 7
    set done(7) 0
    set cmd(7) "true to debug-error\r"
    set msg(7) "$PROGRAM Status: running test - true to debug-error\n"
    set pattern(7) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(7) 8

    # state 8
    set done(8) 0
    set cmd(8) "$phandle to active-package\r"
    set msg(8) "$PROGRAM Status: running $phandle to active-package\n"
    set pattern(8) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(8) 9

    # state 9
    set done(9) 0
    set cmd(9) ".properties\r"
    set msg(9) "$PROGRAM Status: running .properies\n"
    set pattern(9) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(9) 10

    # state 10
    set done(10) 0
    set cmd(10) "clear\r"
    set msg(10) "$PROGRAM Status: running clear\n"
    set pattern(10) "(.*)ok(.*)(\[0-9]) >(.*)"
    set newstate(10) 11

    # state 11, all done
    set done(11) 1

    set state 0
    set timeout 30    ;# shouldn't take long
    while { $done($state) == 0 } {
        set command $cmd($state)
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)
                set state $newstate($state)
            }
            -re "]" {
                send_user "$PROGRAM: Unexpected prompt\n"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                send_user "$PROGRAM: Default catch error\n"
                set rc 1
                return
            }
            timeout {
                send_user "$PROGRAM: Timeout\n"
                nc_msg "$PROGRAM Status: timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                send_user "$PROGRAM: Cannot connect to $NODENAME\n"
                set rc 1
                return
            }
        }
    }
}



#
# Main MAIN main
#
set BINPATH "/opt/xcat/bin"
set PROGRAM "lpar_netboot"

set CONSOLEBIN "/opt/xcat/bin/rcons"

set noboot 0        ;# default is to boot
set Boot_timeout 3000

global expect_out
global loc_code
# Flags and initial variable
set discovery 0
set discover_all 0
set hdr_printed 0
set verbose 0
set discover_macaddr 0
set rc 0
set debug_flag 0
set rmvterm_flag 0
set immed_flag 0
set from_of 0
set dev_type_found 0
set list_physical 0
set set_boot_order 0
set colon 0
set choice 0
set full_path_name ""
set adap_speed ""
set adap_duplex ""
set client_ip ""
set server_ip ""
set gateway_ip ""
set extra_args ""
set macaddress ""
set netmask ""
set dump_target ""
set dump_lun ""
set dump_port ""
set phys_loc ""
set userid ""
set passwd ""
set prompt "\\\$ \$"
set ssh_spawn_id 0
set mac_address ""

# List supported network adapters here.  dev_pat is an array of regexp patterns
# the script searches for in the device tree listing.  dev_type is the type
# of device displayed in the output.
set dev_pat(0) "ethernet"
set dev_type(0) "ent"
set dev_pat(1) "token-ring"
set dev_type(1) "tok"
set dev_pat(2) "fddi"
set dev_type(2) "fddi"
set dev_count [array size dev_pat]

#
# Log the process id
#
set proc_id [ eval pid ]
nc_msg "$PROGRAM Status: process id is $proc_id\n"

#
#
# Process command line options and parameters
#
#
while { [llength $argv] > 0} {
    set flag [lindex $argv 0]
    switch -glob -- $flag {

        "-A" { set discover_all 1
               set argv [lrange $argv 1 end]
             }

        "-C" { set client_ip [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-D" { set discovery 1
               set argv [lrange $argv 1 end]
             }

        "-G" { set gateway_ip [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-P" { set list_physical 1
               set argv [lrange $argv 1 end]
             }

        "-M" { set discover_macaddr 1
               set argv [lrange $argv 1 end]
             }

        "-N" { set netmask [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-S" { set server_ip [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-c" { set colon 1
               set argv [lrange $argv 1 end]
             }

        "-d" { set adap_duplex [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-f" { set rmvterm_flag 1
               set argv [lrange $argv 1 end]
             }

        "-g" { set extra_args [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-i" { set immed_flag 1
               set argv [lrange $argv 1 end]
             }

        "-o" { set from_of 1
               set argv [lrange $argv 1 end]
             }

        "-w" { set set_boot_order [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-L" { set dump_lun [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-l" { set phys_loc [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-m" { set macaddress [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-n" { set noboot 1
               set argv [lrange $argv 1 end]
             }

        "-p" { set dump_port [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-s" { set adap_speed [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-T" { set dump_target [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-t" { set list_type [lindex $argv 1]
               set argv [lrange $argv 2 end]

               if { $list_type == "hfi-ent" } {
                   set dev_pat(0) "hfi-ethernet"
                   set dev_type(0) "hfi-ent"
               }

               #
               # Validate the argument
               #
               set dev_type_found 0
               foreach dev [array names dev_type] {
                   if {$dev_type($dev) == $list_type} {
                       set dev_type_found 1
                       break
                   }
               }

               if {$dev_type_found == 0 } {
                  send_user "$PROGRAM:$dev_type_found, '$list_type' is not a valid adapter choice\n"
                  exit 1
               }
             }

        "-v" { set verbose 1
               set argv [lrange $argv 1 end]
             }

	"-x" { set debug_flag 1
               set argv [lrange $argv 1 end]
             }

        "--help" {
                usage
             }

        "-*" { send_user "$PROGRAM: Illegal option: $flag\n"
               usage
             }

        default {break}
    }
}

if { [llength $argv] < 1 } {
    send_user "$PROGRAM: Missing parameter\n"
    usage
}

set arg0 [lindex $argv 0]
set arg1 [lindex $argv 1]

if { [llength $argv] > 10 } {
    send_user "$PROGRAM: Extraneous parameter(s)\n"
    usage
}

if { $list_physical == 0 } {
    set dev_pat(3) "l-lan"
    set dev_type(3) "ent"
    set dev_count [array size dev_pat]
} else {
    set dev_pat(3) ""
    set dev_type(3) "" 
    set dev_count [array size dev_pat]
}

if { $set_boot_order > 1 } {
    set dev_pat(4) "scsi"
    set dev_type(4) "disk"
    set dev_count [array size dev_pat]
}

set node [lindex $argv 0]
set profile [lindex $argv 1]
set manage [lindex $argv 2]
set lparid [lindex $argv 3]
set hcp [lindex $argv 4]
set name [lindex $argv 5]

if {$dev_type_found}    { nc_msg "$PROGRAM Status: List only $list_type adapters\n"               }
if {$noboot}            { nc_msg "$PROGRAM Status: -n (no boot) flag detected\n"                  }
if {$discovery}         { nc_msg "$PROGRAM Status: -D (discovery) flag detected\n"                }
if {$discover_all}      { nc_msg "$PROGRAM Status: -A (discover all) flag detected\n"             }
if {$verbose}           { nc_msg "$PROGRAM Status: -v (verbose debug) flag detected\n"            }
if {$discover_macaddr}  { nc_msg "$PROGRAM Status: -M (discover mac address) flag detected\n"     }
if {$immed_flag}        { nc_msg "$PROGRAM Status: -i (force immediate shutdown) flag detected\n" }
if {$list_physical}     { nc_msg "$PROGRAM Status: -P (list only phsical network) flag detected\n"}
if {$colon}             { nc_msg "$PROGRAM Status: -c (list colon separated ) flag detected\n"    }

if {$debug_flag}        { 
    nc_msg "$PROGRAM Status: -d (debug) flag detected\n" 
    exp_internal 1
    log_user 1
}

if {$discovery && $adap_speed != ""}  { nc_msg "$PROGRAM Status: using adapter speed of $adap_speed\n"        }
if {$discovery && $adap_duplex != ""} { nc_msg "$PROGRAM Status: using adapter duplex of $adap_duplex\n"      }
if {$discovery && $server_ip != ""}   { nc_msg "$PROGRAM Status: using server IP address of $server_ip\n"     }
if {$discovery && $client_ip != ""}   { nc_msg "$PROGRAM Status: using client IP address of $client_ip\n"     }
if {$discovery && $gateway_ip != ""}  { nc_msg "$PROGRAM Status: using gateway IP address of $gateway_ip\n"   }
if {$discovery && $macaddress != ""}  { nc_msg "$PROGRAM Status: using macaddress of $macaddress\n"           }
if {$discovery && $phys_loc != ""}    { nc_msg "$PROGRAM Status: using physical location code of $phys_loc\n" }

nc_msg "node: $node\n"
nc_msg "name: $name\n"
set node $name


set NODENAME $node

ck_args

if {$extra_args != "" }  { nc_msg "$PROGRAM Status: extra arguments sent to booting: $extra_args\n" }

if { ! $noboot } {
    set hdw_addr ""
    set speed $adap_speed
    set duplex $adap_duplex
}

# Get userid and password
set userid $env(HCP_USERID) 
set passwd $env(HCP_PASSWD)

#
# Connect to the remote server
#
nc_msg "$PROGRAM Status: connecting to $hcp\n"
#connect
nc_msg "$PROGRAM Status: finished connecting to $hcp\n"

#
# open the S1 serial port
#
nc_msg "$PROGRAM Status: open port\n"

if [ catch { spawn $CONSOLEBIN $name -f } rsconole_pid ] {
    send_user "Unable to spawn console connection to $hcp\n"
    exit 1
}

set spawn_id_rconsole $spawn_id
nc_msg "$PROGRAM Status: spawn_id is $spawn_id\n"

#
# Signal handling stuff
#
trap {
    global PROGRAM
    set signal SIG[trap -name]
    send_user "$PROGRAM: Received signal named '$signal'\n"
    exec kill -KILL $spawn_id_rconsole $ssh_spawn_id
    exit -1 
} {INT HUP QUIT TERM}

set timeout 30
set pwd_sent 0
send_user "# Connecting to $name\n"
 
expect {
    -i $spawn_id_rconsole \
    -re "Enter.* for help.*" {
        send_user "# Connected\n"
    }
    timeout {
        send_user "$PROGRAM: Timeout waiting for console connection\n"
        exit 1
    }
    eof {
        send_user "$PROGRAM: Please make sure rcons $name works\n"
        exit 1
    }
}

send_user "# Checking for power off.\n"

run_lssyscfg

if { [string compare "$msg" "Not Available"] == 0  } {
        send_user "$PROGRAM: LPAR is Not Available. Please make sure the CEC's state\n"
        set rc 1
        exit 1
}

if { $from_of } {
    if { ([string compare "$msg" "open-firmware"] != 0) && ([string compare "$msg" "Open Firmware"] != 0) } {
        send_user "$PROGRAM: You used the -o option. Please make sure the LPAR's initial state is open firmware\n"
        set rc 1
        exit 1

    }
}

if { $from_of != 1 } {
if { ([string compare "$msg" "Off"] == 0) || ([string compare "$msg" "off"] == 0)  || ([string compare "$msg" "Not Activated"] == 0) } {
    send_user "# Power off complete.\n"
} else { 

    #
    # power off the node
    #
    send_user "# Power off the node.\n"
    set cmd "0"

    if { $immed_flag } {
        #set cmd "chsysstate -r lpar -o shutdown -m \"$manage\" --immed -n \"$node\""
        set cmd [list exec $BINPATH/rpower $node off]
    } else {
        #set cmd "chsysstate -r lpar -o shutdown -m \"$manage\" -n \"$node\""
        set cmd [list exec $BINPATH/rpower $node off]
    }
    set timeout 120
    set rc [catch $cmd msg]

    if { $rc } {
        send_user "$PROGRAM: Cannot power off $NODENAME\n"
        nc_msg "$PROGRAM Status: error from rpower command\n"
        send_user "$PROGRAM: Error : $msg\n"
        exit 1
    }

    send_user "# Wait for power off.\n"

    #   
    # chsysstate will return a successful result if it was able to pass the power
    # command from the IVM to the node, but the node may not be powered off yet.  Need
    # to query to make sure the node is off before we continue
    #   
    set done 0
    set query_count 0
    while { ! $done } {
        run_lssyscfg 
   
        #
        # separate the nodename from the query status
        #
        if { ([string compare "$msg" "Off"] == 0) ||  ([string compare "$msg" "off"] == 0)  || ([string compare "$msg" "Not Activated"] == 0) } {
            send_user "# Power off complete.\n"
            set done 1
            continue
        }

        incr query_count 
        if { $query_count > 300 } {
            send_user "$PROGRAM: Timed out waiting for power off of $NODENAME\n"
            nc_msg "$PROGRAM Status: error from rpower command : \"$msg\"\n"
            exit 1
        }

        sleep 1
    }
}



set done 0
set retry_count 0
if { $set_boot_order > 1 } {
    send_user "$PROGRAM Status: Power on $NODENAME to SMS.\n"
    set timeout 120
    while { ! $done } {
        set cmd [list exec $BINPATH/rpower $node sms]
        set rc [catch $cmd msg]
        nc_msg "$PROGRAM Status: wait for power on\n"
        
        if { $rc } { 
            if { $retry_count == 3 } {
                nc_msg "$PROGRAM Status: error from rpower command: $msg\n"
                send_user "$PROGRAM Status: error from rpower command: $msg\n"
                exit 1
            }
            sleep 1
            incr retry_count
        } else {
            set done 1
        }
        
    }
} else {
    send_user "# Power on $NODENAME to Open Firmware.\n"
    set timeout 120
    while { ! $done } {
        #send -i $ssh_spawn_id "chsysstate -r lpar -o on -b of -m \"$manage\" -n \"$node\" -f \"$profile\"; echo Rc=\$\?\r"
        set cmd [list exec $BINPATH/rpower $node of] 
        set rc [catch $cmd msg]
        nc_msg "$PROGRAM Status: wait for power on\n"
        
        if { $rc } { 
            if { $retry_count == 3 } {
                nc_msg "$PROGRAM Status: error from rpower command: $msg\n"
                send_user "$PROGRAM Status: error from rpower command: $msg\n"
                exit 1
            }
            sleep 1
            incr retry_count
       } else {
            set done 1
       }
        
    }
}

nc_msg "$PROGRAM Status: wait for power on\n"

if { $rc } {
    send_user "$PROGRAM: Cannot power on $NODENAME\n"
    nc_msg "$PROGRAM Status: error from rpower command\n"
    send_user "$PROGRAM: Error : $msg\n"
    exit 1
}

#
# Verify node is powered on before continuing
#
set done 0
set query_count 0
while { ! $done } {
    run_lssyscfg

    #
    # separate the nodename from the query status
    #
    if { ([string compare "$msg" "Open Firmware"] == 0) || ([string compare "$msg" "Open firmware"] == 0) || ([string compare "$msg" "open-firmware"] == 0)  } {
        send_user "# Power on complete.\n"
        set done 1
        continue
    }

    set timeout 1
    expect {
        -i $spawn_id_rconsole
        -re "(.*)elect this consol(.*)" {
            nc_msg "$PROGRAM Status: selecting active console\n"
            set command  "0"
            send_command
        }
    }

    incr query_count

    if { $query_count > 300 } {
        send_user "$PROGRAM: Timed out waiting for power on of $NODENAME\n"
        nc_msg "$PROGRAM Status: error from rpower command : \"$msg\" \n"
        exit 1
    }

    sleep 1
}
}
set retry 0
set done 0
set timeout 10
nc_msg "$PROGRAM Status: Check for active console.\n"
while { ! $done } {
    expect {
        -i $spawn_id_rconsole
        -re "ok(.*)0 >" {
            nc_msg "$PROGRAM Status: at ok prompt\n"
            set done 1
        }
        -re "(.*)elect this consol(.*)" {
            nc_msg "$PROGRAM Status: selecting active console\n"
            set command  "0"
            send_command
        }
        -re "English|French|German|Italian|Spanish|Portuguese|Chinese|Japanese|Korean" {
            nc_msg "$PROGRAM Status: Languagae Selection Panel received\n"
            set command  "2\r"
            send_command
        }
        -re "'admin'" {
            nc_msg "$PROGRAM Status: No password specified\n"
            send_user "$PROGRAM: No password specified\n"
            exit 1
        }
        -re "Invalid Password" {
            nc_msg "$PROGRAM Status:  FSP password is invalid.\n"
            send_user "$PROGRAM Status: FSP password is invalid.\n"
            exit 1
        }
        -re "SMS(.*)Navigation Keys" {
            nc_msg "$PROGRAM Status: SMS\n"
            set done 1
        }
        
        timeout {
            set command  "\r"
            send_command
            incr retry
            if { $retry == 9 } {
                send_user "$PROGRAM: Timeout waiting for ok prompt; exiting.\n"
                exit 1
            }
        }
        eof {
            send_user "$PROGRAM: Cannot connect to $NODENAME\n"
            exit 1
        }
    } 
}

if { $set_boot_order > 1 } {
    set_disk_boot
}

global phandle

set done 0
set retry 0
exec sleep 1
while { ! $done } {
    # 
    # Call get_phandle to gather information for all the supported network adapters
    # in the device tree.
    #
    get_phandle

    if { $rc } {
        incr retry 
        set command  "\r"
        send_command
        if { $retry == 3 } {
            send_user "$PROGRAM: Unable to obtain network adapter information.  Quitting.\n"
            exit 1
        }
    } else {
        set done 1 
    }
}

# Call multiple_open-dev to circumvent firmware OPEN-DEV failure
multiple_open-dev
if { $rc } {
    nc_msg "Unable to create multiple_open-dev.\n"
    send_user "$PROGRAM: Unable to obtain network adapter information.  Quitting.\n"
    exit 1
}

if { $discovery } {
    send_user "# Client IP address is $client_ip\n"
    send_user "# Server IP address is $server_ip\n"
    send_user "# Gateway IP address is $gateway_ip\n"
}

if { $noboot } { # Display information for all supported adapters

    if {[info exists list_type]} {
        set match_pat $list_type
    } else {
        set match_pat ".*"  ;# match anything
    }

    if { $colon } {
        send_user "#Type:Location_Code:MAC_Address:Full_Path_Name:Ping_Result:Device_Type:Size_MB:OS:OS_Version:\n"
    } else {
        send_user "# Type \tLocation Code \tMAC Address\t Full Path Name\t Ping Result\n"
    }


    if { $discover_all } { 
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
            if {[regexp $match_pat $adap_type($i)] != 0 } {
                if { $adap_type($i) != "hfi-ent" } {
                    set mac_address [get_mac_addr $phandle_array($i)]
                    set loc_code [get_adaptr_loc $phandle_array($i)]
                }

                set ping_result ""
                if { $discovery } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    nc_msg "$PROGRAM Status: ping_server returns $ping_rc\n"
                    if { $ping_rc != 0 } {
                        set ping_result "unsuccessful"
		    } else {
			set ping_result "successful"
                    }
                }

                if { $adap_type($i) == "hfi-ent" } {
                    set mac_address [get_mac_addr $phandle_array($i)]
                    set loc_code [get_adaptr_loc $phandle_array($i)]
                }
		
                if { [string first "vdevice" "$full_path_name_array($i)"] != -1 } {
                    set device_type "virtual"
                } else {
                    set device_type "physical"
                }

                if { $colon } {
                    send_user "$adap_type($i)\:$loc_code\:$mac_address\:$full_path_name_array($i)\:$ping_result\:$device_type\:\:\:\:\n"
                } else {
                    send_user "$adap_type($i) $loc_code $mac_address $full_path_name_array($i) $ping_result $device_type\n"
                }
            }
        }

    } else {
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
	    if {[regexp $match_pat $adap_type($i)] != 0 } {
                if { $adap_type($i) == "hfi-ent" } {
                    set mac_address [get_mac_addr $phandle_array($i)]
                    set loc_code [get_adaptr_loc $phandle_array($i)]
                }
                set ping_result "" 
                if { $discovery } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    nc_msg "$PROGRAM Status: ping_server returns $ping_rc\n"
                    if { $ping_rc != 0 } {
                        set ping_result "unsuccessful"
		    } else {
			set ping_result "successful"
                    }
                }
                if { $adap_type($i) == "hfi-ent" } {
                    set mac_address [get_mac_addr $phandle_array($i)]
                    set loc_code [get_adaptr_loc $phandle_array($i)]
                }
                if { [string first "vdevice" "$full_path_name_array($i)"] != -1 } {
                    set device_type "virtual"
                } else {
                    set device_type "physical"
                }

		if { $colon } {
                    send_user "$adap_type($i)\:$loc_code\:$mac_address\:$full_path_name_array($i)\:$ping_result\:$device_type\:\:\:\:\n"
	        } else {
                    send_user "$adap_type($i) $loc_code $mac_address $full_path_name_array($i) $ping_result $device_type\n"
		}
                break;
            }
        }
    }

    if { $from_of != 1 } {  
    
      nc_msg "$PROGRAM Status: power off the node after noboot == 1\n"
      set cmd "0"

      if { $immed_flag } {
          #set cmd "chsysstate -r lpar -o shutdown -m \"$manage\" --immed -n \"$node\""
          set cmd [list exec $BINPATH/rpower $node off]
       } else {
           #set cmd "chsysstate -r lpar -o shutdown -m \"$manage\" -n \"$node\""
           set cmd [list exec $BINPATH/rpower $node off]
       }
       set timeout 120
       set rc [catch $cmd msg]
       
       if { $rc } {
          send_user "$PROGRAM: Cannot power off $NODENAME\n"
          nc_msg "$PROGRAM Status: error from rpower command\n"
          send_user "$PROGRAM: Error : $msg\n"
          exit 1
       }
    }
} else {  # Do a network boot

    # Loop throught the adapters and perform a ping test to discover an
    # adapter that pings successfully, then use that adapter to network boot.
    if { $discover_all == 1 } {
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
            set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]

            # if ping was successful, then use that adapter for lpar_netboot
            if { $ping_rc == 0 } {
                set phandle $phandle_array($i)
                set full_path_name $full_path_name_array($i)
                set chosen_adap_type $adap_type($i)

                break
            } 
        }
    } elseif { $macaddress != "" } {
        set match 0
	for {set i 1} {$i <= $adapter_found} {incr i 1} {
            if {[regexp "hfi-ent" $adap_type($i)] } {
                set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
            }
            set mac_address [get_mac_addr $phandle_array($i)]
            if { [string first "$mac_address" "$macaddress"] != -1 } {
                if { $discovery == 1 } {
                    if { $adap_type($i) != "hfi-ent" } {
                        set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    }
                    if { $ping_rc != 0 } {
                        send_user "$PROGRAM: Unable to boot network adapter.\n"
                        exit 1
                    }
                }
                set phandle $phandle_array($i)
                set full_path_name $full_path_name_array($i)
                set chosen_adap_type $adap_type($i)
                set match 1
                break
            }
        }
        if { !$match } {
            send_user "$PROGRAM: Can not find mac address '$macaddress'\n"
            exit 1
        }
    } elseif { $phys_loc != "" } {
        set match 0
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
            set loc_code [get_adaptr_loc $phandle_array($i)]
            if { [string first "$phys_loc" "$loc_code"] != -1 } {
                if { $discovery == 1 } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    if { $ping_rc != 0 } {
                        send_user "$PROGRAM: Unable to boot network adapter.\n"
                        exit 1
                    }
                }
                set phandle $phandle_array($i)
                set full_path_name $full_path_name_array($i)
                set chosen_adap_type $adap_type($i)
                set match 1
                break
            }
        }
        if { !$match } {
            send_user "$PROGRAM: Can not find physical location '$phys_loc'\n"
            exit 1
	}
    } else {
        #
        # Use the first ethernet adapter in the
        # device tree.
        #
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
            if { $adap_type($i) == $list_type } {
                if { $discovery == 1 } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    if { $ping_rc != 0 } {
                        exit 1
                    }
                }
                set phandle $phandle_array($i)
                set full_path_name $full_path_name_array($i)
                set chosen_adap_type $adap_type($i)
                break
            }
        }
    }

    if { $full_path_name == "" } {
        send_user "$PROGRAM: Unable to boot network adapter.\n"
        exit 1
    } else {
        send_user "# Network booting install adapter.\n"
        boot_network
    }

    if { $rc == 0 } {
        send_user "# bootp sent over network.\n"

        Boot
    } else {
        nc_msg "return code $rc from boot_network\n"
    }

    if { $rc == 5} {
        # Need to retry network boot because of intermittant network failure 
        # after partition reboot.  Force partition to stop at Open Firmware prompt.
        expect {
            -i $spawn_id_rconsole
            -re "keyboard" {
                set command "8"
                send_command
                sleep 10
            }
            timeout {
                send_user "$PROGRAM: Timeout; exiting\n"
                exit 1
            }
            eof {
                send_user "$PROGRAM: cannot connect to $NODENAME"
                exit 1
            }
	    send_user "# Network booting install adapter.\n"
	    nc_msg "Retrying network-boot from RESTART-CMD error.\n"
            set done 0
            while { ! $done } {
                boot_network
                if { $rc == 0 } {
                    set done 1
                } else {
                    # try again in 10 seconds
                    sleep 10
                }
            }
        }
        send_user "# bootp sent over network.\n"
        Boot
    }
}

#
# mission accomplished, beam me up scotty.
#
if { !$noboot  } { 
    if { $rc == 0 } {
        send_user "# Finished.\n" 
    } else {
        send_user "# Finished in an error.\n"
    }
} else {
    set done 0
    set query_count 0
    while { ! $done } {
        run_lssyscfg
                                                                     
        #                                                            
        # separate the nodename from the query status                
        #                 
        if { $from_of != 1 } {                                                   
        if { ([string compare "$msg" "Off"] == 0) || ([string compare "$msg" "off"] == 0)  || ([string compare "$msg" "Not Activated"] == 0) } {
            set done 1                                               
        }                     
        } else { 
            if { ([string compare "$msg" "open-firmware"] == 0) || ([string compare "$msg" "Open Firmware"] == 0)  } {
                set done 1
            }
        }                                       

        incr query_count                                             
        if { $query_count > 60 } {                                  
            set done 1                                                   
        }                                                            
                                                                     
        sleep 1                                                      
    }                                                                
}

set command "~."
send_command

exit $rc
