Bash Script to check for host - bash

I have a complicated script request that I am trying to figure out. Basically, I need a script that will check for certain conditions and depending on the outcome will run certain commands.
So, I need it to check if $hostip is up and running. If it is running, I need it to check and see if $domain has same IP address as $domainip. If they match, script then ends. If they don't match, I need to run command "shutdown $hostip". Now, if it fails to find $domain, ie internet is down, then I need it to sleep for 6 hours and then try again. I want it to try 3 times and if still can't find domain after 18 hours, then run "shutdown $hostip".
The goal above is to shutdown a server if the domain does not match the set IP or the internet is disconnected for 18 hours.
I also need script to check the other side of things as well. So, if $hostip is not online, but $domain and $domainip do match, then power on server.
These are my variables I have so far.
domain=google.com
domainip=216.58.194.46
hostip=192.168.1.1
gatewayip=$(ping -q -c 1 -t 1 $domain | grep PING | sed -e "s/).*//" | sed -e "s/.*(//")

So there are two parts to this.
First, you need to check if the hosts is up. You can do this with ping as you have done:
ping -q -c 1 "$domain" >/dev/null 2>&1
which will set its exit code to true or false as expected if the host is up or down. Note I've removed the -t option as it was causing the ping to fail.
Second, you need to check that $domain resolves to $domainip. You can do this with several tools - host is straightforward:
host -t a $domain | grep -q "$domain has address $domainip"
Putting that together you get something like:
domain=google.com
domainip=216.58.194.46
hostip=192.168.1.1
for i in {1..3}; do
sleep 6h
if ping -q -c 1 "$domain" 2>/dev/null; then
exit 0
fi
if host -t a $domain | grep -q "$domain has address $domainip"; then
exit 0
fi
done
shutdown $hostip

Related

How to check whether tomcat server is started up

I want to check if tomcat server is really started up. When you start tomcat, you get an entry like "Server startup" in catalina.out. Once I got this, the script should go ahead.
That's my code
echo "Waiting for Tomcat"
if [ $(tail -f /home/0511/myapp/logs/catalina.out | grep "Server startup" | wc -l) -eq 1 ]; then
echo "Tomcat started"
fi
#further code...
Output:
Waiting for Tomcat
|
So, I am sure, after 30-60 seconds, the "tail .. | wc -l" gets 1. However, I cannot match it with my code above. Nothing happens, I have to interrupt per CTRL C.
What is wrong or is there any better solution for my intention?
Try this:
while true;do
if tail -n 100 /home/0511/myapp/logs/catalina.out | grep -q "Server
startup";then
echo "Tomcat started"
exit 0
fi
done
So you constantly check the last 100 lines from the log, and if match, exit with a message.
Another (more elegant) solution without a loop:
if tail -f /home/0511/myapp/logs/catalina.out | grep -q "Server
startup";then
echo "Tomcat started"
fi
You said "I want to check if tomcat server is really started up". If you check the log with tail and grep, in worst case scenario, you could detect an old start which ended with a crash.
Tomcat server when is started it is listening to a certain port(e.g. 8080). So you should check if tomcat server is listening on that port.
If you are using a different port replace 8080 in following lines with your custom port.
In order to display tomcat status you should use netstat. Example of a line returned by netstat:
TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING
In order to display only if Tomcat is started or not, you could use:
netstat -an|grep -e ":8080[^0-9].*[0-9].*LISTENING" && echo "Tomcat started" || echo "Tomcat not started"
The grep expression matches ":" followed by port 8080, followed by non-digit, followed by any characters, followed by digit, followed by any characters, followed by "LISTENING".
In order to wait for Tomcat you could use:
echo Waiting for Tomcat port to be ...
until netstat -an | grep -e ":8080[^0-9].*[0-9].*LISTENING" > /dev/null ; do
sleep 1 ;
done
echo Tomcat started
grep doesn't exit as soon as it found a match; it keeps reading for further matches.
To make grep not produce any output, but instead exit with 0 status as soon as a match is found, use the -q option:
if tail -f /home/0511/myapp/logs/catalina.out | grep -q "Server startup"; then
That said, a log message isn't the most reliable way to check if a server is actually up and serving. Instead, you could try, for example, repeatedly polling it with curl instead.

