I want to make a script that it take as expect first promt after spawn "ssh user#server".
Am example:
I have 5 servers but all differs the initial promt and i can't modify:
[root#test home]#
root#server2:~$
User: root Server: server3 ~ !
You got the point.
This is how i think but i can't figure out how to get that
set timeout -1
spawn "ssh root#server"
expect "assword:"
send "password\n"
#
var=getpromt
#
expect "$var"
send "stuff\n"
expect eof
How can i get those promts to a expect script that can recognize that is the promt to follow?
I would just keep an array of regular expressions:
array set prompt_re {
test {#\s*$}
server2 {$\s*$}
server3 {!\s*$}
}
spawn ssh $user#$host
expect assword:
send "$password\r"
expect -re $prompt_re($host)
Or, you could mash those up into a single regex
expect -re {[#$!]\s*$} ;# expect the prompt.
Try this (pseudo code):
echo commands-to-be-executed-on-ssh | expect-script
& your expect-script would looks something like:
set timeout -1
spawn "ssh root#server"
expect "assword:"
send "password\n"
interact # <~~~~~~~~~~~ At this point, expect would pass the redirected/piped stdin to ssh process.
Note: I haven't tested this. So apologies for any syntax errors :)
Related
I'm trying to control a small car toy using the raspberry pi 3 the SSH
from my laptop.
The challenge I'm facing is, when I'm directly connected on RPi (mouse,
keyboard and monitor plugged to the RPI) everything works well, but
when I connect to RPi over the SSH connection, this is what I see as an
output: Xlib.error.DisplayNameError: Bad display name ""
The method I'm using to connect to my RPi is "ssh pi#"
Please can you advice me what to do next?
The sample code I'm using is shown below:
Thank you,
Johny 1984
from pynput import keyboard
from pynput.keyboard import Key, Listener
def on_press(key):
# DO SOMETHING
def on_release(key):
# DO SOMETHING
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
the way I connecting to RPI over SSH
ssh pi#(RPI_IP_ADDRESS)
The error with the bunch of text above:
Xlib.error.DisplayNameError: Bad display name ""
It happens pynput needs X and some other setup listed in their documentation. So I ended up using keyboard package https://pypi.org/project/keyboard/ instead of pynput.keyboard.
https://unix.stackexchange.com/questions/427345/keyboard-monitoring-without-display
I modified his solution a bit so that I can exit from listening when escape key is pressed. Roughly something like this:
import keyboard
import time
#declaring it global so that it can be modified from function
global releaseListening
keepListening = True
def key_press(key):
print(key.name)
#if escape is pressed make listening false and exit
if key.name == "esc"
keepListening = False
keyboard.on_press(key_press)
while keepListening :
time.sleep(1)
I had a similar problem recently, and I could find a solution.
To make above work over ssh it is required to open /etc/ssh/sshd_config and uncomment the following lines if
they are commented:
X11Forwarding yes
X11DisplayOffset 10
X11UseLocalhost yes
then on remote RPI type echo $DISPLAY. The result should be something like this:
localhost:10.0
when running/debugging your script, you should set env variable DISPLAY=result_of_echo_$DISPLAY
P.s Do not forget to connect via ssh using X server -> ssh user#address -X
i have set of servers (150) for logging and a command (to get disk space). How can i execute this command for each server.
Suppose if script is taking 1 min to get report of command for single server, how can i send report for all the servers for every 10 min?
use strict;
use warnings;
use Net::SSH::Perl;
use Filesys::DiskSpace;
# i have almost more than 100 servers..
my %hosts = (
'localhost' => {
user => "z",
password => "qumquat",
},
'129.221.63.205' => {
user => "z",
password => "aardvark",
},
'129.221.63.205' => {
user => "z",
password => "aardvark",
},
);
# file system /home or /dev/sda5
my $dir = "/home";
my $cmd = "df $dir";
foreach my $host (keys %hosts) {
my $ssh = Net::SSH::Perl->new($host,port => 22,debug => 1,protocol => 2,1 );
$ssh->login($hostdata{$host}{user},$hostdata{$host}{password} );
my ($out) = $ssh->cmd($cmd});
print "$out\n";
}
It has to send output of disk space for each server
Is there a reason this needs to be done in Perl? There is an existing tool, dsh, which provides precisely this functionality of using ssh to run a shell command on multiple hosts and report the output from each. It also has the ability, with the -c (concurrent) switch to run the command at the same time on all hosts rather than waiting for each one to complete before going on to the next, which you would need if you want to monitor 150 machines every 10 minutes, but it takes 1 minute to check each host.
To use dsh, first create a file in ~/.dsh/group/ containing a list of your servers. I'll put mine in ~/.dsh/group/test-group with the content:
galera-1
galera-2
galera-3
Then I can run the command
dsh -g test-group -c 'df -h /'
And get back the result:
galera-3: Filesystem Size Used Avail Use% Mounted on
galera-3: /dev/mapper/debian-system 140G 36G 99G 27% /
galera-1: Filesystem Size Used Avail Use% Mounted on
galera-1: /dev/mapper/debian-system 140G 29G 106G 22% /
galera-2: Filesystem Size Used Avail Use% Mounted on
galera-2: /dev/mapper/debian-system 140G 26G 109G 20% /
(They're out-of-order because I used -c, so the command was sent to all three servers at once and the results were printed in the order the responses were received. Without -c, they would appear in the same order the servers are listed in the group file, but then it would wait for each reponse before connecting to the next server.)
But, really, with the talk of repeating this check every 10 minutes, it sounds like what you really want is a proper monitoring system such as Icinga (a high-performance fork of the better-known Nagios), rather than just a way to run commands remotely on multiple machines (which is what dsh provides). Unfortunately, configuring an Icinga monitoring system is too involved for me to provide an example here, but I can tell you that monitoring disk space is one of the checks that are included and enabled by default when using it.
There is a ready-made tool called Ansible for exactly this purpose. There you can define your list of servers, group then and execute commands on all of them.
I launch a master script : master.ksh
I want to do some background task during the work of master.ksh.
For this, I created an script sourced at the beggining of master.ksh : slave.ksh with a $
./slave.ksh &
here is the code of slave.ksh:
#!/bin/ksh
touch tmpfile
export thepid=$!
while [[`if [ -n "$thepid" ];fi`]]; do
pwd >> tmpfile
#other set of commands ...
export thepid=$!
done
thepid is used to monitor the pid of the master.ksh, when master.ksh ends, I expect the end of the slave.ksh too and so, the exit of slave.ksh too
but I get an error from slave.ksh :
syntax error at line 5; fi unexpected
if I delete fi , I get another error. What is the good way to test $thepid ?
...
I'm not sure where to begin. This is broken in at least three ways: shell variables don't work that way, if statements don't work that way, and conditionals don't work that way.
Here's one way to do it (tested on 93u+):
> cat master.ksh
#!/bin/ksh -eu
print master says hi
./slave.ksh&
sleep 5
print master says bye
> cat slave.ksh
#!/bin/ksh -eu
print slave says hi
while (($(ps oppid= $$)==$PPID))
do
# work
print slave working....
sleep 1
done
print slave says bye
> ./master.ksh
master says hi
slave says hi
slave working....
slave working....
slave working....
slave working....
slave working....
master says bye
> slave says bye
This compares the PPID shell variable, which appears to be set at process launch, to the parent process id as returned by the linux ps tool, which returns the true current value. This works because when a process dies, any child processes it had have their parent process changed to 1 (init). So the slave works as long as its original PPID matches its current PPID, and then exits.
I need to detect when a computer is idle for a certain time period. My definition of idleness is:
No users logged in, either by remote methods or on the local machine
X server inactivity, with no movement of mouse or key presses
TTY keyboard inactivity (hopefully)
Since the majority of distros have now moved to logind, I should be able to use its DBUS interface to find out if users are logged in, and also to monitor logins/logouts. I have used xautolock to detect X idleness before, and I could continue using that, but xscreensaver is also available. Preferably however I want to move away from any specific dependencies like the screensaver due to different desktop environments using different components.
Ideally, I would also be able to base idleness on TTY keyboard inactivity, however this isn't my biggest concern. According to this answer, I should be able to directly query the /dev/input/* interfaces, however I have no clue how to go about this.
My previous attempts at making such a monitor have used Bash, due to the ease of changing a plain text script file, howver I am happy using C++ in case more advanced methods are required to accomplish this.
From a purely shell standpoint (since you tagged this bash), you can get really close to what you want.
#!/bin/sh
users_are_logged_in() {
who |grep -q .
return $?
}
x_is_blanked() {
local DISPLAY=:0
if xscreensaver-command -time |grep -q 'screen blanked'; then
return 0 # we found a blanked xscreensaver: return true
fi
# no blanked xscreensaver. Look for DPMS modes
xset -q |awk '
/DPMS is Enabled/ { dpms = 1 } # DPMS is enabled
/Monitor is On$/ { monitor = 1 } # The monitor is on
END { if(dpms && !monitor) { exit 0 } else { exit 1 } }'
return $? # true when DPMS is enabled and the monitor is not on
}
nobody_here() {
! users_are_logged_in && x_is_blanked
return $?
}
if nobody_here; then
sleep 2m
if nobody_here; then
# ...
fi
fi
This assumes that a user can log in in two minutes and that otherwise, there is no TTY keyboard activity.
You should verify that the who |grep works on your system (i.e. no headers). I had originally grepped for / but then it won't work on FreeBSD. If who has headers, maybe try [ $(who |grep -c .) -gt 1 ] which will tell you that the number of lines that who outputs is more than one.
I share your worry about the screensaver part; xscreensaver likely isn't running in the login manager (any other form of X would involve a user logged in, which who would detect), e.g. GDM uses gnome-screensaver, whose syntax would be slightly different. The DPMS part may be good enough, giving a far larger buffer for graphical logins than the two minutes for console login.
Using return $? in the last line of a function is redundant. I used it to clarify that we're actually using the return value from the previous line. nobody_here short circuits, so if no users are logged in, there is no need to run the more expensive check for the status of X.
Side note: Be careful about using the term "idle" as it more typically refers to resource (hardware, that is) consumption (e.g. CPU load). See the uptime command for load averages for the most common way of determining system (resource) idleness. (This is why I named my function nobody_here instead of e.g. is_idle)
Want to build up a Auth Smtp Connection with expect script... just to test I wanted to get ehlo parameters but expect is not working like this
#!/usr/bin/expect
set timeout -1
set smtp [lindex $argv 0]
set port [lindex $argv 1]
spawn telnet $smtp $port
expect "[2]{2,}[0]{1,}"
send "ehlo\n"
I expect the code 220 to come from mailserver to continue to send ehlo ... just like
..../...:telnet smtp.mail.yahoo.de 25
Trying 77.238.184.85...
Connected to smtp2-de.mail.vip.ukl.yahoo.com.
Escape character is '^]'.
220 smtp116.mail.ukl.yahoo.com ESMTP
ehlo
250-smtp116.mail.ukl.yahoo.com
250-AUTH LOGIN PLAIN XYMCOOKIE
250-PIPELINING
250-SIZE 41697280
250 8BITMIME
error saying:
spawn telnet smtp.mail.yahoo.de 25
invalid command name "2"
while executing
"2"
invoked from within
"expect "[2]{2,}[0]{1,}""
(file "./login.exp" line 6)
if I just write expect "220" instead of expect "[2]{2,}[0]{1,}" it works but ignors send "ehlo\n"
As above adviced I used exp_internal 1 to get sense of what expects really listen to...
Also I can recommend autoexpect which created the expect script not perfectly but after improving some codings it is a real help and at last it worked.
#!/usr/bin/expect
#exp_internal 1
set timeout -1
set smtp [lindex $argv 0]
set port [lindex $argv 1]
spawn telnet $smtp $port
expect -re {[2]{2,}[0]{1,}}
sleep 3;
send -- "Ehlo\r"
expect -re {[2]{1,}[5]{1,}[0]{1,}}
send -- "quit\r"
expect eof
You need to send a newline after sending "ehlo":
send "ehlo\n"
EDIT: Based on your latest edit, you also have to escape the leading bracket in your regex to prevent tcp from trying to interpret it as a command:
expect "\[2]{2,}\[0]{1,}"
EDIT: Also, your expect line isn't actually matching what you think it is. At this point, I'd suggest running through on of the many tutorials on expect, or simply use autoexpect to generate your script.