how to catch when the connectivity is lost in bash? - linux

i have the following script
rc=0
while [ $rc -ne 1 ]; do
ping -c1 someip
if [ "$?" -ne "0" ]; then
echo "lost ip"
else
sshpass -p password ssh user#ip 'date +"%s"'
sshpass -p password ssh user#ip 'command1'
sshpass -p password ssh user#ip 'command2'
# some other commands
fi
done
the problem is when ping lost connectivity when the script is executing the external commands through ssh, the script is hang even when the external machine recover its internet connectivity, so i was wondering how can i solve that situation.
Thanks

Related

How can I always run a command automatically after doing rlogin to a remote server?

I use my script when connecting to a remote server.
#!/bin/bash
host=$1
root=$2
case "$host" in
"1" ) host="server1";;
"2" ) host="server2";;
esac
if [ -z "$root" ];then
rlogin -l user $host
else
rlogin -l root $host
fi
Something like this.
What I want more is to run PS1="\[\e[34;1m\]\u#\[\e[36;1m\]\h:\[\e[32;1m\]\w:> \[\e[0m\]" after connecting to the remote server, which makes the terminal visually good.
Editting .bashrc cannot be done because this remote server is used my multiple users.
I tried
rlogin -l user $host && PS1="\[\e[34;1m\]\u#\[\e[36;1m\]\h:\[\e[32;1m\]\w:> \[\e[0m\]"
but this didn't work.
How can I achieve this?

Shell Script for automate SSH logins

I have created a shell script for login my servers:
#!/bin/sh
echo "Please hostname ..."
while :
do
read INPUT_STRING
case $INPUT_STRING in
host1)
`bash |sshpass -p 'qwerty123' ssh -o StrictHostKeyChecking=no root#192.168.1.4`
;;
host2)
`sshpass -p 'qwerty123' ssh -o StrictHostKeyChecking=no root#192.168.1.8`
;;
*)
echo "Sorry, I don't understand"
break;
;;
esac
done
echo
echo "Bye"
But I canĀ“t login to the server. I think the issue may be changing the shell on the scrip while accessing the server.
Please help.
The back tics are causing your program to fail. Remove the backticks and also the bash | and you may have a chance. All of the comments I've read are correct, though. You should consider using a public / private key pair. You could make your logins to the hosts easier with ~/.ssh/config.
Try this code for now:
#!/bin/sh
echo "Please hostname ..."
while :
do
read INPUT_STRING
case $INPUT_STRING in
host1)
sshpass -p 'qwerty123' ssh -o StrictHostKeyChecking=no root#192.168.1.4
;;
host2)
sshpass -p 'qwerty123' ssh -o StrictHostKeyChecking=no root#192.168.1.8
;;
*)
echo "Sorry, I don't understand"
break;
;;
esac
done
echo
echo "Bye"
Here's a one liner that pretty much does the same thing:
select INPUT_STRING in host1 host2; do ssh $INPUT_STRING; done
However, you'll need to create a ~/.ssh/config file so that host1 and host2 are mapped to IP addresses:
Host host1
HostName 192.168.1.4
User root
StrictHostKeyChecking no
Host host2
HostName 192.168.1.8
User root
StrictHostKeyChecking no
Also, to avoid the sshpass, you can use commands such as ssh-keygen and ssh-copy-id. Plenty has been written about ssh-keygen and ssh-copy-id so I won't go into it here (the keywords should be enough to get you far).

ssh to different nodes using shell scripting

