How to wait until ssh is available? - bash

I'm trying to code a script which will wait for a server to be up and check if ssh is running.
#!/bin/bash
until [ $(ssh -o BatchMode=yes -o ConnectTimeout=5 root#HOST echo ok 2>&1) = "ok" ]; do
echo "Trying again..."
done
echo "SSH is running"
I have this error if server is power off :
test3: ligne 3 : [: Too many arguments
Trying again...
^C
If server is running it output :
ok

The trivial fix is to put double quotes around the string which might come up empty.
until [ "$(ssh ...)" = "ok" ]; do ...
The Bash-only test [[ is more tolerant, so you could use [[ ... ]] instead of [ ... ] and not have to add quotes.
... but a better solution is to look for the exit status from ssh:
until ssh ...; do ...
If you want the operation to be silent, add a redirection.
until ssh user#hostname true >/dev/null 2>&1; do ...
with whatever additional options you want, of course. You might need to add one or more ssh -t options if it complains about not being connected to a TTY, for example.

Your ssh command is expanding to nothing, or to multiple words; you should quote it (and run Shellcheck on your script):
until [ "$(ssh ... )" = ok ]; do

Related

bash script which can ssh to a remote machine and execute commands, throws error when password is incorrect

I'm trying a bash script where i'm taking an argument of a file which has the IP address. I'm using sshpass, but with this i'm not able to know if ssh login was successful or not. Is there a way to check this ?
Please suggest if not sshpass, do i need to use any other cmd, to do remote login and execute cmds ?
Here is the snippet of the code :
#!/bin/bash
filename="$1"
while read -r line; do
sshpass -p 'test' ssh -o StrictHostKeyChecking=no test#$line 'df -h'
done < "$filename"
Have tried the suggested way to check $? value (if its incorrect password, $? value will be 5, however with valid or invalid password, shell script is not echoing 'wrong password', its always echoing "Can ssh to box- Password is correct" as per the following code :
#!/bin/bash
filename="$1"
while read -r line; do
sshpass -p 'test' ssh -o StrictHostKeyChecking=no test#$line 'df -h'
if [ $? -eq 5]
then
echo "Wrong password"
else
echo "Can ssh to box- Password is correct"
fi
done < "$filename"
My Requirement is to ssh to the remote box and execute commands. And in case, ssh fails i.e password is invalid, it need to print that password is invalid.
Use return value from sshpass.
According to man sshpass:
RETURN VALUES
As with any other program, sshpass returns 0 on success. In case of failure, the following return codes are used:
5 Invalid/incorrect password
After running sshpass, in bash return value from command is stored in $? variable.
Proof:
devilan#localhost:~ $ sshpass -p 'test' ssh smurf#localhost
devilan#localhost:~ $ echo $?
5
Sugested usage:
sshpass -p 'test' ssh smurf#localhost
if [ $? -eq 5 ]
then
echo "Wrong password"
else
echo "Something else"
fi
Space was missing after 5, and thus if condition wasn't getting evaluated successfully.
Here is the modified code which works :
filename="$1"
while read -r line; do
sshpass -p 'test' ssh -o StrictHostKeyChecking=no test#$line 'df -h'
if [ $? -eq 5 ]
then
echo "Wrong password"
else
echo "Can ssh to box- Password is correct"
fi
done < "$filename"

How to check connection to a list of servers in bash?

Im trying to check connections for a list of servers. I want to loop through the list, check if a connection works and if yes, do some stuff, if not, echo out a problem message.
My problem is:
the script stops at the first node without echoing the $?.
So, whats wrong with my for-loop?
These vars are included from a config file:
$nodes is a list of server IPs like 1.1.1.1,2.2.2.2,10.10.10.10
$user is one string
for node in $(echo $nodes | sed "s/,/ /g")
do
echo "Checking Node: $node"
ssh -q -o ConnectTimeout=3 $user#$node echo ok
echo $?
if [[ $? != 0 ]]
then
echo "Problem in logging into $node"
else
# do some stuff here
fi
done
EDIT #1:
for node in $(echo $nodes | sed "s/,/ /g")
do
echo "Checking Node: $node"
ssh -q -t -o ConnectTimeout=3 $user#$node "echo ok"
retcode=$?
echo $retcode
if [[ "$retcode" -ne 0 ]]
then
echo "Problem in logging into $node"
else
echo "OK"
fi
done
It is because ssh first asks you to validate The authority of the host and If you accept the authority it will ask for password. That is why your command does not return to shell and waits for input.
If your intention is just validating ssh connection, then you may consider to use
telnet <your_host> <port> < /dev/null
But if your intend is to run some commands you need a trust relationship between hosts. In that case you can use:
Execute this commands:
ssh-keygen -t rsa
then
ssh-copy-id -i root#ip_address
Now you can connect with
ssh <user>#<host>
Furher information
You can add -tto make virtual terminal and add quotes on command:
ssh -q -t -o ConnectTimeout=3 ${user}#${node} "echo ok"
Also use -ne instead of != which is for compare strings
if [[ "$?" -ne 0 ]]
Also echo $? mess the return code. You should use something like:
ssh -q -t -o ConnectTimeout=3 ${user}#${node} "echo ok"
retcode=$?
echo $retcode
if [[ "$retcode" -ne 0 ]]
You can rewrite ssh command like this to avoid problems with ssh host keys
ssh -q -t -o StrictHostKeyChecking=no -o ConnectTimeout=3 ${user}#${node} "echo ok"

How do I check if a program is running in bash?

I have made a chat-script in bash and I want to check whether or not netcat is running.
I've tried pgrep but and it's working but it prints out an error in the terminal but it still keeps going like normal.
This is a part of that script:
function session()
{
echo -n "Port (default is 3333): "
read port
if [ -n "${port}" ]
then
clear
echo "Only 2 users can talk to each other simultaneously."
echo "To send a message, simply write and hit enter. Press Ctrl+C to quit."
nc -l -p ${port}
if [ pgrep "nc -l -p ${port}" ]
then
echo "${l_name} logged in to chat session"
else
clear
new
fi
else
echo "Invalid port!"
new
fi
}
Don't put prep inside [ ]. That doesn't run the prep command, it just treats the word pgrep as an argument to the test command.
You also need to use the -f option to make pgrep match the entire command line, not just the program name.
It should be
if pgrep -f "nc -l -p ${port}"
then
...
else
...
fi
Try to run script with a "-x" parameter. This shows each line that runs. Here is a description from man page:
-x : After expanding each simple command, for command, case
command, select command, or arithmetic for command, display the
expanded value of PS4, followed by the command and its expanded
arguments or associated word list.
Here is an example script:
#!/bin/bash
for i in $( ls ); do
echo item: $i
done
If you run with -x you can see each command running with a head of + sign:
$ bash -x list.sh
++ ls
+ for i in '$( ls )'
+ echo item: list.sh
item: list.sh

Catch SSH exceptions in a UNIX script

I have a background process in my server which updates my ~/.ssh/authorized_keys frequently. If I ssh from my client machine at the very moment it will fail
$ ssh my_server date
SSH Version: OpenSSH_5.3p1
user#my_server's password:
and the ssh will mark the script as failed after a number of tries.
I want to break away and add an exception handling piece to sleep 30 seconds whenever this ssh failure occurs.
Something like
*ssh -o 'StrictHostKeyChecking no' appsrvr01.myserv.com "date" 2> /tmp/error
if [ $? -ne 0 ]
then
echo -e "\n Please wait..\n\n"
sleep 1s
else
echo -e "\n The Environment is ready to use!\n\n"
exit 0
fi*
Is there any better approach as the above snippet will still prompt for password
Maybe you could approach this in one shell script by "flock" on a lock file, and then "flock" in the shell script you run above:
In the script that updates your authorized keys:
(flock 200
# commands here that modify your authorized_keys file
) 200>/tmp/authkey_lock
And around the script piece you have posted above:
(flock 200
ssh -o 'StrictHostKeyChecking no' appsrvr01.myserv.com "date" 2> /tmp/error
if [ $? -ne 0 ]
then
echo -e "\n Please wait..\n\n"
sleep 1s
else
echo -e "\n The Environment is ready to use!\n\n"
exit 0
fi
) 200>/tmp/authkey_lock
Please see "man flock" for for information about flock.

Checking SSH failure in a script

Hi what is the best way to check to see if SSH fails for whatever reason?
Can I use a IF statement ( if it fails then do something)
I'm using the ssh command in a loop and passing my hosts names form a flat file.
so I do something like:
for i in `cat /tmp/hosts` ; do ssh $i 'hostname;sudo ethtool eth1'; done
I get sometime this error or I just cannot connect
ssh: host1 Temporary failure in name resolution
I want to skip the hosts that I cannot connect to is SSH fails. What is the best way to do this? Is there a runtime error I can trap to bypass the hosts that I cannot ssh into for whatever reason, perhaps ssh is not allowed or I do not have the right password ?
Thanking you in advance
Cheers
To check if there was a problem connecting and/or running the remote command:
if ! ssh host command
then
echo "SSH connection or remote command failed"
fi
To check if there was a problem connecting, regardless of success of the remote command (unless it happens to return status 255, which is rare):
if ssh host command; [ $? -eq 255 ]
then
echo "SSH connection failed"
fi
Applied to your example, this would be:
for i in `cat /tmp/hosts` ;
do
if ! ssh $i 'hostname;sudo ethtool eth1';
then
echo "Connection or remote command on $i failed";
fi
done
You can check the return value that ssh gives you as originally shown here:
How to create a bash script to check the SSH connection?
$ ssh -q user#downhost exit
$ echo $?
255
$ ssh -q user#uphost exit
$ echo $?
0
EDIT - I cheated and used nc
Something like this:
#!/bin/bash
ssh_port_is_open() { nc -z ${1:?hostname} 22 > /dev/null; }
for host in `cat /tmp/hosts` ; do
if ssh_port_is_open $host; then
ssh -o "BatchMode=yes" $i 'hostname; sudo ethtool eth1';
else
echo " $i Down"
fi
done

Resources