issues with spawn id <idx> not open when executing script from iTerm2 - bash

I am trying to write a script and execute under iTerm2 profile. However whenever I open the new tab i run into this error. but I execute the same script on Terminal and no problem for it. anyone may know why?
expect: spawn id exp6 not open
`while { 1 } {
(file "xxxxx" line xx)
....
#!/usr/bin/expect
set username [lindex $argv 1]
set hostname [lindex $argv 2]
set password "abcd"
set timeout 60
# trap SIGWINCH and pass to spawned process
trap {
set rows [stty rows]
set cols [stty columns]
stty rows $rows columns $cols < $spawn_out(slave,name)
} WINCH
spawn ssh -o NoHostAuthenticationForLocalhost=yes -o UseKeychain=yes $username#$hostname
while { 1 } {
expect "Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
} "Enter passphrase for key " {
interact
break
} "password:" {
send "$password\r"
}
}

To expect several things simultaneously, don't use while use exp_continue
expect {
"Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
exp_continue
}
"Enter passphrase for key " {
interact
}
"password:" {
send "$password\r"
interact
}
}
If you get the connection prompt, send yes and loop back to the expect statement.
If you get prompted for the passphrase, just drop into interactive mode.
If it's the password prompt, send the password prompt and go interactive.
When the user disconnects, interact will return, the expect statement returns, and the script exits.

Related

How does Expect script determine contents of expect_out

I am trying to write a script will provide credentials to a vpn with one caveat where VPN requires that I provide OTP.
This is my script:
#! /usr/bin/expect
set vpn [ lindex $argv 0]
set group [ lindex $argv 1]
set user [ lindex $argv 2]
set secret [ lindex $argv 3]
log_user 2
spawn nmcli con up $vpn --ask
expect {
"GROUP:" {
send "$group\r"
exp_continue
}
"Username:" {
send "$user\r"
exp_continue
}
Password: {
send_user "\nProvide RSA OTP:"
expect_user -re ":(.*)\r"
set otp $expect_out(0,string) <--------- Error!!!
set pass "$secret$otp"
send "$pass\r"
exp_continue
}
"Connection successfully activated" {
send_user "Connected to VPN\r"
}
"Login failed" {
send_user "Login failed\r"
exp_continue
}
"already active" {
send_user "Already connected to VPN\r"
}
}
exit
I included an arrow to what I think is the source of the problem because when I run this script, it looks like expect_out contains Password:. Given what i read in man page I imagined that the buffer is cleared each time new match is made and therefore it contains string matched in most recent expect clause( in this case expect_user -re ":(.*)\r"), however it seems that I'm wrong. I did not see any notes saying that expect_user contents need to be accessed using different function like interact does, so I'm not sure where did my password go?
Thank you
When using send_user you will not see an echo of what you write, unlike when you send to the spawned command, so you will never match : unless the user explicitly types it. Also, you will read newline, not carriage-return. So use
expect_user -re "(.*)\n"
to get the user input and $expect_out(1,string) (1 not 0) will contain the input.
What you are observing is a timeout in your expect_user, which is why the variable is not updated and still contains the old match.
To protect yourself against unseen timeouts, you could set up a "global" timeout check by starting with
proc abort { } { send_user "Timeout!" ; exit 2 }
expect_before timeout abort
The expect_before is done "before" each expect, but also before each expect_user, it seems.

Non-reliable results from Expect when running a remote script

Because my company insists on using sudo I now have to scrap my ssh system I've built for running remote commands.
I have a script that will connect to a server, sudo into the db2 inst owner account and then run a script (previously delivered into /tmp). Maybe 1 in 10 times it will work (outputs 'Hello world' for my test).
Here is the ksh script on the remote server (/tmp/cwow/generic.ksh):
#!/usr/bin/ksh93
echo "Hello world"
[I've also tried adding sleep 5 and wait with mixed results but it doesn't solve the problem]
The expect script I'm running locally is:
#!/usr/bin/expect
set spath /tmp/cwow/generic.ksh
set pass $env(MYEXPECTPASS)
set user $env(MYEXPECTUSER)
if { [llength $argv] != 2 } {
send_user "USAGE: $argv0 host inst\n"
exit
}
set host [lindex $argv 0]
set inst [lindex $argv 1]
set timeout 10
log_user 1
exp_internal 0
eval spawn /usr/bin/ssh -t $user#$host "sudo su - cwow"
expect {
timeout { send_user "TimedOut"; exit }
-glob "assword:" {
send "$pass\r"
expect {
-glob "assword:" {
send "$pass\r"
expect {
-glob " " {
send "/tmp/cwow/generic.ksh\r\n"
expect {
-glob "world" {
send_user "Got it\r"
}
}
}
}
}
}
}
}
I should also note that I never get the 'Got it' message but I don't really need that to work, just curious why it doesn't. What I need to work reliably is for the script to run and, most of the time, it doesn't appear to.
Any ideas for a weak expect user would be greatly appreciated.
(not an answer, just a formatted comment)
You don't need to nest all the expect commands: if you expect a pattern with no action body, the script will continue to the next command. This is more readable, IMO:
expect {
timeout { send_user "TimedOut"; exit }
"assword:"
}
send "$pass\r"
expect "assword:"
send "$pass\r"
expect " "
send "/tmp/cwow/generic.ksh\r"
expect "world"
send_user "Got it\n"
Note, you should send to the spawned process with \r as "hitting Enter". But \n is used for send_user.

Run command on cisco switch

I made the below script to login to a switch and execute a command. as soon as i execute the script it logins to switch and exits without running the command.
#!/usr/bin/expect -f
set timeout 3000
spawn bash -c "ssh ananair#10.60.255.100"
expect "*assword:"
send "pass#123\n"
expect "*#"
send "show interfaces status"
I suspect the problem is the missing \n in your final send. If you simply send "string", then expect never hits Enter at the end of the command, And since there's no final expect, it figures its work is done, and exits, probably even before it has a chance to echo the command it sent but never executed.
Consider the following:
#!/usr/bin/expect -f
set timeout 3000
spawn ssh switchhostname # no need to run this inside a shell
expect "ssword:"
send "1ns3cur3\n" # it would be better to use key based auth...
expect "#"
send "term len 0\n" # tell the switch to avoid "More" prompts
expect "#"
send "show interfaces status\n" # note the final "\n"
expect "#" # don't quit until the "show" command finishes
You might also consider getting access to this information via SNMP.
try \r instead of \n
I have my expect script that I use to login to my cisco switches for me. and then I use interact from the expect script that leaves me at the cisco prompt.
I would need to rework it to not show passwords but I can definitely help you out.
I didn't need timeouts in mine.
#!/usr/bin/expect
proc enable {} {
expect "assword:" {
send "<enable password>\r"
expect "#" {
}
}
}
proc login {} {
send "<login password>\r"
expect {
"failed" {
send "<username>\r"
enable
}
">" {
send "en\r"
enable
}
"#" {
}}
}
set user_computer_attached [lindex $argv 0]
set user_computer [split $user_computer_attached "#"]
spawn ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 \
-oGlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null \
-oStrictHostKeyChecking=no $user_computer_attached
expect_after eof {
wait
spawn telnet [lindex $user_computer 1]
expect "name:"
send [lindex $user_computer 0]
send "\r"
expect "assword:" {
login
}
}
expect "assword:" {
login
}
interact

how to make command "ps" don't show password in expect script?

I have make an example as below. The password(mingps)is the shell variable. When execute the shell script, in the mean while, execute command "ps -ef", I found the result of "ps" showed the password(mingps). For security reason, I don't want to show the password when execute command "ps -ef". So how to hide it? Thanks in advance.
#!/bin/sh
MalbanIP="XXX.XXX.XXX.XXX"
MalbanLogin="ming"
MalbanPwd="mingps"
MalbanCmd="netstat"
firstTime="true"
/usr/bin/expect <<EOF
set timeout 10
log_user 0
spawn /usr/bin/ssh $MalbanIP -l $MalbanLogin
expect {
-nocase "continue connecting (yes/no)?" {
send "yes\r"
expect "password:" {
send "$MalbanPwd\r"; set firstTime "false"; exp_continue
}
}
"password" {
if {$firstTime == "true"} {
send "$MalbanPwd\r"; set firstTime "false"
} else {
log_user 1; puts stdout "password is wrong"; log_user 0;
exit 1
}
}
}
expect "0-0-3"
log_user 1
send "$MalbanCmd \r"
set results \$expect_out(buffer)
expect "0-0-3" { send "exit\r" }
expect eof
EOF
exit 0
Option 1
The best way is to switch to using RSA keys to log in, as this will enable you to significantly strengthen your overall system security substantially. With that, you can probably avoid using Expect entirely.
Option 2
However, if you can't do that, the key to fixing things is to not pass it as either an argument or an environment variable (since ps can see both with the right options). Instead, you pass the password by writing it into a file and giving the name of that file to the Expect script. The file needs to be in a directory that only the current user can read; chmod go-rx will help there.
MalbanPwdFile=/home/malban/.securedDirectory/examplefile.txt
# Put this just before the spawn
set f [open $MalbanPwdFile]
set MalbanPwd [gets $f]
close $f
You might also need to put a backslash in front of the use of $MalbanPwd so that it doesn't get substituted by the shell script part too early.
Option 3
Or you could stop using that shell wrapper and do everything directly in Tcl/Expect.
#!/usr/bin/expect
set MalbanIP "XXX.XXX.XXX.XXX"
set MalbanLogin "ming"
set MalbanPwd "mingps"
set MalbanCmd "netstat"
set firstTime true
set timeout 10
log_user 0
spawn /usr/bin/ssh $MalbanIP -l $MalbanLogin
expect {
-nocase "continue connecting (yes/no)?" {
send "yes\r"
expect "password:" {
send "$MalbanPwd\r"
set firstTime false
exp_continue
}
}
"password" {
if {$firstTime} {
send "$MalbanPwd\r"
set firstTime false
} else {
log_user 1
puts stdout "password is wrong"
log_user 0
exit 1
}
}
}
expect "0-0-3"
log_user 1
send "$MalbanCmd \r"
set results \$expect_out(buffer)
expect "0-0-3" { send "exit\r" }
expect eof
I suspect that this last option will work best for you in the longer term. It's definitely the simplest one (other than switching to RSA keys, which is what I've got deployed on my own infrastructure) and I think it is going to avoid some subtle bugs that you've got in your current code (due to substitution of variables at the wrong time).

Is it possible to access an expect-spawned process from other expect scripts, called in bash

I have an expect script that opens a telnet session and performs the authorization. After the authorization has been performed, I want to be able to call from bash other expect scripts, that perform various actions in the same session.
Is it possible?
Not exactly getting what you want because if you provide some code or script then batter but anyway i just having bash script which only open telnet session which is first part what you want now let me know what next part while gone through script so i can modify my script as you want.
#!/bin/bash
testUserAction()
{
expect<<EOF
set timeout 200
spawn telnet $1
expect "login:"
send "testuser\r"
expect "Password"
send "12345678\r"
;;;;;;;;;;;;;#rest of expect which you want
EOF
}
testUserAction 171.172.12.1
It is possible see below example:
#!/usr/local/bin/expect
set user "user"
set host "host"
set pass "password"
spawn telnet $host
set timeout 10
expect {
timeout {
puts "Unable to connect to $host"
exit 1
}
"login: " {
send -- "$user\r"
exp_continue
}
"assword: " {
send -- "$pass\r"
}
}
#Call the other expect script on host
send -- "./test.exp $user\r"
expect {
"($user)" {
puts "OK got it!"
}
}
And then the test.exp script which should be in users home directory on host
#!/usr/local/bin/expect
set name [lindex $argv 0]
puts "Welcome ($name)"

Resources