print expect output to the terminal on the fly - tcl

I want to run some script on other machine, by doing ssh there. This script might take longer time and it would be better for me if I am able to display what is going on right now in execution step.
But, when I pass to spawn ssh with script, task is completed and it comes out silently. Is there a way that we can print the output from the other machine. I can take a log also, but it can be accessed when job is completed. Can I do it on the fly?
package require Expect
spawn ssh machine
expect -regexp {
"bash" {
send "perl script.pl\r"
}
timeout {
exit
}
eof {
exit
}
interact
}
puts "Done"
I have used, puts $expect_out(buffer); which is giving me no variable error.
Thanks in advance.

Related

Expect script: Need a means to validate an SSH connection does not need a password

I am a system tester and I have a particular set of DUTs which can run either on a pre-release version of the firmware or a release candidate. On the pre-release I can access the Linux core OS of the DUT by logging in with a particular user account. The release candidate does not allow this.
The whole basis of the script I'm writing is to be able to remotely execute a script which resides on the DUT. I would like to check if I have access to the Linux core OS with need of a password or not. If I do have access then continue else exit the script.
I have tried many things and each has failed when testing against the release candidate as a password is expected. Here is the latest attempt:
set status [catch {exec ssh $user#$host ls} result]
if { [regexp "Password:" $result]} then {
# A password was asked for. Fail
puts "This is a Release Candidate Version\nAccess to the Linux OS is denied"
exit
} else {
# no password needed. Success
puts "This is a Pre-Release version"
}
When executed against a pre-release version this code works. But when a password is required it does not as the SSH session prompts for a password and waits for input.
Would anyone have a workaround that would break out of the required password scenario?
Thank you
If you have a case where the connection to the remote system may ask for a password, but isn't always going to, you're best of doing the connection from within expect. This is because the expect command can be told to wait for several different things at once.
set timeout 20; # 20 seconds; if things don't respond within that, we've got problems
# 'echo OK' because it is quick and produces known output
spawn ssh $user#$host echo OK
# Wait for the possibilities we know about; note the extra cases!
expect {
"Password:" {
# Password was asked for
puts "This is a Release Candidate Version\nAccess to the Linux OS is denied"
close
exit
}
"OK" {
# Password not asked for
puts "This is a Pre-Release version"
}
timeout {
puts "There was a network problem? Cannot continue test"
close
exit 1
}
eof {
puts "Inferior ssh exited early? Cannot continue test"
close
exit 1
}
}
close
# ... now you've checked the connection ...

How to build a telnet application using spawn? How to give user input to "send" command?

I am trying to build a telnet application in TCL.
I have to do prompt the user whenever the device is prompting.
have to expect any prompt to make it generic.
I tried:
expect -re {^(.*)$}
to match anything.
I have to get input from user and the send it to the spawned process.
Print whatever the device gives for the command given by the user .and again from step 1.
My problem is that I cannot use gets stdin while doing expect.
Example:
while {1} {
expect {
-re {^(.*)$} {
gets stdin cmd
send "$cmd"
}
}
# print the device output for $cmd here
}
So , Is there any way to give user input to "send" command ?

TCL program to login to a remote server and excute command

I need to write a TCL program through which I shall be able to login to the remote server and then execute commands on the remote server; also I need to get the output from the remote server.
EDIT:
Thanks Kostix for the reply. My requirement says that the TCL script should be able to login to the remote server. I am planning to send the password thru the expect mechanism, and after that I am planning to send the commands. My sample code goes like this:
set prompt "(%|>|\#|\\\$) #"
spawn /usr/bin/ssh $username#$server
expect {
-re "Are you sure you want to continue connecting (yes/no)?" {
exp_send "yes\r"
exp_continue
#continue to match statements within this expect {}
}
-nocase "password: " {
exp_send "$password\r"
interact
}
}
I am able to login with this but dont know how to extend this code to send commands. I've tried few methods, but didn't work out.
Since you're about to use SSH, you might not need neither Tcl nor Expect to carry out this task at all: since SSH stands for "Secure SHell", all you need to do to execute commands remotely is to tell SSH what program to spawn on the remote side after logging in (if you do not do this, SSH spawns the so-called "login shell" of the logged in user) and then SSH feeds that program what you pass the SSH client on its standard input and channels back what the remote program writes to its standard output streams.
To automate logging via SSH, several ways exist:
Authentication using public keys: if the private (client's) key is not protected by a password, this method requires no scripting at all — you just tell the SSH client what key to use.
"Keyboard interactive" authentication (password-based). This is what most people think SSH is all about (which is wrong). Scripting this is somewhat hard as SSH insists on getting the password from a "real terminal". It can be tricked to beleive so either through using Expect or simply by wrapping a call to the SSH client using the sshpass program.
Both the SSH client and the server might also support Kerberos-based or GSSAPI-based authentication. Using it might not require any scripting as it derives the authentication credentials from the system (the local user's session).
So the next steps to carry out would be to narrow your requirements:
What kind of authentication has to be supported?
What program should perform the commands you intend to send from the client? Will that be a Unix shell? A Tcl shell? Something else?
Should that remote command be scripted using some degree of interactivity (we send something to it then wait for reply and decide what to send next based on it) or batch-style script would be okay?
Before these questions are answered, the inital question has little sense as it's too broad and hence does not meet stackoverflow format.
Commands on the server can be executed either using exec command like this,
set a [exec ls -lrta]
puts $a
[OR] The expect and execute loop can be continued as above;
I am making a proc using which linux commands can easily be run;
package require Expect
proc ExecCommand {username server password cmd } {
spawn /usr/bin/ssh $username#$server
expect {
".*(yes/no)? " {exp_send "yes\r" ; exp_continue}
".*password: " {exp_send "$password\r";exp_continue}
".*$ " {exp_send -i $spawn_id "$cmd \r";
expect {
".*$ " { return 1; close }
}
}
}
}
set result [ExecCommand "admin" "0" "qwerty" "ls"]
if {$result > 0 } {
puts "Command succesfully executed\n"
} else {
puts "Failed to execute\n"
}

Handle exit command executed by embedded Tcl runtime

I have a small shell application that embeds Tcl to execute some set of Tcl code. The Tcl interpreter is initialized using Tcl_CreateInterp. Everything is very simple:
user types Tcl command
the command gets passed to Tcl_Eval for evaluation
repeat
But if a user types 'exit', which is a valid Tcl command, the whole thing - Tcl interpreter and my shell application - exit automatically.
Q: is there any way I can catch this exit signal coming from Tcl interpreter. I really would like not to check every user command. I tried Tcl_CreateExitHandler, but it didn't work.
Thanks so much.
Get rid of the command
rename exit ""
Or redefine it to let the user know it's disabled:
proc exit {args} { error "The exit command is not available in this context" }
Also worth considering is running the user's code in a safe interp instead of in the main shell. Doing so would allow you to control exactly what the user has access to.
You might also be able to create a child interp (non-safe) and just disable the exit command for that interp.
Lastly, you could just rename exit to something else, if you're only trying to avoid users typing it by mistake:
namespace eval ::hidden {}
rename exit ::hidden::exit
Rename the exit command:
rename exit __exit
proc exit {args} {
puts -nonewline "Do you really want to exit? (y/n) "
flush stdout
gets stdin answer
if {$answer == "y"} {
__exit [lindex $args 0]
}
}
This way, when the user type exit, he/she will execute your custom exit command, in which you can do anything you like.
Using Tcl_CreateExitHandler works fine. The problem was that I added a printf into the handler implementation and the output didn't show up on the terminal. So I thought it hasn't been called. However, by the time this handler is executed there is no stdout any more. Running strace on the application shows that the handler is being executed fine.
Another solution to this problem can be to use atexit and process the exit event there.

Expect/TCL: pass commands to specific proc/spawn IDs

I am trying to write an expect script that will do the following..
open up 13 terminal windows (gnome-terminal, xterm etc)
each window connects to a terminal server via ssh (ssh InReach#10.1.6.254)
and is provided the password via expect.
i can get this to work fine in a single window. the problem i am having though is getting the input passed over to each window.
for instance...
i can do
set timeout -1
spawn gnome-terminal -x ssh InReach#10.1.6.254
inside of a while loop and get my 13 windows. but i would like each one to be logged in automatically via expect.
You can try a slightly different approach. Instead of opening the terminal windows in the expect script, open them in a basic shell script, and have each terminal run an expect script to start a single SSH session.
So the expect script could be as simple as this:
#!/usr/bin/expect -f
spawn ssh InReach#10.1.6.254
# ... provide password ...
interact
And the shell script:
#!/bin/sh
for a in `seq 1 13`; do
gnome-terminal -x ./expect_script
done
When you spawn, you need to cache the $spawn_id value which is set by the attempt.
e.g.
if [catch "spawn ssh -l mtc $ub1_ip_address" ub1_pid] {
Log $ERROR "Unable to spawn ssh to Xubuntu.\n$ub1_pid\n"
return 0
}
set stored_id $spawn_id
To send a command to one terminal session in particular, do
send -i $stored_id "command"
Then, before you contact each, you must first do
expect {
-i $stored_id
[ ... your regexes, globs, etc. ... ]
}
You can find some add'l info http://wiki.tcl.tk/11583
I would also suggest making use of gnome-terminal's ability to specify multiple tabs, including an indication of which is the currently-active one, and a command to be executed. gnome-terminal --help-all is helpful (no pun intended).