I am using below code to ssh to different nodes and find if an user exists or not. If the user doesn't exist it will create it.
The script works fine if I don't do ssh but it fails if I do ssh.
How can I go through different nodes using this script?
for node in `nodes.txt`
usr=root
ssh $usr#$node
do
if [ $(id -u) -eq 0 ]; then
read -p "Enter username : " username
read -s -p "Enter password : " password
egrep "^$username" /etc/passwd >/dev/null
if [ $? -eq 0 ]; then
echo "$username exists!"
exit 1
else
pass=$(perl -e 'print crypt($ARGV[0], "password")' $password)
useradd -m -p $pass $username
[ $? -eq 0 ] && echo "User has been added to system!" || echo "F
ailed to add a user!"
fi
else
echo "Only root may add a user to the system"
exit 2
fi
done
Your script has grave syntax errors. I guess the for loop at the beginning is what you attempted to add but you totally broke the script in the process.
The syntax for looping over lines in a file is
while read -r line; do
.... # loop over "$line"
done <nodes.txt
(or marginally for line in $(cat nodes.txt); do ... but this has multiple issues; see http://mywiki.wooledge.org/DontReadLinesWithFor for details).
If the intent is to actually run the remainder of the script in the ssh you need to pass it to the ssh command. Something like this:
while read -r node; do
read -p "Enter user name: " username
read -p -s "Enter password: "
ssh root#"$node" "
# Note addition of -q option and trailing :
egrep -q '^$username:' /etc/passwd ||
useradd -m -p \"\$(perl -e 'print crypt(\$ARGV[0], \"password\")' \"$password\")" '$username'" </dev/null
done <nodes.txt
Granted, the command you pass to ssh can be arbitrarily complex, but you will want to avoid doing interactive I/O inside a root-privileged remote script, and generally make sure the remote command is as quiet and robust as possible.
The anti-pattern command; if [ $? -eq 0 ]; then ... is clumsy but very common. The purpose of if is to run a command and examine its result code, so this is better and more idiomatically written if command; then ... (which can be even more succinctly written command && ... or ! command || ... if you only need the then or the else part, respectively, of the full long-hand if/then/else structure).
Maybe you should only do the remote tasks via ssh. All the rest runs local.
ssh $user#$node egrep "^$username" /etc/passwd >/dev/null
and
ssh $user#$node useradd -m -p $pass $username
It might also be better to ask for username and password outside of the loop if you want to create the same user on all nodes.

Save ssh -V to variable

I am trying to automate the testing of passwordless ssh from 72 remote servers back to a central server. I have central server passwordless ssh working to the 72 servers, but need it working from them back the the central server.
The 72 servers have one of two ssh versions.
OpenSSH_4.3p2, OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008
OR
sshg3: SSH Tectia Client 6.1.8 on x86_64-unknown-linux-gnu Build: 136 Product: SSH Tectia Client License type: commercial
The issue I am experience is trying to save ssh -V into a variable, it seems that it does not print to STDOUT. Thus my attempts below are failing.
ssh -V > someFile.txt
ssh_version=$(ssh -V)
How can I easily save output of ssh -V so that the appropriate ssh batch option can be called?
Below is the script I am using for remote testing.
#!/bin/sh
ssh -V > /tmp/ssh_version_check.txt
cat /tmp/ssh_version_check.txt | grep "OpenSSH"
rc=$?
if [[ $rc == 0 ]]
then
ssh -o BatchMode=yes <central_server> "test -d /tmp"
rc=$?
if [[ $rc != 0 ]]
then
echo "$(hostname) failed" >> /tmp/failed_ssh_test.txt
fi
else
ssh -B <central_server> "test -d /tmp"
rc=$?
if [[ $rc != 0 ]]
then
echo "$(hostname) failed" >> /tmp/failed_ssh_test.txt
fi
fi
ssh -V outputs to STDERR, not STDOUT.
Instead of saying
ssh -V > /tmp/ssh_version_check.txt
say
ssh -V >& /tmp/ssh_version_check.txt
or
ssh -V > /tmp/ssh_version_check.txt 2>&1
In order to save to a variable, say:
ssh_version=$(ssh -V 2>&1)

How to set up an automatic (re)start of a background ssh tunnel

I am a beginner user of linux, and also quite newbie at ssh and tunnels.
Anyway, my goal is to maintain a ssh tunnel open in background.
In order to do that, I wrote the following batch that I then added into crontab (the batch is automatically processed every 5 minutes during workdays and from 8am to 9pm).
I read in some other thread in stackoverflow that one should use autossh that will ensure the ssh will always be ok through a recurrent check. So did I....
#!/bin/bash
LOGFILE="/root/Tunnel/logBatchRestart.log"
NOW="$(date +%d/%m/%Y' - '%H:%M)" # date & time of log
if ! ps ax | grep ssh | grep tunnelToto &> /dev/null
then
echo "[$NOW] ssh tunnel not running : restarting it" >> $LOGFILE
autossh -f -N -L pppp:tunnelToto:nnnnn nom-prenom#193.xxx.yyy.zzz -p qqqq
if ! ps ax | grep ssh | grep toto &> /dev/null
then
echo "[$NOW] failed starting tunnel" >> $LOGFILE
else
echo "[$NOW] restart successfull" >> $LOGFILE
fi
fi
My problem is that sometimes the tunnel stops working, although every thing looks ok (ps ax | grep ssh > the result shows the two expected tasks : autossh main task and the ssh tunnel itself). I actually know about the problem cause the tunnel is used by a third party software that triggers an error as soon as the tunnel is no more responding.
SO I am wondering how I should improve my batch in order It will be able to check the tunnel and restart it if it happens to be dead. I saw some ideas in there, but it was concluded by the "autossh" hint... which I already use. Thus, I am out of ideas... If any of you have, I'd gladly have a look at them!
Thanks for taking interest in my question, and for your (maybe) suggestions!
Instead of checking the ssh process with ps you can do the following trick
create script, that does the following and add it to your crontab via crontab -e
#!/bin/sh
REMOTEUSER=username
REMOTEHOST=remotehost
SSH_REMOTEPORT=22
SSH_LOCALPORT=10022
TUNNEL_REMOTEPORT=8080
TUNNEL_LOCALPORT=8080
createTunnel() {
/usr/bin/ssh -f -N -L$SSH_LOCALPORT:$REMOTEHOST:SSH_REMOTEPORT -L$TUNNEL_LOCALPORT:$REMOTEHOST:TUNNEL_REMOTEPORT $REMOTEUSER#$REMOTEHOST
if [[ $? -eq 0 ]]; then
echo Tunnel to $REMOTEHOST created successfully
else
echo An error occurred creating a tunnel to $REMOTEHOST RC was $?
fi
}
## Run the 'ls' command remotely. If it returns non-zero, then create a new connection
/usr/bin/ssh -p $SSH_LOCALPORT $REMOTEUSER#localhost ls >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo Creating new tunnel connection
createTunnel
fi
In fact, this script will open two ports
port 22 which will be used to check if the tunnel is still alive
port 8080 which is the port you might want to use
Please check and send me further questions via comments
(I add this as an answer since there is not enough room for it un a comment)
Ok, I managed to make the batch run to launch the ssh tunnel (I had to specify my hostname instead of localhost in order it could be triggered) :
#!/bin/bash
LOGFILE="/root/Tunnel/logBatchRedemarrage.log"
NOW="$(date +%d/%m/%Y' - '%H:%M)" # date et heure du log
REMOTEUSER=username
REMOTEHOST=remoteHost
SSH_REMOTEPORT=22
SSH_LOCALPORT=10022
TUNNEL_REMOTEPORT=12081
TUNNEL_SPECIFIC_REMOTE_PORT=22223
TUNNEL_LOCALPORT=8082
createTunnel() {
/usr/bin/ssh -f -N -L$SSH_LOCALPORT:$REMOTEHOST:$SSH_REMOTEPORT -L$TUNNEL_LOCALPORT:$REMOTEHOST:$TUNNEL_REMOTEPORT $REMOTEUSER#193.abc.def.ghi -p $TUNNEL_SPECIFIC_REMOTE_PORT
if [[ $? -eq 0 ]]; then
echo [$NOW] Tunnel to $REMOTEHOST created successfully >> $LOGFILE
else
echo [$NOW] An error occurred creating a tunnel to $REMOTEHOST RC was $? >> $LOGFILE
fi
}
## Run the 'ls' command remotely. If it returns non-zero, then create a new connection
/usr/bin/ssh -p $SSH_LOCALPORT $REMOTEUSER#193.abc.def.ghi ls >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo [$NOW] Creating new tunnel connection >> $LOGFILE
createTunnel
fi
However, I got some immediate message (below) when the tunnel is running and when cron tries to lauch the batch again... sounds like it cannot listen to it. Also since I need some time to get a proof , I can't say yet it will successfully restart if the tunnel is out.
Here's the response to the second start of the batch.
bind: Address already in use channel_setup_fwd_listener: cannot listen
to port: 10022 bind: Address already in use
channel_setup_fwd_listener: cannot listen to port: 8082 Could not
request local forwarding.

Resources