Check whether ssh is possible inside shell a script? - bash

I need to print a count of a remote server which is written on '/REMOTE_DIR/DR_count'. But that remote server is not much reliable due to a network or OS failure. However I need to print the DR_count value from the local machine if that remote machine is not available. Here is my logic. Please correct me to how to check that if condition in correct way. I'm running this script on Solaris 11.3.
#!/bin/sh
if[check wether ssh user#host_name is possible]
then
op="cat /REMOTE_DIR/DR_count"
cmd="ssh user#host_name $op"
drlog=`$cmd`
else
drlog='cat /LOCAL_DIR/DR_count'
fi
echo $drlog

As I said in my comment, I would simply try to ssh, and if use its exit code to see whether it worked:
ssh -o ConnectTimeout=5 user#host_name cat /REMOTE_DIR/DR_count 2>/dev/null || cat /LOCAL_DIR/DR_count

you should check for exit code 255 to detect whether you have network error or not
#!/bin/bash
#EXIT STATUS
# ssh exits with the exit status of the remote command or with 255 if an error occurred.
cnt=`ssh -o ConnectTimeout=5 root#$host "cat /REMOTE_DIR/DR_count"`
exit_code=$?
if [ $exit_code -eq 255 ]; then
cnt=`cat /LOCAL_DIR/DR_count`
fi
it makes sense also to check for other (non 0 / 255) exit codes to check possible issues at remote side (like file missing on remote side)

Related

How to check remote servers can ping each other using a script?

I need to write a script which checks whether 3 servers can ping each other. I run the script at my local linux host.
Here is what I plan to do:
ssh root#10.238.155.155 "ping -c 1 10.20.77.1"
echo $?
0
In the above example, 10.238.155.155 is one server, the command login to this server and ping 10.20.77.1 which is an interface at another server.
Then I check the command return value, $?, if it is 0, then it means ping is good.
ssh root#10.238.155.155 "ping -c 1 10.20.77.9"
echo $?
1
In this example, 10.20.77.9 does not exist, we can see $? is 1.
My script basically repeats running SSH login to each server, ping other servers and checks $?.
Do you think this is a reliable solution?
With echo $? you are checking the return of ssh, which is not what you want.
Try capturing the output of the compound ssh command in a variable, then parsing that variable as needed
myvar=$(ssh root#place.com "ping -c 5 server1.place.com")

Why would this script work on Mac OS and not on Windows using Git Bash?

I wrote a site diagnostic script and have been testing it using iTerm2 on MacOS. A good portion of people that may consume this script use Windows and Git Bash (it's probably 50|50 Windows vs Mac OS): so not supporting Windows might not be an option.
I had someone here test out the script on their Windows PC and it immediately failed. Would anyone know why this function would succeed on Mac and fail on Windows?
_ping_test(){
# Define Error Codes & Messages Below:
local _success="0"
local _success_message="${_green}SUCCESS:${_reset_color} Ping Test Passed"
local _unknown_host="68"
local _unknown_host_message="${_red}ERROR:${_reset_color} Ping Test Failed on ${_url} - Unknown Host"
local _unrecognized_code="${_red}ERROR:${_reset_color} Ping Test Failed - Unrecognized Status Code${_ping_test} "
local _url="${1}.testdomain.net"
local _ping_url=$(ping -c 1 -q $_url &> /dev/null; echo $?)
if [ "${_ping_url}" == "${_success}" ] ; then
echo $_success_message
elif [ "${_ping_url}" == "${_unknown_host}" ] ; then
echo $_unknown_host_message
exit 1
elif true ; then
echo $_unrecognized_code
exit 1
fi
}
Running it returns the Unrecognized Code response on his computer, but given the same site is successful on my Mac.
I echo'd back $_url, and that returns the correct site. I also tried removing the &> /dev/null portion to hear back what the issue was, but it returned that it didn't recognize the command ping
Then I just ran the ping command directly in his Git Bash window, and that worked outside of the shell script.
I realize the ping test portion is a bit silly (it's used to exit the script if they input a bad URL), but the more valuable ssh commands that run later were also failing similarly
I managed to grab a Windows PC and TiL that the parameters you pass to ping follow a different paradigm based on OS:
-c means count on Mac OS
but
-c means compartmentalize on Windows which required admin access
fixed the params and then it worked

Optimistic way to test port before executing sftp

I have a bash script which is doing very plain sftp to transfer data to production and uat servers. See my code below.
if [ `ls -1 ${inputPath}|wc -l` -gt 0 ]; then
sh -x wipprod.sh >> ${sftpProdLog}
sh -x wipdev.sh >> ${sftpDevLog}
sh -x wipdevone.sh >> ${sftpDevoneLog}
fi
sometimes the UAT server may go down. In those cases the number of scripts hanged are getting increased. If it reaches the user max. number of process the other scripts also getting affected. So I am thinking before executing each of the above script i have to test the port 22 availability on the destination server. Then I can execute the script.
Is this the right way? If yes what is the optimistic way to do that? If no what else is the best approach to avoid unnecessary sftp connection when destination not available? Thanks in advance.
Use sftp in batch mode together with ConnectTimeout option explicitely set. Sftp will take care about up/down problems by itself.
Note, that ConnectTimeout should be slightly higher if your network is slow.
Then put sftp commands into your wip*.sh backup scripts.
If UAT host is up:
[localuser#localhost tmp]$ sftp -b - -o ConnectTimeout=1 remoteuser#this_host_is_up <<<"put testfile.xml /tmp/"; echo $?
sftp> put testfile.xml /tmp/
Uploading testfile.xml to /tmp/testfile.xml
0
File is uploaded, sftp exits with exit code 0.
If UAT host is down, sftp exits wihin 1 second with exit code 255.
[localuser#localhost tmp]$ sftp -b - -o ConnectTimeout=1 remoteuser#this_host_is_down <<<"put testfile.xml /tmp/"; echo $?
ssh: connect to host this_host_is_down port 22: Connection timed out
Couldn't read packet: Connection reset by peer
255
It sounds reasonable - if the server is inaccessible you want to immediately report an error and not try and block.
The question is - why does the SFTP command block if the server is unavailable? If the server is down, then I'd expect the port open to fail almost immediately and you need only detect that the SFTP copy has failed and abort early.
If you want to detect a closed port in bash, you can simply as bash to connect to it directly - for example:
(echo "" > /dev/tcp/remote-host/22) 2>/dev/null || echo "failed"
This will open the port and immediately close it, and report a failure if the port is closed.
On the other hand, if the server is inaccessible because the port is blocked (in a firewall, or something, that drops all packets), then it makes sense for your process to hang and the base TCP test above will also hang.
Again this is something that should probably be handled by your SFTP remote copy using a timeout parameter, as suggested in the comments, but a bash script to detect blocked port is also doable and will probably look something like this:
(
(echo "" > /dev/tcp/remote-host/22) &
pid=$!
timeout=3
while kill -0 $pid 2>/dev/null; do
sleep 1
timeout=$(( $timeout - 1 ))
[ "$timeout" -le 0 ] && kill $pid && exit 1
done
) || echo "failed"
(I'm going to ignore the ls ...|wc business, other than to say something like find and xargs --no-run-if-empty are generally more robust if you have GNU find, or possibly AIX has an equivalent.)
You can perform a runtime connectivity check, OpenSSH comes with ssh-keyscan to quickly probe an SSH server port and dump the public key(s), but sadly it doesn't provide a usable exit code, leaving parsing the output as a messy solution.
Instead you can do a basic check with a bash one-liner:
read -t 2 banner < /dev/tcp/127.0.0.1/22
where /dev/tcp/127.0.0.1/22 (or /dev/tcp/hostname/ssh) indicates the host and port to connect to.
This relies on the fact that the SSH server will return an identifying banner terminated with CRLF. Feel free to inspect $banner. If it fails after the indicated timeout read will receive SIGALARM (exit code 142), and connection refused will result in exit code 1.
(Support for /dev/tcp and network redirection is enabled by default since before bash-2.05, though it can be disabled explicitly with --disable-net-redirections or with --enable-minimal-config at build time.)
To prevent such problems, an alternative is to set a timeout: with any of the ssh, scp or sftp commands you can set a connection timeout with the option -o ConnectTimeout=15, or, implicitly via ~/.ssh/config:
Host 1.2.3.4 myserver1
ConnectionTimeout 15
The commands will return non-zero on timeout (though the three commands may not all return the same exit code on timeout). See this related question: how to make SSH command execution to timeout
Finally, if you have GNU parallel you may use its sem command to limit concurrency to prevent this kind of problem, see https://unix.stackexchange.com/questions/168978/limit-maximum-number-of-concurrent-scp-processes-running-on-a-host .

Bash Script Quits After Exiting SSH

I'm trying to write a Bash script that logs into 2 different linux based power-strips (Ubiquiti Mpower Pros) and turns 2 different lights off (one on each strip). To do this I login to the 1st strip, change the appropriate file to 0 (thus turning off the light), and exit, repeating the same process on the next power-strip. However, after I exit the first SSH connection, the script stops working. Could someone please suggest a fix? My only idea would be to encase this script in a python program. Here's my code:
#!/bin/bash
ssh User#192.168.0.100
echo "0" > /proc/power/relay1
exit
# hits the enter key
cat <(echo "") | <command>
ssh User#192.168.0.103
echo "logged in"
echo "0" > /proc/power/relay1
exit
cat <(echo "") | <command>
ssh as an app BLOCKS while it's running, the echo and exit are executed by the local shell, not by the remote machine. so you are doing:
ssh to remote machine
exit remote shell
echo locally
exit locally
and boom, your script is dead. If that echo/exit is supposed to be run on the remote system, then you should be doing:
ssh user#host command
^^^^^---executed on the remote machine
e.g.
ssh foo#bar 'echo ... ; exit'
The commands you're apparently trying to run through ssh are actually being executed locally. You can just pass the command you want to run to ssh and it will do it (without needing an explicit exit)
ssh User#192.168.0.110 'echo "0" > /proc/power/relay1'
will do that, and similar for the other ssh command

Fix ssh line for Xpra bash script

im writting a small script for starting an 'xpra' session with a remote machine. I'm pretty new to bash scripting, so I was wondering if someone could help me clean up the code a bit, concerning best practices.
The ssh line is the one i'm having problems with, as I must CTRL-C on the keyboard for the command to be killed and let it continue to echo "done".
How can I fix that minor issue?
### ###
# syntax: xpra.sh hostmachine command #
## ###
## Wake on LAN host machine.
~/scripts/$1
## Check if online and ssh command.
## Attach xpra client.
while :; do
ping -c 1 $1
if [ $? -eq 0 ]; then
ssh $1 "xpra start :7 && sleep 2 && ("DISPLAY=:7 $2"&) ";
echo "done";
sleep 5;
echo "attaching";
(xpra attach ssh:$1:7 &);
break;
else
echo "host offline";
sleep 180s;
fi
done
Newer versions support starting remote sessions in one command, try:
xpra start ssh://HOST:SSH_PORT/DISPLAY --start-child=xterm
this will
start a new remote session on display DISPLAY
start an xterm in it
then connect your client to this session
It's a lot cleaner than a script that relies on "sleep"...
Either you want the ssh line to finish before moving to the next line, in which case what you have is correct; or you want to move on to the next line while it is still running, in which case you can append a "&" character to the line:
ssh $1 "xpra start :7 && sleep 2 && ("DISPLAY=:7 $2"&) " &
(Main comment I would make about your style is that ending all your lines with ";"'s is unnecessary, and it would be clearer if you indented the parts of your if statement.)
as Adam Liss mentioned in the comments:
ssh -f $COMMAND
will open an ssh session, ask for your credentials, then go into the background as it launches the command on the remote host.

Resources