Cannot exit bash script at the end of the for cycle

I wrote a simple IP scanner based on ping (see below), but it has a problem.
#!/bin/bash
counter = 0
for ip in 192.168.44.{1..254}; do
ping -c 1 -W 1 $ip | grep "64 bytes" &
let counter++
if [[ "$counter" -eq 254 ]];
then
exit 0;
fi
done
First of all, the for cycle appears to be launching multiple threads and the only output to the terminal are the ping answers. However, when the script finishes pinging all the machines in the network, it never exits, as you can see in the next screenshot:
and I have to press Enter to finally end it.
I've also tried to place an exit 0 after the done statement, but it still does not work. How can I make the script exit when the for cycle ends?
Note: I've found this implementation to be the fastest to find the existing machines in a LAN, but if anyone has a suggestion of a more appropriate code, I would appreciate it.
Assuming that you do not want to use a dedicated network scanning tools, You can use bash or xargs to iterate over all addresses in parallel:
Bash:
#! /bin/bash -x
for ip in 192.168.44.{1..254}; do
ping -c 1 -W 1 $ip | grep "64 bytes" &
done
# Wait for all children to finish
wait
Or with xargs, with the advantage that you can control the number of parallel addresses being pinged (20 in this example) - to avoid overloading your server with large number of concurrent process.
echo 192.168.44.{1..254} | xargs --max-args=1 -P20 ping -c 1 -W 1
You can try this:
#!/bin/bash
for ip in 192.168.44.{1..254}; do
ping -c 1 -W 1 $ip | grep "64 bytes" &
done
wait
If CTRLc is acceptable:
parallel -j0 --lb ping ::: 192.168.1.{1..250}

sshpass exit in automation

I have total of 6 IP addresses and out of the 6 only 2 IP addresses are valid. I wrote a shell script to use sshpass to test each IP.
The issue is when script reaches IP that is working it log's in the system (Cisco switch) and stays there and not continuing with the loop to test the remaining IPs. If i type "exit" on the system than it continues with the loop.
After a successful login how can script automatically get out, from logged system, and continue with testing remaining IP?
/usr/bin/sshpass -p $ADMINPASS ssh -oStrictHostKeyChecking=no -oCheckHostIP=no -t $ADMINLOGIN#$IP exit
i can use the exit status to figure out which IP worked and which on didn't work.
Test first if IP is alive, and then 'ssh' on it, could help you.I don't know if you are using a loop or not, but loop can be a good choice.Should look like : for f in ip-1 ip-2 ip-3 ip-4 ip-5 ip-6; do ping -c 1 -w 3 $f; if [ $? -eq 0 ]; then echo OK; ssh_pass $f your_command; else echo " IP is NOK"; fi; done
You can then also add 'exit' command, depending on what you test : 'exit 0' if it is OK, after you 'ssh' command, 'exit 1' if NOK.

Modify shell script to monitor/ping multiple ip addresses

