From 06bb01609efa7861d8a82bc02545222f96869575 Mon Sep 17 00:00:00 2001 From: jostreff Date: Mon, 7 Apr 2025 18:02:43 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D1=8F=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=BD=D0=B0=20srllogin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srllogin | 905 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 905 insertions(+) create mode 100644 srllogin diff --git a/srllogin b/srllogin new file mode 100644 index 0000000..1764b5c --- /dev/null +++ b/srllogin @@ -0,0 +1,905 @@ +#! /usr/local/bin/expect -- +## +## rancid 3.11 +## Copyright (c) 2023 by Nick Peelman +## All rights reserved. +## +## This code is derived from software contributed to and maintained by +## Henry Kilmer, John Heasley, Andrew Partan, +## Pete Whiting, Austin Schutz, and Andrew Fort. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## 3. Neither the name of RANCID nor the names of its +## contributors may be used to endorse or promote products derived from +## this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY Henry Kilmer, John Heasley AND CONTRIBUTORS +## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS +## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +## +## It is the request of the authors, but not a condition of license, that +## parties packaging or redistributing RANCID NOT distribute altered versions +## of the etc/rancid.types.base file nor alter how this file is processed nor +## when in relation to etc/rancid.types.conf. The goal of this is to help +## suppress our support costs. If it becomes a problem, this could become a +## condition of license. +# +# The expect login scripts were based on Erik Sherk's gwtn, by permission. +# +# The original looking glass software was written by Ed Kern, provided by +# permission and modified beyond recognition. +# +# noklogin - Nokia (Alcatel-Lucent) SR OS login +# +# Most options are intuitive for logging into a Cisco router. +# The default username password is the same as the vty password. +# + +# Sometimes routers take awhile to answer (the default is 10 sec) +set timeoutdflt 45 +# Some CLIs having problems if we write too fast (Extreme, PIX, Cat) +set send_human {.2 .1 .4 .2 1} + +# Usage line +set usage "Usage: $argv0 \[-dhiSV\] \[-m|M\] \[-autoenable\] \[-noenable\] \ +\[-c command\] \[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \ +\[-p user-password\] \[-r passphrase\] \[-s script-file\] \[-t timeout\] \ +\[-u username\] \[-v vty-password\] \[-w enable-username\] \[-x command-file\] \ +\[-y ssh_cypher_type\] router \[router...\]\n" + +# Password file +set password_file $env(HOME)/.cloginrc +# Default is to login to the router +set do_command 0 +set do_interact 0 +set do_script 0 +# The default is to automatically enable +if {! [info exists avenable]} { + set avenable 0 +} +# The default is that you login non-enabled (tacacs can have you login already +# enabled) +set avautoenable 0 +# The default is to look in the password file to find the passwords. This +# tracks if we receive them on the command line. +set do_passwd 1 +set do_enapasswd 1 +# Save config, if prompted +set do_saveconfig 0 +# cloginrc debugging knob +set do_cloginrcdbg 0 +# Sometimes routers take awhile to answer (the default is 10 sec) +if {! [info exists timeoutdflt]} { + set timeoutdflt 10 +} +# spawn tty options +set spawnopts {} +# intialize cloginrc parsing stacks +set int_file {} +set int_lineno {} + +# env(CLOGIN) may contain: +# x == do not set xterm banner or name + +# Find the user in the ENV, or use the unix userid. +if {![info exists default_user] && [info exists env(CISCO_USER)]} { + if {[string length $env(CISCO_USER)]} { + set default_user $env(CISCO_USER) + } +} +if {![info exists default_user] && [info exists env(USER)]} { + if {[string length $env(USER)]} { + set default_user $env(USER) + } +} +if {![info exists default_user] && [info exists env(LOGNAME)]} { + if {[string length $env(LOGNAME)]} { + set default_user $env(LOGNAME) + } +} +if (![info exists default_user]) { + # This uses "id" which I think is portable. At least it has existed + # (without options) on all machines/OSes I've been on recently - + # unlike whoami or id -nu. + if [catch {exec id} reason] { + send_error "\nError: could not exec id: $reason\n" + exit 1 + } + regexp {\(([^)]*)} "$reason" junk default_user +} +if {[info exists env(CLOGINRC)]} { + set password_file $env(CLOGINRC) +} + +# Process the command line +for {set i 0} {$i < $argc} {incr i} { + set arg [lindex $argv $i] + + switch -glob -- $arg { + # Expect debug mode + -d* { + exp_internal 1 + # Help + } -h* { + send_user "$usage" + exit 0 + # Command to run. + } -c* { + if {! [regexp .\[cC\](.+) $arg ignore command]} { + incr i + set command [lindex $argv $i] + } + set do_command 1 + # Enable Password + } -e* { + if {! [regexp .\[e\](.+) $arg ignore enapasswd]} { + incr i + set enapasswd [lindex $argv $i] + } + set do_enapasswd 0 + # Environment variable to pass to -s scripts + } -E* { + if {[regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} { + set E$varname $varvalue + } else { + send_user "\nError: invalid format for -E in $arg\n" + exit 1 + } + # alternate cloginrc file + } -f* { + if {! [regexp .\[fF\](.+) $arg ignore password_file]} { + incr i + set password_file [lindex $argv $i] + } + # interactive + } -i* { + set do_interact 1 + # user Password + } -p* { + if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} { + incr i + set userpasswd [lindex $argv $i] + } + set do_passwd 0 + # cloginrc debugging knobs + } -m* { + set do_cloginrcdbg 1 + } -M* { + set do_cloginrcdbg 2 + # ssh passphrase + } -r* { + if {! [regexp .\[rR\](.+) $arg ignore passphrase]} { + incr i + set avpassphrase [lindex $argv $i] + } + # Expect script to run. + } -s* { + if {! [regexp .\[sS\](.+) $arg ignore sfile]} { + incr i + set sfile [lindex $argv $i] + } + if {! [file readable $sfile]} { + send_user "\nError: Can't read $sfile\n" + exit 1 + } + set do_script 1 + # save config on exit + } -S* { + # may not be supported by the script and may not be applicable to + # the platform + set do_saveconfig 1 + # Timeout + } -t* { + if {! [regexp .\[tT\](.+) $arg ignore timeout]} { + incr i + set timeoutdflt [lindex $argv $i] + } + # Username + } -u* { + if {! [regexp .\[uU\](.+) $arg ignore user]} { + incr i + set username [lindex $argv $i] + } + # VTY Password + } -v* { + # some scripts ignore -v, like jlogin + if {! [regexp .\[vV\](.+) $arg ignore passwd]} { + incr i + set passwd [lindex $argv $i] + } + set do_passwd 0 + # Version string + } -V* { + send_user "rancid 3.11\n" + exit 0 + # Enable Username + } -w* { + # may not be supported by the script and may not be applicable to + # the platform + if {! [regexp .\[wW\](.+) $arg ignore enauser]} { + incr i + set enausername [lindex $argv $i] + } + # Command file + } -x* { + if {! [regexp .\[xX\](.+) $arg ignore cmd_file]} { + incr i + set cmd_file [lindex $argv $i] + } + if [catch {set cmd_fd [open $cmd_file r]} reason] { + send_user "\nError: $reason\n" + exit 1 + } + set cmd_text [read $cmd_fd] + close $cmd_fd + set command [join [split $cmd_text \n] \;] + set do_command 1 + # 'ssh -c' cypher type + } -y* { + if {! [regexp .\[eE\](.+) $arg ignore cypher]} { + incr i + set cypher [lindex $argv $i] + } + # Do we enable? + } -noenable { + set avenable 0 + # Does tacacs automatically enable us? + } -autoenable { + set avautoenable 1 + set avenable 0 + } -* { + send_user "\nError: Unknown argument! $arg\n" + send_user $usage + exit 1 + } default { + break + } + } +} +# Process routers...no routers listed is an error. +if {$i == $argc} { + send_user "\nError: $usage" +} + +# Only be quiet if we are running a script (it can log its output +# on its own) +if {$do_script} { + log_user 0 +} else { + log_user 1 +} + +# +# Done configuration/variable setting. Now run with it... +# + +# Sets Xterm title if interactive...if its an xterm and the user cares +proc label {host} { + global env + # if CLOGIN has an 'x' in it, don't set the xterm name/banner + if [info exists env(CLOGIN)] { + if {[string first "x" $env(CLOGIN)] != -1} { return } + } + # take host from ENV(TERM) + if [info exists env(TERM)] { + if [regexp \^(xterm|vs) $env(TERM) ignore] { + send_user "\033]1;[lindex [split $host "."] 0]\a" + send_user "\033]2;$host\a" + } + } +} + +# This is a helper function to make the password file easier to +# maintain. Using this the password file has the form: +# add password sl* pete cow +# add password at* steve +# add password * hanky-pie +proc add {var args} { + global int_file int_lineno int_$var + set file [lindex $int_file 0] + set lineno [lindex $int_lineno 0] + lappend int_$var "$var:$file:$lineno: $args" +} +proc include {args} { + global env + regsub -all "(^{|}$)" $args {} args + if {[regexp "^/" $args ignore] == 0} { + set args $env(HOME)/$args + } + source_password_file $args +} + +proc find {var router} { + global do_cloginrcdbg + upvar int_$var list + if {[info exists list]} { + foreach line $list { + if {[string match -nocase [lindex $line 1] $router]} { + if {$do_cloginrcdbg > 0} { + send_error -- [join [list [lindex $line 0] [lrange $line 1 end] "\r\n"]] + } + if {$do_cloginrcdbg == 2} { + # save return value + if {! [info exists result]} { + set result [lrange $line 2 end] + } + } else { + return [lrange $line 2 end] + } + } + } + } + + if {$do_cloginrcdbg == 2} { + if {[info exists result]} { + return $result + } + } + return {} +} + +# Loads the password file. Note that as this file is tcl, and that +# it is sourced, the user better know what to put in there, as it +# could install more than just password info... I will assume however, +# that a "bad guy" could just as easy put such code in the clogin +# script, so I will leave .cloginrc as just an extention of that script +proc source_password_file {file} { + global env int_file int_lineno + if {! [file exists $file]} { + send_user "\nError: password file ($file) does not exist\n" + exit 1 + } + file stat $file fileinfo + if {[expr ($fileinfo(mode) & 007)] != 0000} { + send_user "\nError: $file must not be world readable/writable\n" + exit 1 + } + if [catch {set fd [open $file "r"]} reason] { + send_user "\nError: $reason\n" + exit 1 + } + set int_file [linsert $int_file 0 $file] + set int_lineno [linsert $int_lineno 0 0] + while {[gets $fd line] >= 0} { + set tmp [lindex $int_lineno 0]; incr tmp + lset int_lineno 0 $tmp + eval $line + } + set int_file [lrange $int_file 1 end] + set int_lineno [lrange $int_lineno 1 end] + close $fd +} + +# Log into the router. +# returns: 0 on success, 1 on failure, -1 if rsh was used successfully +proc login { router user userpswd passwd cmethod cyphertype identfile } { + global command spawn_id in_proc do_command do_script passphrase + global prompt prompt_match u_prompt p_prompt e_prompt sshcmd telnetcmd + global spawnopts + set in_proc 1 + set uprompt_seen 0 + + # try each of the connection methods in $cmethod until one is successful + set progs [llength $cmethod] + foreach prog [lrange $cmethod 0 end] { + incr progs -1 + if [string match "telnet*" $prog] { + regexp {telnet(:([^[:space:]]+))*} $prog methcmd suffix port + if {"$port" == ""} { + set retval [catch {eval spawn $spawnopts [split "$telnetcmd $router"]} reason] + } else { + set retval [catch {eval spawn $spawnopts [split "$telnetcmd $router $port"]} reason] + } + if { $retval } { + send_user "\nError: telnet failed: $reason\n" + return 1 + } + } elseif [string match "ssh*" $prog] { + # ssh to the router & try to login with or without an identfile. + regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port + set cmd $sshcmd + if {"$port" != ""} { + set cmd "$cmd -p $port" + } + if {"$cyphertype" != ""} { + set cmd "$cmd -c $cyphertype" + } + if {"$identfile" != ""} { + set cmd "$cmd -i $identfile" + } + set retval [catch {eval spawn $spawnopts [split "$cmd -x -l $user $router" { }]} reason] + if { $retval } { + send_user "\nError: $cmd failed: $reason\n" + return 1 + } + } elseif ![string compare $prog "rsh"] { + if { ! $do_command } { + if { [llength $cmethod] == 1 } { + send_user "\nError: rsh is an invalid method for -x and " + send_user "interactive logins\n" + } + if { $progs == 0 } { + return 1 + } + continue; + } + + # handle escaped ;s in commands, and ;; and ^; + regsub -all {([^\\]);} $command "\\1\u0002;" esccommand + regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command + regsub {^;} $command "\u0002;" esccommand + regsub -all {[\\];} $esccommand ";" command + regsub -all {\u0002;} $command "\u0002" esccommand + set sep "\u0002" + set commands [split $esccommand $sep] + set num_commands [llength $commands] + set rshfail 0 + for {set i 0} {$i < $num_commands && !$rshfail} { incr i} { + log_user 0 + set retval [catch {spawn rsh $user@$router [lindex $commands $i] } reason] + if { $retval } { + send_user "\nError: rsh failed: $reason\n" + log_user 1; return 1 + } + send_user "$router# [lindex $commands $i]\n" + + # rcmd does not get a pager and no prompts, so we just have to + # look for failures & lines. + expect { + "Connection refused" { catch {close}; catch {wait}; + send_user "\nError: Connection\ + Refused ($prog): $router\n" + set rshfail 1 + } + -re "(Connection closed by|Connection to \[^\n\r]+ closed)" { + catch {close}; catch {wait}; + send_user "\nError: Connection\ + closed ($prog): $router\n" + set rshfail 1 + } + "Host is unreachable" { catch {close}; catch {wait}; + send_user "\nError: Host Unreachable:\ + $router\n" + set rshfail 1 + } + "No address associated with" { + catch {close}; catch {wait}; + send_user "\nError: Unknown host\ + $router\n" + set rshfail 1 + } + -re "\b+" { exp_continue } + -re "\[\n\r]+" { send_user -- "$expect_out(buffer)" + exp_continue + } + timeout { catch {close}; catch {wait}; + send_user "\nError: TIMEOUT reached\n" + set rshfail 1 + } + eof { catch {close}; catch {wait}; } + } + log_user 1 + } + if { $rshfail } { + if { !$progs } { + return 1 + } else { + continue + } + } + # fake the end of the session for rancid. + send_user "$router# quit\n" + # return rsh "success" + return -1 + } else { + send_user "\nError: unknown connection method: $prog\n" + return 1 + } + sleep 0.3 + + # This helps cleanup each expect clause. + expect_after { + timeout { + global in_proc + send_user "\nError: TIMEOUT reached\n" + catch {close}; catch {wait}; + if {$in_proc} { + return 1 + } else { + continue + } + } eof { + global in_proc + send_user "\nError: EOF received\n" + catch {close}; catch {wait}; + if {$in_proc} { + return 1 + } else { + continue + } + } + } + + # Here we get a little tricky. There are several possibilities: + # the router can ask for a username and passwd and then + # talk to the TACACS server to authenticate you, or if the + # TACACS server is not working, then it will use the enable + # passwd. Or, the router might not have TACACS turned on, + # then it will just send the passwd. + # if telnet fails with connection refused, try ssh + expect { + -re "(Connection refused|Secure connection \[^\n\r]+ refused)" { + catch {close}; catch {wait}; + if !$progs { + send_user "\nError: Connection Refused ($prog): $router\n" + return 1 + } + } + -re "(Connection closed by|Connection to \[^\n\r]+ closed)" { + catch {close}; catch {wait}; + if !$progs { + send_user "\nError: Connection closed ($prog): $router\n" + return 1 + } + } + eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 } + -nocase "unknown host\r" { + send_user "\nError: Unknown host $router\n"; + catch {close}; catch {wait}; + return 1 + } + "Host is unreachable" { + send_user "\nError: Host Unreachable: $router\n"; + catch {close}; catch {wait}; + return 1 + } + "No address associated with name" { + send_user "\nError: Unknown host $router\n"; + catch {close}; catch {wait}; + return 1 + } + -re "(Host key not found |The authenticity of host .* be established)" { + expect { + -re "\\(yes\/no\[^\\)]*\\)\\?" { + send "yes\r"; + send_user "\nHost $router added to the list of known hosts.\n" + } + -re "\[^\r\n]*\[\r\n]+" { exp_continue; } + } + exp_continue + } + -re "HOST IDENTIFICATION HAS CHANGED" { + send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" + expect { + -re "\\(yes\/no\\)\\?" { send "no\r" } + -re " strict checking\.\[\r\n]+" { } + -re "\[^\r\n]*\[\r\n]+" { exp_continue; } + } + catch {close}; catch {wait}; + return 1 + } + -re "Offending key for " { + send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n" + expect { + -re "\\(yes\/no\\)\\?" { send "no\r" } + -re "\[^\r\n]*\[\r\n]+" { exp_continue; } + } + catch {close}; catch {wait}; + return 1 + } + -nocase -re "^warning: remote host denied authentication agent forwarding." { + exp_continue; + } + -re "(denied|Sorry)" { + send_user "\nError: Check your passwd for $router\n" + catch {close}; catch {wait}; return 1 + } + "Login failed" { + send_user "\nError: Check your passwd for $router\n" + catch {close}; catch {wait}; return 1 + } + -re "% (Bad passwords|Authentication failed)" { + send_user "\nError: Check your passwd for $router\n" + catch {close}; catch {wait}; return 1 + } + -re "Last login:" { + exp_continue + } + -re "Press the key \[^\r\n]+\[\r\n]+" { + exp_continue + } + -re "@\[^\r\n]+ $p_prompt" { + # ssh pwd prompt + sleep 1 + send -- "$userpswd\r" + exp_continue + } + -re "Enter passphrase.*: " { + # sleep briefly to allow time for stty -echo + sleep .3 + send -- "$passphrase\r" + exp_continue + } + -re "$u_prompt" { + send -- "$user\r" + set uprompt_seen 1 + exp_continue + } + -re "$p_prompt" { + sleep 1 + if {$uprompt_seen == 1} { + send -- "$userpswd\r" + } else { + send -- "$passwd\r" + } + exp_continue + } + -re "$prompt" { + set prompt_match $expect_out(0,string); + break; + } + "Login invalid" { + send_user "\nError: Invalid login: $router\n"; + catch {close}; catch {wait}; return 1 + } + -re "\[^\r\n]*\[\r\n]+" { exp_continue; } + } + } + + set in_proc 0 + return 0 +} + +# Run commands given on the command line. +proc run_commands { prompt command } { + global do_interact do_saveconfig in_proc + set in_proc 1 + + # match sros config mode prompts too, such as A:name>mode>mode# + # or *A:name>...#. + regsub -all -- {#$} $prompt {[^\r\n# ]*#} reprompt + + # this is the only way i see to get rid of more prompts in o/p..grrrrr + log_user 0 + + # handle escaped ;s in commands, and ;; and ^; + regsub -all {([^\\]);} $command "\\1\u0002;" esccommand + regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command + regsub {^;} $command "\u0002;" esccommand + regsub -all {[\\];} $esccommand ";" command + regsub -all {\u0002;} $command "\u0002" esccommand + set sep "\u0002" + set commands [split $esccommand $sep] + set num_commands [llength $commands] + for {set i 0} {$i < $num_commands} { incr i} { + send -- "[subst -nocommands [lindex $commands $i]]\r" + expect { + -re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)" + } + -re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)" + } + -re "\[^\r\n]*\[\n\r]+" { send_user -- "$expect_out(buffer)" + exp_continue + } + -re "\[^\r\n]*Press any key to continue \\\(Q to quit\\\)" { + send " " + # bloody ^[[2K after " " + expect { + -re "^\[^\r\n]*\r" {} + } + exp_continue + } + } + } + log_user 1 + + if { $do_interact == 1 } { + interact + return 0 + } + + send -h "quit\r" + expect { + -re "Connection to (.*) closed." { return 0 } + -re "\[\n\r]+" { exp_continue } + timeout { catch {close}; catch {wait}; + return 1 + } + eof { return 0 } + } + set in_proc 0 +} + +# +# For each router... (this is main loop) +# +source_password_file $password_file +set in_proc 0 +set exitval 0 +set prompt_match "" +# if we have dont have a tty, we need some additional terminal settings +if [catch {open /dev/tty w} ttyid] { + # no tty, ie: cron + set spawnopts "-nottycopy" + set stty_init "cols 132" +} else { + catch {close ttyid} reason +} +foreach router [lrange $argv $i end] { + set router [string tolower $router] + send_user -- "$router\n" + + # device timeout + set timeout [find timeout $router] + if { [llength $timeout] == 0 } { + set timeout $timeoutdflt + } + + # Default prompt. + set prompt [join [find prompt $router] ""] + if { [llength $prompt] == 0 } { + set prompt "#" + } + + # Figure out passwords + if { $do_passwd } { + set pswd [find password $router] + if { [llength $pswd] == 0 } { + send_user -- "\nError: no password for $router in $password_file.\n" + continue + } + set passwd [join [lindex $pswd 0] ""] + } else { + set passwd $userpasswd + } + + # Figure out username + if {[info exists username]} { + # command line username + set ruser $username + } else { + set ruser [join [find user $router] ""] + if { "$ruser" == "" } { set ruser $default_user } + } + + # Figure out username's password (if different from the vty password) + if {[info exists userpasswd]} { + # command line username + set userpswd $userpasswd + } else { + set userpswd [join [find userpassword $router] ""] + if { "$userpswd" == "" } { set userpswd $passwd } + } + + # Figure out prompts + set u_prompt [find userprompt $router] + if { "$u_prompt" == "" } { + set u_prompt "(\[Uu]sername|Login|login|user name|User):" + } else { + set u_prompt [join [lindex $u_prompt 0] ""] + } + set p_prompt [find passprompt $router] + if { "$p_prompt" == "" } { + set p_prompt "(\[Pp]assword|passwd|Enter password for \[^ :]+):" + } else { + set p_prompt [join [lindex $p_prompt 0] ""] + } + + # Figure out identity file to use + set identfile [join [lindex [find identity $router] 0] ""] + + # Figure out passphrase to use + if {[info exists avpassphrase]} { + set passphrase $avpassphrase + } else { + set passphrase [join [lindex [find passphrase $router] 0] ""] + } + if { ! [string length "$passphrase"]} { + set passphrase $passwd + } + + # Figure out cypher type + if {[info exists cypher]} { + # command line cypher type + set cyphertype $cypher + } else { + set cyphertype [find cyphertype $router] + } + + # Figure out connection method + set cmethod [find method $router] + if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} } + + # Figure out the SSH executable name + set sshcmd [join [lindex [find sshcmd $router] 0] ""] + if { "$sshcmd" == "" } { set sshcmd {ssh} } + + # Figure out the telnet executable name + set telnetcmd [join [lindex [find telnetcmd $router] 0] ""] + if { "$telnetcmd" == "" } { set telnetcmd "telnet -K" } + + # if [-mM], skip do not login + if { $do_cloginrcdbg > 0 } { continue; } + + # Login to the router + if {[login $router $ruser $userpswd $passwd $cmethod $cyphertype $identfile]} { + incr exitval + # if login failed or rsh was unsuccessful, move on to the next device + continue + } + # we are logged in, now figure out the full prompt: + # cli prompt: + # A:some.domain.name# configure + # cli prompt when config is unsaved: + # *A:some.domain.name# configure + # config prompt: + # A:some.domain.name>config# vrrp + # *A:some.domain.name>config>vrrp# + send "\r" + regsub -all {^(\^*)(.*)} $prompt {\2} reprompt + regsub -all {\e\[\??\w+} $prompt {\2} reprompt + expect { + -re "\[\r\n]+" { exp_continue; } + -re "^.+$reprompt" { set junk $expect_out(0,string); + regsub -all "^\\\*" $junk {} junk + regsub -all "\[\]\[\(\)+]" $junk {\\&} prompt; + set prompt "\[*]?$prompt"; + } + } + + if { $do_command || $do_script } { + # disable the fancy prompt + send "environment cli-engine type basic\r" + expect { + -re "MINOR: CLI Command not allowed" { + send_error "Error: environment no more command is not allowed\n" + incr exitval + continue + } + -re $prompt {} + } + send "environment save home\r" + expect { + -re "MINOR: CLI Command not allowed" { + send_error "Error: //environment no more command is not allowed\n" + incr exitval + continue + } + -re $prompt {} + } + } + + if { $do_command } { + if {[run_commands $prompt $command]} { + incr exitval + continue + } + } elseif { $do_script } { + source $sfile + catch {close}; + } else { + label $router + log_user 1 + interact + } + + # End of for each router + catch {wait}; + sleep 0.3 +} +exit $exitval \ No newline at end of file