expect garbage before prompt - tcl

I try to connect to my router using ssh in order to automatically extract some logs from it.
I developed this code below :
#!/usr/bin/expect -f
spawn ssh root#192.168.1.1
expect "Are you sure you want to"
send -- "yes\r"
expect "password"
send -- "root\r"
expect "\#"
send -- "ls\r"
expect "\#"
the problem is I expected a garbage before the prompt in the output log.
spawn ssh root#192.168.1.1
The authenticity of host '192.168.1.1 (192.168.1.1)' can't be established.
RSA key fingerprint is SHA256:6aeE74qXMeQzg0SGJBZMIa0HFQ5HJrNqE5f3XZ6Irds.
Are you sure you want to continue connecting (yes/no)? yes
Failed to add the host to the list of known hosts (/home/amin/.ssh/known_hosts).
root#192.168.1.1's password:
BusyBox v1.30.1 () built-in shell (ash)
OpenWrt Version: ALPHA
OpenWrt base: 19.07
------------------------------------
]0;root#openwrt: ~root#openwrt:~# ls
[0;0mnetwork[m
]0;root#openwrt: ~root#openwrt:~#
what's the main cause of this issue? How I can fix it?

The problem is that there are terminal escape sequences being issued, probably to control what colour the terminal uses. The easiest fix is to set the terminal type (an environment variable) to something that doesn't support colour before doing the spawn. Perhaps this will do the trick:
set env(TERM) "dumb"
If that doesn't work (it depends on exactly what is in someone's .bashrc) then you can just override the PS1 environment variable on the remote side with your first command after logging in.
# etc for logging in
expect "# "
send "PS1='# '\r"
expect "# "
# Everything should be right from here on

Related

How to send binary flashing file to embedded system with only serial console?