Alright so I need to constantly monitor multiple routers and computers, to make sure they remain online. I have found a great script here that will notify me via growl(so i can get instant notifications on my phone) if a single ip cannot be pinged. I have been attempting to modify the script to ping multiple addresses, with little luck. I'm having trouble trying to figure out how to ping a down server while the script keeps watching the online servers. any help would be greatly appreciated. I haven't done much shell scripting so this is quite new to me.
Thanks
#!/bin/sh
#Growl my Router alive!
#2010 by zionthelion73 [at] gmail . com
#use it for free
#redistribute or modify but keep these comments
#not for commercial purposes
iconpath="/path/to/router/icon/file/internet.png"
# path must be absolute or in "./path" form but relative to growlnotify position
# document icon is used, not document content
# Put the IP address of your router here
localip=192.168.1.1
clear
echo 'Router avaiability notification with Growl'
#variable
avaiable=false
com="################"
#comment prefix for logging porpouse
while true;
do
if $avaiable
then
echo "$com 1) $localip avaiable $com"
echo "1"
while ping -c 1 -t 2 $localip
do
sleep 5
done
growlnotify -s -I $iconpath -m "$localip is offline"
avaiable=false
else
echo "$com 2) $localip not avaiable $com"
#try to ping the router untill it come back and notify it
while !(ping -c 1 -t 2 $localip)
do
echo "$com trying.... $com"
sleep 5
done
echo "$com found $localip $com"
growlnotify -s -I $iconpath -m "$localip is online"
avaiable=true
fi
sleep 5
done
The simplest approach is to wrap this script with another one that creates N processes. Assume your script is called "watchip", then put into another script the text
watchip 10.0.1.1 &
watchip 10.0.1.2 &
watchip 10.0.1.3 &
etc
and set localip to $1 inside watchip.
I don't think it's necessary to run multiple scripts. Here is a general script to monitor a list of IP addresses and note changes in ping success...
#!/bin/bash
set 10.0.0.1 10.0.0.2 # etc
trap exit 2
while true; do
i=1
for ipnumber in "$#"; do
statusname=up$i
laststatus=${!statusname:-0}
ping -c 1 -t 2 $ipnumber > /dev/null
ok=$?
eval $statusname=$ok
if [ ${!statusname} -ne $laststatus ]; then
echo status changed for $ipnumber
if [ $ok -eq 0 ]; then
echo now it is up
else
echo now it is down
fi
fi
i=$(($i + 1))
done
sleep 5
done
Change localip=192.168.1.1 to:
localip=$1
This allows the IP address to be passed in as a command-line argument. Then you can run multiple copies of the script passing in different IP addresses. You could then create a master script to run multiple copies of the monitoring script. Assuming the script you posted is monitor.sh:
#!/bin/sh
monitor.sh 192.168.1.1 &
monitor.sh 192.168.2.2 &
monitor.sh 192.168.3.3 &
wait
Keep two arrays. One with available IPs; the other with unavailable ones. When their status changes, move them to the other array. No need for multiple background processes.
I've omitted the logging stuff. You can add it back in. This is untested code.
available=(192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.4)
unavailable=()
while true
do
for index in ${!available[#]}
do
if ! ping -c 1 -t 2 ${available[index]}
then
growlnotify -s -I $iconpath -m "${available[index]} is offline"
unavailable+=(${available[index]})
unset "available[index]"
fi
done
for index in ${!unavailable[#]}
do
if ping -c 1 -t 2 ${unavailable[index]}
then
growlnotify -s -I $iconpath -m "${unavailable[index]} is back online"
available+=(${unavailable[index]})
unset "unavailable[index]"
fi
done
done

How do I kill a backgrounded/detached ssh session?

I am using the program synergy together with an ssh tunnel
It works, i just have to open an console an type these two commands:
ssh -f -N -L localhost:12345:otherHost:12345 otherUser#OtherHost
synergyc localhost
because im lazy i made an Bash-Script which is run with one mouseclick on an icon:
#!/bin/bash
ssh -f -N -L localhost:12345:otherHost:12345 otherUser#OtherHost
synergyc localhost
the Bash-Script above works as well, but now i also want to kill synergy and the ssh tunnel via one mouseclick, so i have to save the PIDs of synergy and ssh into file to kill them later:
#!/bin/bash
mkdir -p /tmp/synergyPIDs || exit 1
rm -f /tmp/synergyPIDs/ssh || exit 1
rm -f /tmp/synergyPIDs/synergy || exit 1
[ ! -e /tmp/synergyPIDs/ssh ] || exit 1
[ ! -e /tmp/synergyPIDs/synergy ] || exit 1
ssh -f -N -L localhost:12345:otherHost:12345 otherUser#OtherHost
echo $! > /tmp/synergyPIDs/ssh
synergyc localhost
echo $! > /tmp/synergyPIDs/synergy
But the files of this script are empty.
How do I get the PIDs of ssh and synergy?
(I try to avoid ps aux | grep ... | awk ... | sed ... combinations, there has to be an easier way.)
With all due respect to the users of pgrep, pkill, ps | awk, etc, there is a much better way.
Consider that if you rely on ps -aux | grep ... to find a process you run the risk of a collision. You may have a use case where that is unlikely, but as a general rule, it's not the way to go.
SSH provides a mechanism for managing and controlling background processes. But like so many SSH things, it's an "advanced" feature, and many people (it seems, from the other answers here) are unaware of its existence.
In my own use case, I have a workstation at home on which I want to leave a tunnel that connects to an HTTP proxy on the internal network at my office, and another one that gives me quick access to management interfaces on co-located servers. This is how you might create the basic tunnels, initiated from home:
$ ssh -fNT -L8888:proxyhost:8888 -R22222:localhost:22 officefirewall
$ ssh -fNT -L4431:www1:443 -L4432:www2:443 colocatedserver
These cause ssh to background itself, leaving the tunnels open. But if the tunnel goes away, I'm stuck, and if I want to find it, I have to parse my process list and home I've got the "right" ssh (in case I've accidentally launched multiple ones that look similar).
Instead, if I want to manage multiple connections, I use SSH's ControlMaster config option, along with the -O command-line option for control. For example, with the following in my ~/.ssh/config file,
host officefirewall colocatedserver
ControlMaster auto
ControlPath ~/.ssh/cm_sockets/%r#%h:%p
the ssh commands above, when run, will leave spoor in ~/.ssh/cm_sockets/ which can then provide access for control, for example:
$ ssh -O check officefirewall
Master running (pid=23980)
$ ssh -O exit officefirewall
Exit request sent.
$ ssh -O check officefirewall
Control socket connect(/home/ghoti/.ssh/cm_socket/ghoti#192.0.2.5:22): No such file or directory
And at this point, the tunnel (and controlling SSH session) is gone, without the need to use a hammer (kill, killall, pkill, etc).
Bringing this back to your use-case...
You're establishing the tunnel through which you want syngergyc to talk to syngergys on TCP port 12345. For that, I'd do something like the following.
Add an entry to your ~/.ssh/config file:
Host otherHosttunnel
HostName otherHost
User otherUser
LocalForward 12345 otherHost:12345
RequestTTY no
ExitOnForwardFailure yes
ControlMaster auto
ControlPath ~/.ssh/cm_sockets/%r#%h:%p
Note that the command line -L option is handled with the LocalForward keyword, and the Control{Master,Path} lines are included to make sure you have control after the tunnel is established.
Then, you might modify your bash script to something like this:
#!/bin/bash
if ! ssh -f -N otherHosttunnel; then
echo "ERROR: couldn't start tunnel." >&2
exit 1
else
synergyc localhost
ssh -O exit otherHosttunnel
fi
The -f option backgrounds the tunnel, leaving a socket on your ControlPath to close the tunnel later. If the ssh fails (which it might due to a network error or ExitOnForwardFailure), there's no need to exit the tunnel, but if it did not fail (else), synergyc is launched and then the tunnel is closed after it exits.
You might also want to look in to whether the SSH option LocalCommand could be used to launch synergyc from right within your ssh config file.
Quick summary: Will not work.
My first idea is that you need to start the processes in the background to get their PIDs with $!.
A pattern like
some_program &
some_pid=$!
wait $some_pid
might do what you need... except that then ssh won't be in the foreground to ask for passphrases any more.
Well then, you might need something different after all. ssh -f probably spawns a new process your shell can never know from invoking it anyway. Ideally, ssh itself would offer a way to write its PID into some file.
just came across this thread and wanted to mention the "pidof" linux utility:
$ pidof init
1
You can use lsof to show the pid of the process listening to port 12345 on localhost:
lsof -t -i #localhost:12345 -sTCP:listen
Examples:
PID=$(lsof -t -i #localhost:12345 -sTCP:listen)
lsof -t -i #localhost:12345 -sTCP:listen >/dev/null && echo "Port in use"
well i dont want to add an & at the end of the commands as the connection will die if the console wintow is closed ... so i ended up with an ps-grep-awk-sed-combo
ssh -f -N -L localhost:12345:otherHost:12345 otherUser#otherHost
echo `ps aux | grep -F 'ssh -f -N -L localhost' | grep -v -F 'grep' | awk '{ print $2 }'` > /tmp/synergyPIDs/ssh
synergyc localhost
echo `ps aux | grep -F 'synergyc localhost' | grep -v -F 'grep' | awk '{ print $2 }'` > /tmp/synergyPIDs/synergy
(you could integrate grep into awk, but im too lazy now)
You can drop the -f, which makes it run it in background, then run it with eval and force it to the background yourself.
You can then grab the pid. Make sure to put the & within the eval statement.
eval "ssh -N -L localhost:12345:otherHost:12345 otherUser#OtherHost & "
tunnelpid=$!
Another option is to use pgrep to find the PID of the newest ssh process
ssh -fNTL 8073:localhost:873 otherUser#OtherHost
tunnelPID=$(pgrep -n -x ssh)
synergyc localhost
kill -HUP $tunnelPID
This is more of a special case for synergyc (and most other programs that try to daemonize themselves). Using $! would work, except that synergyc does a clone() syscall during execution that will give it a new PID other than the one that bash thought it has. If you want to get around this so that you can use $!, then you can tell synergyc to stay in the forground and then background it.
synergyc -f -n mydesktop remoteip &
synergypid=$!
synergyc also does a few other things like autorestart that you may want to turn off if you are trying to manage it.
Based on the very good answer of #ghoti, here is a simpler script (for testing) utilising the SSH control sockets without the need of extra configuration:
#!/bin/bash
if ssh -fN -MS /tmp/mysocket -L localhost:12345:otherHost:12345 otherUser#otherHost; then
synergyc localhost
ssh -S /tmp/mysocket -O exit otherHost
fi
synergyc will be only started if tunnel has been established successfully, which itself will be closed as soon as synergyc returns.
Albeit the solution lacks proper error reporting.
You could look out for the ssh proceess that is bound to your local port, using this line:
netstat -tpln | grep 127\.0\.0\.1:12345 | awk '{print $7}' | sed 's#/.*##'
It returns the PID of the process using port 12345/TCP on localhost. So you don't have to filter all ssh results from ps.
If you just need to check, if that port is bound, use:
netstat -tln | grep 127\.0\.0\.1:12345 >/dev/null 2>&1
Returns 1 if none bound or 0 if someone is listening to this port.
There are many interesting answers here, but nobody mentioned that the manpage of SSH does describe this exact case! (see TCP FORWARDING section). And the solution they offer is much simpler:
ssh -fL 12345:localhost:12345 user#remoteserver sleep 10
synergyc localhost
Now in details:
First we start SSH with a tunnel; thanks to -f it will initiate the connection and only then fork to background (unlike solutions with ssh ... &; pid=$! where ssh is sent to background and next command is executed before the tunnel is created). On the remote machine it will run sleep 10 which will wait 10 seconds and then end.
Within 10 seconds, we should start our desired command, in this case synergyc localhost. It will connect to the tunnel and SSH will then know that the tunnel is in use.
After 10 seconds pass, sleep 10 command will finish. But the tunnel is still in use by synergyc, so SSH will not close the underlying connection until the tunnel is released (i.e. until synergyc closes socket).
When synergyc is closed, it will release the tunnel, and SSH in turn will terminate itself, closing a connection.
The only downside of this approach is that if the program we use will close and re-open connection for some reason then SSH will close the tunnel right after connection is closed, and the program won't be able to reconnect. If this is an issue then you should use an approach described in #doak's answer which uses control socket to properly terminate SSH connection and uses -f to make sure tunnel is created when SSH forks to the background.

Resources