I have an embedded Linux system that uses ramdisk boot so it has run time no persistent storage available (it does have Flash to store kernel and ramdisk).
The only connectivity is RS-232 serial login console. So I am limited by what is provided by its built in busybox. I want to retrieve the ramdisk, modify it, and rewrite the ramdisk. The kernel does not have Flash filesystem support built-in. The ramdisk partition size is about 10 MBytes. When all files in the user directory are deleted, the free ramdisk size is about 14 MBytes.
The command dd is available so I can copy the ramdisk partition to the ramdisk, and can write to the flash from a ramdisk file. flashcp is also available.
So my problem is now how to receive and send binary files through the RS-232 serial console?
I research the followings and none is useful for me:
Linux command to send binary file to serial port with HW flow control? on stackoverflow
Binary data over serial terminal on stackoverflow
Transferring files using serial console on k.japko.eu
File transfer over a serial line on superuser.com
How to get file to a host when all you have is a serial console? on stackexchange
Mostly because x/y/zmodem are not available in the busybox.
Any idea? Thanks!
Per the request, here's what I should have included in the first place.
Available u-boot commands:
U-Boot >?
? - alias for 'help'
askenv - get environment variables from stdin
base - print or set address offset
bdinfo - print Board Info structure
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootm - boot application image from memory
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
crc32_chk_uimage- checksum calculation of an image for u-boot
echo - echo args to console
editenv - edit environment variable
env - environment handling commands
exit - exit script
false - do nothing, unsuccessfully
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fatwrite- write file into a dos filesystem
go - start application at address 'addr'
gpio - input/set/clear/toggle gpio pins
help - print command description/usage
i2c - I2C sub-system
iminfo - print header information for application image
imxtract- extract a part of a multi-image
itest - return true/false on integer compare
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
md - memory display
mdc - memory display cyclic
mm - memory modify (auto-incrementing address)
mw - memory write (fill)
mwc - memory write cyclic
nm - memory modify (constant address)
printenv- print environment variables
reset - Perform RESET of the CPU
run - run commands in an environment variable
saveenv - save environment variables to persistent storage
saves - save S-Record file over serial line
setenv - set environment variables
sf - SPI flash sub-system
showvar - print local hushshell variables
sleep - delay execution for some time
source - run script from memory
sspi - SPI utility command
test - minimal test like /bin/sh
true - do nothing, successfully
usb - USB sub-system
usbboot - boot from USB device
version - print monitor, compiler and linker version
U-Boot >
Available busybox commands:
BusyBox v1.13.2 (2015-03-16 10:50:56 EDT) multi-call binary
Copyright (C) 1998-2008 Erik Andersen, Rob Landley, Denys Vlasenko
and others. Licensed under GPLv2.
See source distribution for full notice.
Usage: busybox [function] [arguments]...
or: function [arguments]...
BusyBox is a multi-call binary that combines many common Unix
utilities into a single executable. Most people will create a
link to busybox for each function they wish to use and BusyBox
will act like whatever it was invoked as!
Currently defined functions:
[, [[, addgroup, adduser, ar, ash, awk, basename, blkid,
bunzip2, bzcat, cat, chattr, chgrp, chmod, chown, chpasswd,
chroot, chvt, clear, cmp, cp, cpio, cryptpw, cut, date,
dc, dd, deallocvt, delgroup, deluser, df, dhcprelay, diff,
dirname, dmesg, du, dumpkmap, dumpleases, echo, egrep, env,
expr, false, fbset, fbsplash, fdisk, fgrep, find, free,
freeramdisk, fsck, fsck.minix, fuser, getopt, getty, grep,
gunzip, gzip, halt, head, hexdump, hostname, httpd, hwclock,
id, ifconfig, ifdown, ifup, inetd, init, insmod, ip, kill,
killall, klogd, last, less, linuxrc, ln, loadfont, loadkmap,
logger, login, logname, logread, losetup, ls, lsmod, makedevs,
md5sum, mdev, microcom, mkdir, mkfifo, mkfs.minix, mknod,
mkswap, mktemp, modprobe, more, mount, mv, nc, netstat,
nice, nohup, nslookup, od, openvt, passwd, patch, pidof,
ping, ping6, pivot_root, poweroff, printf, ps, pwd, rdate,
rdev, readahead, readlink, readprofile, realpath, reboot,
renice, reset, rm, rmdir, rmmod, route, rtcwake, run-parts,
sed, seq, setconsole, setfont, sh, showkey, sleep, sort,
start-stop-daemon, strings, stty, su, sulogin, swapoff,
swapon, switch_root, sync, sysctl, syslogd, tail, tar, tcpsvd,
tee, telnet, telnetd, test, tftp, tftpd, time, top, touch,
tr, traceroute, true, tty, udhcpc, udhcpd, udpsvd, umount,
uname, uniq, unzip, uptime, usleep, vconfig, vi, vlock,
watch, wc, wget, which, who, whoami, xargs, yes, zcat
In uboot you could use loady/loadx to get file from pc via uart.I usually use teraterm to send file.
The process should be this:
run loady in uboot
use teraterm send data
the file is transfer to you device's memory located in 0x01000000.
Independently I found a way to upload binary files through the Linux console and I'll document the steps here in case others find it useful since I had a hard time looking for this information on the net.
Here's the theory: change the console mode to raw so all the binary traffic are't interpretted as console command, e.g. ctrl-C. Turn off echo so it doesn't add extra serial traffic. Run tar to accept input from the stdin. Since ctrl-C won't work, and tar won't know when to terminate, use a background task to kill the login shell so you can login again to do your staff.
Steps:
Create a script to run in the background. Change myvar variable so it kills the login shell after the transfer is complete. Currently 120 corresponds to 1200 seconds, sufficient for a 10 MBytes file. In addition edit the 808 to match your login shell PID:
create bg file:
myvar=120
while [ $myvar -gt 0 ]
do
myvar=$(( $myvar-1 ))
echo -e " $myvar \n"
ls -l
sleep 10
done
kill -9 808
Launch the script in the background:
in console type:
source ./bg &
Use stty to change console to raw mode and do not echo
in console type:
stty raw -echo
Start tar to untar stdin. Note: I have to use ctrl-J since no longer work after the stty command
in console type and ends with ctrl-j, not :
tar zx -f - 1> 1.log 2> 2.log
Start Teraterm to send binary file
Wait for completion and the new login prompt
I forgot I asked this question. I figured out how to make ssh connection which in turn allows many more things to be done more easily. Of course it requires sshd in addition to nc and stty so you are out of luck if these are not available on your embedded Linux. I have tried it several times and it seems to work well, allowing multiple ssh sessions to be established, and mc to transfer files.
You will need two shell sessions on the host computer, one to loop the serial port to socket, and the other for the ssh, and more if you want to establish more ssh sessions.
First you need to setup the serial port. The '--noreset' option for picocom does this:
sudo picocom --noreset -b 115200 -e b /dev/ttyUSB3
Quit picocom once this is done (^B^X to exit).
Next we need to verify that the line endings are not translated or else ssh won't work. In the first shell run:
cat /dev/ttyUSB3 | hexdump -C
In the second shell run:
echo "echo -e \"LFLF\\n\\nCRCR\\r\\rEND\"" > /dev/ttyUSB3
You may see that \n (0x0A) is translated to \r\n (0x0D0x0A)
Use stty to set raw mode without echo and you should see no more translation:
echo "stty raw -echo" > /dev/ttyUSB3
echo "echo -e \"LFLF\\n\\nCRCR\\r\\rEND\"" > /dev/ttyUSB3
Finally in the first shell run nc to funnel local traffic between the serial port and ssh socket:
cat /dev/ttyUSB3 | nc -l -p 2222 > /dev/ttyUSB3
and funnel remote serial traffic to sshd:
echo "while true ; do nc localhost 22 ; done" > /dev/ttyUSB3
and connect ssh with port forwarding:
ssh -vvv root#localhost -p 2222 -L 0.0.0.0:22022:localhost:22
you can make more ssh connections simultaneously:
ssh -vvv root#localhost -p 22022
if you use mc, you can connect to it so you can easily browse the remote file system and copy files:
sh://root#localhost:22022
Last words: nc strips the TCP headers so the ssh packets are no checksumed and are not retried. If there were data error, the connection will break. If you remember your login shell PID, you can kill it and login again, otherwise you have to reboot. The '-vvv' flag for the ssh is for debugging.

Delay between two expect in TCL

I am writing a script to automate the ambari server installation.
I have created on tcl script to automate ambari-server setup. My issue is at one place it downloads and installs jdk and that step takes a little time and meanwhile the other sends under other expects starts popping up on screen and screws up all the installation.
My Script:
#!/usr/bin/expect
spawn sudo ambari-server setup
expect "OK to continue"
send "y\r"
expect "Customize user account for ambari-server daemon"
send "y\r"
expect "Enter user account for ambari-server daemon (root):"
send "root\r"
expect "Enter choice (1):"
send "1\r"
expect "Do you accept the Oracle Binary Code License Agreement"
send "y\r"
expect"Enter advanced database configuration"
send "y\r"
expect "Enter choice (1):"
send "3\r"
expect "Hostname (localhost):"
send "localhost\r"
expect "Port (3306):"
send "3306\r"
expect "Database name (ambari):"
send "ambari\r"
expect "Username (ambari):"
send "ambari\r"
expect "Enter Database Password (bigdata):"
send "password\r"
expect "Proceed with configuring remote database connection properties"
send "y\r"
after accepting Oracle Binary Code License Agreement it downloads and installs jdk and in that duration it starts taking send of next excepts.
Can someone suggest me how to stop the execution of an except while the previous one is still running.
i did try to use after and sleep to try something but it did not work.
Thanks
altering the timeout is the right way to do it. I'd write:
expect "Do you accept the Oracle Binary Code License Agreement"
set old_timeout $timeout ;# remember the previous value
set timeout -1 ;# disable the timeout
send "y\r"
expect"Enter advanced database configuration"
set timeout $old_timeout ;# restore the timeout
send "y\r"
Your code is missing handlers for what to do if an expected string is not received. It would be good style to add those. Otherwise the script will just continue if the string was not received within the allotted time (10 seconds by default), as you have seen.
To use a different timeout for one expect command, you can simply use the -timeout option. For example to allow 10 minutes for the jdk to install:
expect -timeout 600 "Enter advanced database configuration"
send "y\r"
And with an added handler in case of failure:
expect {
-timeout 600
"Enter advanced database configuration" {
send "y\r"
}
default {
error "jdk failed to install in time"
}
}

MySQL login-path issues with clustercheck script used in xinetd

default: on
# description: mysqlchk
service mysqlchk
{
# this is a config for xinetd, place it in /etc/xinetd.d/
disable = no
flags = REUSE
socket_type = stream
type = UNLISTED
port = 9200
wait = no
user = root
server = /usr/bin/mysqlclustercheck
log_on_failure += USERID
only_from = 0.0.0.0/0
#
# Passing arguments to clustercheck
# <user> <pass> <available_when_donor=0|1> <log_file> <available_when_readonly=0|1> <defaults_extra_file>"
# Recommended: server_args = user pass 1 /var/log/log-file 0 /etc/my.cnf.local"
# Compatibility: server_args = user pass 1 /var/log/log-file 1 /etc/my.cnf.local"
# 55-to-56 upgrade: server_args = user pass 1 /var/log/log-file 0 /etc/my.cnf.extra"
#
# recommended to put the IPs that need
# to connect exclusively (security purposes)
per_source = UNLIMITED
}
/etc/xinetd.d #
It is kind of strange that script works fine when run manually when it runs using /etc/xinetd.d/ , it is not working as expected.
In mysqlclustercheck script, instead of using --user= and passord= syntax, I am using --login-path= syntax
script runs fine when I run using command line but status for xinetd was showing signal 13. After debugging, I have found that even simple command like this is not working
mysql_config_editor print --all >>/tmp/test.txt
We don't see any output generated when it is run using xinetd ( mysqlclustercheck)
Have you tried the following instead of /usr/bin/mysqlclustercheck?
server = /usr/bin/clustercheck
I am wondering if you could test your binary location with the linux which command.
A long time ago since this question was asked, but it just came to my attention.
First of all as mentioned, Percona Cluster Control script is called clustercheck, so make sure you are using the correct name and correct path.
Secondly, since the server script runs fine from command line, it seems to me that the path of mysql client command is not known by the xinetd when it runs the Cluster Control script.
Since the mysqlclustercheck script as it is offered from Percona, it uses only the binary name mysql without specifying the absolute path I suggest you do the following:
Find where mysql client command is located on your system:
ccloud#gal1:~> sudo -i
gal1:~ # which mysql
/usr/local/mysql/bin/mysql
gal1:~ #
then edit script /usr/bin/mysqlclustercheck and in the following line:
MYSQL_CMDLINE="mysql --defaults-extra-file=$DEFAULTS_EXTRA_FILE -nNE --connect-timeout=$TIMEOUT \
place the exact path of mysql client command you found in the previous step.
I also see that you are not using MySQL connection credentials for connecting to MySQL server. mysqlclustercheck script as it is offered from Percona, it uses User/Password in order to connect to MySQL server.
So normally, you should execute the script in the command line like:
gal1:~ # /usr/sbin/clustercheck haproxy haproxyMySQLpass
HTTP/1.1 200 OK
Content-Type: text/plain
Where haproxy/haproxyMySQLpass is the MySQL connection user/pass for HAProxy monitoring user.
Additionally, you should specify them to your script's xinetd settings like:
server = /usr/bin/mysqlclustercheck
server_args = haproxy haproxyMySQLpass
Last but not least, the signal 13 you are getting is because you try to write something in a script run by xinetd. If for example in your mysqlclustercheck you try to add a statement like
echo "debug message"
you probably going to see the broken pipe signal (13 in POSIX).
Finally, I had issues with this script using SLES 12.3 and I finally manage to run it not as 'nobody' but as 'root'.
Hope it helps

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"
}

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).