check for device connectivity on network then send action based on connectivity - bash

I am fairly new to bash script even though I have some experience.
I am looking for my Raspberry Pi to detect my phone when it becomes available on the network, when it does so to play an audio clip, I have managed do this via the script below.
I have a problem, however, when my phone becomes available on the network, I do not want the audio to keep looping; I need it to play once and then stop playing the audio clip once it has already played. However, I do want the script to keep on running so it can detect the next time my phone becomes available on the network.
Maybe there is a better way of doing it, if there is I would love to hear your suggestions.
#!/bin/sh
if ping -c 10 192.168.1.4 &> /dev/null
then
kodi-send --action="PlayMedia(/storage/music/welcome.mp3)"
ping 192.168.1.4 &> /dev/null
else
./checkforerikphone.sh
fi

try this
#!/bin/bash
while : ; do
if ping -c 10 192.168.1.4 &> /dev/null ; then
kodi-send --action="PlayMedia(/storage/music/welcome.mp3)"
fi
sleep 600
done
This solution runs forever while :. and every 10 minutes, checks if your phone is active. So this significantly reduces the noise in your life, but it also lets you know that your phone is still connected.
You could change sleep 600 to sleep 300 and check every 5 minutes, or of course you can change 600 to any number of seconds you comforatable with.
Not a perfect solution per your spec, but managing lock files can be complicated.
Get comfortable with this solution and then think about adding something like
if ping ... ; then
if ! [[ -e /tmp/phoneOnLine ]] ; then
kodi-send ...
echo "Found phone at $(date)" > /tmp/phoneOnLine
fi
else
echo "no phone found"
/bin/rm -f /tmp/phoneOnLine
fi
You will definitely find corner cases where this doesn't work, so be prepared to debug the code. I would add an echo msg inside of each logic path (if/else/...). to understand how the code is working.
Also to prevent script for being faked out, I would delete the file at startup.
So a possible complete solution is
#!/bin/bash
#file may not exist, ignore error msg with "2> /dev/null"
/bin/rm -f /tmp/phoneOnLine 2> /dev/null
#loop forever
while : ; do
# check for phone
if ping -c 10 192.168.1.4 &> /dev/null ; then
# check for "lock" file
if ! [[ -e /tmp/phoneOnLine ]] ; then
kodi-send --action="PlayMedia(/storage/music/welcome.mp3)"
echo "Found phone at $(date)" > /tmp/phoneOnLine
else
echo "Phone already found"
fi # !! -e /tmp/ph
else # no ping
echo "no phone found"
/bin/rm -f /tmp/phoneOnLine 2>/dev/null
fi # ping
sleep 600
done

Try following:
#!/bin/bash
#when result of ping $? is 0, the phone is detected
ACTIVE=0
#default startup as NOT ACTIVE(not detected) => !0
pre_available=1
# loop forever
while :
do
#ping and store outcome in "available"
ping -c 10 192.168.0.8 > /dev/null
available=$?
#if phone was not active and just got active (detected)
if [[ "$pre_available" != "$ACTIVE" && "$available" == "$ACTIVE" ]]
then
#do your stuff
kodi-send --action="PlayMedia(/storage/music/welcome.mp3)"
fi
#store availability for next iteration
pre_available=$available
done

Related

Creating a shell script to check network connectivity

I'm making a simple shell script that runs an infinite loop, then if the output of the ping command contains "time" (indicating that it pinged successfully) it should echo "Connected!", sleep 1, and clear. However, I get no output from my script.
#!/bin/bash
while :
do
if [[ $(ping google.com) == *time* ]];
then
echo -en '\E[47;32m'"\033[1mS\033[0m"
echo "Connected!"
else
echo -en '\E[47;31m'"\033[1mZ\033[0m"
echo "Not Connected!"
fi
clear
sleep 1
done
Your script is not giving output because ping never terminates. To get ping to test your connectivity, you'll want to give it a run count (-c) and a response timeout (-W), and then check its return code:
#!/bin/bash
while true
do
if ping -c 1 -W 5 google.com 1>/dev/null 2>&1
then
echo "Connected!"
break
else
echo "Not Connected!"
sleep 1
fi
done
ping will return 0 if it is able to ping the given hostname successfully, and nonzero otherwise.
It's also worth noting that an iteration of this loop will run for a different period of time depending on whether ping succeeds quickly or fails, for example due to no network connection. You may want to keep the iterations to a constant length of time -- like 15 seconds -- using time and sleep.

Ping Tool to check if server is online

Is this tool that I created from various SOF threads valid? Will it work? I want to have a ping test done to a server every minute. If it fails 5 times in a row it sends an email out. It then flushes and resets the script pretty much to check again.
#!/bin/bash
# ping checker tool
numOfFails=0
incrememnt=1
EMAILMESSAGE="/tmp/emailmessage.txt"
while true; do
if ! ping -c 1 google.com ; then #if ping exits nonzero...
numOfFails=$(($num + $increment))
else
numOfFails=0
fi
if ((numOfFails > 4)); then
numOfFails=0
echo "SAN is offline!" > $EMAILMESSAGE
mail -s "SAN offline" "test#test.com" < $EMAILMESSAGE
fi
sleep 60 #check again in one minute
done
Your code won't work at all, this is a revised version:
#!/bin/bash
# ping checker tool
FAILS=0
EMAIL_ADDRESS="example#example.com"
SERVER="192.168.1.1"
SLEEP=60
while true; do
ping -c 1 $SERVER >/dev/null 2>&1
if [ $? -ne 0 ] ; then #if ping exits nonzero...
FAILS=$[FAILS + 1]
else
FAILS=0
fi
if [ $FAILS -gt 4 ]; then
FAILS=0
echo "Server $SERVER is offline!" \
| mail -s "Server offline" "$EMAIL_ADDRESS"
fi
sleep $SLEEP #check again in SLEEP seconds
done
Change example#example.com and 192.168.1.1 for your email address and the IP address of the server you are testing. I recommend using and IP address instead of a hostname to prevent mixing name resolution errors with connection errors.
Please be advised that although this will work I would recommend running a slightly different script from cron instead of having it running continuously like you seem to want, when running from cron you would not need to monitor that the script is running since if it stops for some reason the monitoring of the server stops as well.
Something like this run from crontab every minute.
#!/bin/bash
# ping checker tool
TMP_FILE="/tmp/ping_checker_tool.tmp"
if [ -r $TMP_FILE ]; then
FAILS=`cat $TMP_FILE`
else
FAILS=0
fi
EMAIL_ADDRESS="example#example.com"
SERVER="192.168.1.1"
ping -c 1 $SERVER >/dev/null 2>&1
if [ $? -ne 0 ] ; then #if ping exits nonzero...
FAILS=$[FAILS + 1]
else
FAILS=0
fi
if [ $FAILS -gt 4 ]; then
FAILS=0
echo "Server $SERVER is offline!" \
| mail -s "Server offline" "$EMAIL_ADDRESS"
fi
echo $FAILS > $TMP_FILE
Consider using Pingdom. It provides this service for you.
One thing you have not considered is once your site goes down, you will continue to get email messages every minute, until the site is up again, or until you stop this script.
A good approach is to switch states from reporting when the site is down to reporting when the site is up, once you have detected that it is down. And then back again, once it is back up.
Essentially you one receive an email reporting 'site down', then another later on, hopefully, reporting 'site is up'.
Pingdom does this for you, very nicely.
I have been investigating how to do this so that I can activate/deactivate services depending on whether my phone is at home. I have come up with the following:
#!/bin/bash
HOST_TO_CHECK=<hostname>
if ping -qc 20 $HOST_TO_CHECK >/dev/null; then
echo "Host $HOST_TO_CHECK is up"
else
echo "Host $HOST_TO_CHECK is down"
fi
Replace <hostname> with the host you wish to check.
The script will ping the host 20 times.
The reason it does this is that my mobile doesn't always respond to pings immediately.
Obviously you can replace the echo commands with something to actually do something useful :-)
You can then schedule the script to check every 5 minutes by adding it to your crontab:
*/5 * * * * /opt/pingcheck.sh

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

Making bash script to check connectivity and change connection if necessary. Help me improve it?

My connection is flaky, however I have a backup one. I made some bash script to check for connectivity and change connection if the present one is dead. Please help me improve them.
The scripts almost works, except for not waiting long enough to receive an IP (it cycles to next step in the until loop too quick). Here goes:
#!/bin/bash
# Invoke this script with paths to your connection specific scripts, for example
# ./gotnet.sh ./connection.sh ./connection2.sh
until [ -z "$1" ] # Try different connections until we are online...
do
if eval "ping -c 1 google.com"
then
echo "we are online!" && break
else
$1 # Runs (next) connection-script.
echo
fi
shift
done
echo # Extra line feed.
exit 0
And here is an example of the slave scripts:
#!/bin/bash
ifconfig wlan0 down
ifconfig wlan0 up
iwconfig wlan0 key 1234567890
iwconfig wlan0 essid example
sleep 1
dhclient -1 -nw wlan0
sleep 3
exit 0
Here's one way to do it:
#!/bin/bash
while true; do
if ! [ "`ping -c 1 google.com; echo $?`" ]; then #if ping exits nonzero...
./connection_script1.sh #run the first script
sleep 10 #give it a few seconds to complete
fi
if ! [ "`ping -c 1 google.com; echo $?`" ]; then #if ping *still* exits nonzero...
./connection_script2.sh #run the second script
sleep 10 #give it a few seconds to complete
fi
sleep 300 #check again in five minutes
done
Adjust the sleep times and ping count to your preference. This script never exits so you would most likely want to run it with the following command:
./connection_daemon.sh 2>&1 > /dev/null & disown
Have you tried omitting the -nw option from the dhclient command?
Also, remove the eval and quotes from your if they aren't necessary. Do it like this:
if ping -c 1 google.com > /dev/null 2>&1
Trying using ConnectTimeout ${timeout} somewhere.

How to test an Internet connection with bash?

How can an internet connection be tested without pinging some website?
I mean, what if there is a connection but the site is down? Is there a check for a connection with the world?
Without ping
#!/bin/bash
wget -q --spider http://google.com
if [ $? -eq 0 ]; then
echo "Online"
else
echo "Offline"
fi
-q : Silence mode
--spider : don't get, just check page availability
$? : shell return code
0 : shell "All OK" code
Without wget
#!/bin/bash
echo -e "GET http://google.com HTTP/1.0\n\n" | nc google.com 80 > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Online"
else
echo "Offline"
fi
Ping your default gateway:
#!/bin/bash
ping -q -w 1 -c 1 `ip r | grep default | cut -d ' ' -f 3` > /dev/null && echo ok || echo error
Super Thanks to user somedrew for their post here: https://bbs.archlinux.org/viewtopic.php?id=55485 on 2008-09-20 02:09:48
Looking in /sys/class/net should be one way
Here's my script to test for a network connection other than the loop back.
I use the below in another script that I have for periodically testing if my website is accessible. If it's NOT accessible a popup window alerts me to a problem.
The script below prevents me from receiving popup messages every five minutes whenever my laptop is not connected to the network.
#!/usr/bin/bash
# Test for network conection
for interface in $(ls /sys/class/net/ | grep -v lo);
do
if [[ $(cat /sys/class/net/$interface/carrier) = 1 ]]; then OnLine=1; fi
done
if ! [ $OnLine ]; then echo "Not Online" > /dev/stderr; exit; fi
Note for those new to bash: The final 'if' statement tests if NOT [!] online and exits if this is the case. See man bash and search for "Expressions may be combined" for more details.
P.S. I feel ping is not the best thing to use here because it aims to test a connection to a particular host NOT test if there is a connection to a network of any sort.
P.P.S. The Above works on Ubuntu 12.04 The /sys may not exist on some other distros. See below:
Modern Linux distributions include a /sys directory as a virtual filesystem (sysfs, comparable to /proc, which is a procfs), which stores and allows modification of the devices connected to the system, whereas many traditional UNIX and Unix-like operating systems use /sys as a symbolic link to the kernel source tree.[citation needed]
From Wikipedia https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard
This works on both MacOSX and Linux:
#!/bin/bash
ping -q -c1 google.com &>/dev/null && echo online || echo offline
In Bash, using it's network wrapper through /dev/{udp,tcp}/host/port:
if : >/dev/tcp/8.8.8.8/53; then
echo 'Internet available.'
else
echo 'Offline.'
fi
(: is the Bash no-op, because you just want to test the connection, but not processing.)
The top answer misses the fact that you can have a perfectly stable connection to your default gateway but that does not automatically mean you can actually reach something on the internet. The OP asks how he/she can test a connection with the world. So I suggest to alter the top answer by changing the gateway IP to a known IP (x.y.z.w) that is outside your LAN.
So the answer would become:
ping -q -w 1 -c 1 x.y.z.w > /dev/null && echo ok || echo error
Also removing the unfavored backticks for command substitution[1].
If you just want to make sure you are connected to the world before executing some code you can also use:
if ping -q -w 1 -c 1 x.y.z.w > /dev/null; then
# more code
fi
I've written scripts before that simply use telnet to connect to port 80, then transmit the text:
HTTP/1.0 GET /index.html
followed by two CR/LF sequences.
Provided you get back some form of HTTP response, you can generally assume the site is functioning.
make sure your network allow TCP traffic in and out, then you could get back your public facing IP with the following command
curl ifconfig.co
Execute the following command to check whether a web site is up, and what status message the web server is showing:
curl -Is http://www.google.com | head -1 HTTP/1.1 200 OK
Status code ‘200 OK’ means that the request has succeeded and a website is reachable.
The top voted answer does not work for MacOS so for those on a mac, I've successfully tested this:
GATEWAY=`route -n get default | grep gateway`
if [ -z "$GATEWAY" ]
then
echo error
else
ping -q -t 1 -c 1 `echo $GATEWAY | cut -d ':' -f 2` > /dev/null && echo ok || echo error
fi
tested on MacOS High Sierra 10.12.6
If your local nameserver is down,
ping 4.2.2.1
is an easy-to-remember always-up IP (it's actually a nameserver, even).
This bash script continuously check for Internet and make a beep sound when the Internet is available.
#!/bin/bash
play -n synth 0.3 sine 800 vol 0.75
while :
do
pingtime=$(ping -w 1 8.8.8.8 | grep ttl)
if [ "$pingtime" = "" ]
then
pingtimetwo=$(ping -w 1 www.google.com | grep ttl)
if [ "$pingtimetwo" = "" ]
then
clear ; echo 'Offline'
else
clear ; echo 'Online' ; play -n synth 0.3 sine 800 vol 0.75
fi
else
clear ; echo 'Online' ; play -n synth 0.3 sine 800 vol 0.75
fi
sleep 1
done
Similarly to #Jesse's answer, this option might be much faster than any solution using ping and perhaps slightly more efficient than #Jesse's answer.
find /sys/class/net/ -maxdepth 1 -mindepth 1 ! -name "*lo*" -exec sh -c 'cat "$0"/carrier 2>&1' {} \; | grep -q '1'
Explenation:
This command uses find with -exec to run command on all files not named *lo* in /sys/class/net/. These should be links to directories containing information about the available network interfaces on your machine.
The command being ran is an sh command that checks the contents of the file carrier in those directories. The value of $interface/carrier has 3 meanings - Quoting:
It seems there are three states:
./carrier not readable (for instance when the interface is disabled in Network Manager).
./carrier contain "1" (when the interface is activated and it is connected to a WiFi network)
./carrier contain "0" (when the interface is activated and it is not connected to a WiFi network)
The first option is not taken care of in #Jesse's answer. The sh command striped out is:
# Note: $0 == $interface
cat "$0"/carrier 2>&1
cat is being used to check the contents of carrier and redirect all output to standard output even when it fails because the file is not readable.
If grep -q finds "1" among those files it means there is at least 1 interface connected. The exit code of grep -q will be the final exit code.
Usage
For example, using this command's exit status, you can use it start a gnubiff in your ~/.xprofile only if you have an internet connection.
online() {
find /sys/class/net/ -maxdepth 1 -mindepth 1 ! -name "*lo*" -exec sh -c 'cat "$0"/carrier 2>&1 > /dev/null | grep -q "1" && exit 0' {} \;
}
online && gnubiff --systemtray --noconfigure &
Reference
Help testing special file in /sys/class/net/
find -exec a shell function?
shortest way: fping 4.2.2.1 => "4.2.2.1 is alive"
i prefer this as it's faster and less verbose output than ping, downside is you will have to install it.
you can use any public dns rather than a specific website.
fping -q google.com && echo "do something because you're connected!"
-q returns an exit code, so i'm just showing an example of running something you're online.
to install on mac: brew install fping; on ubuntu: sudo apt-get install fping
Ping was designed to do exactly what you're looking to do. However, if the site blocks ICMP echo, then you can always do the telnet to port 80 of some site, wget, or curl.
Checking Google's index page is another way to do it:
#!/bin/bash
WGET="/usr/bin/wget"
$WGET -q --tries=20 --timeout=10 http://www.google.com -O /tmp/google.idx &> /dev/null
if [ ! -s /tmp/google.idx ]
then
echo "Not Connected..!"
else
echo "Connected..!"
fi
For the fastest result, ping a DNS server:
ping -c1 "8.8.8.8" &>"/dev/null"
if [[ "${?}" -ne 0 ]]; then
echo "offline"
elif [[ "${#args[#]}" -eq 0 ]]; then
echo "online"
fi
Available as a standalone command: linkStatus
Pong doesn't mean web service on the server is running; it merely means that server is replying to ICMP echo.
I would recommend using curl and check its return value.
If your goal is to actually check for Internet access, many of the existing answers to this question are flawed. A few things you should be aware of:
It's possible for your computer to be connected to a network without that network having internet access
It's possible for a server to be down without the entire internet being inaccessible
It's possible for a captive portal to return an HTTP response for an arbitrary URL even if you don't have internet access
With that in mind, I believe the best strategy is to contact several sites over an HTTPS connection and return true if any of those sites responds.
For example:
connected_to_internet() {
test_urls="\
https://www.google.com/ \
https://www.microsoft.com/ \
https://www.cloudflare.com/ \
"
processes="0"
pids=""
for test_url in $test_urls; do
curl --silent --head "$test_url" > /dev/null &
pids="$pids $!"
processes=$(($processes + 1))
done
while [ $processes -gt 0 ]; do
for pid in $pids; do
if ! ps | grep "^[[:blank:]]*$pid[[:blank:]]" > /dev/null; then
# Process no longer running
processes=$(($processes - 1))
pids=$(echo "$pids" | sed --regexp-extended "s/(^| )$pid($| )/ /g")
if wait $pid; then
# Success! We have a connection to at least one public site, so the
# internet is up. Ignore other exit statuses.
kill -TERM $pids > /dev/null 2>&1 || true
wait $pids
return 0
fi
fi
done
# wait -n $pids # Better than sleep, but not supported on all systems
sleep 0.1
done
return 1
}
Usage:
if connected_to_internet; then
echo "Connected to internet"
else
echo "No internet connection"
fi
Some notes about this approach:
It is robust against all the false positives and negatives I outlined above
The requests all happen in parallel to maximize speed
It will return false if you technically have internet access but DNS is non-functional or your network settings are otherwise messed up, which I think is a reasonable thing to do in most cases
If you want to handle captive portals, you can do this oneliner:
if [[ $(curl -s -D - http://www.gstatic.com/generate_204 2>/dev/null | head -1 | cut -d' ' -f 2) == "204" ]]; then
echo 'online'
else
echo 'offline'
fi
Or if you want something more readable that can differentiate captive portals from lack of signal:
function is_online() {
# Test signal
local response
response=$(curl --silent --dump-header - http://www.gstatic.com/generate_204 2> /dev/null)
if (($? != 0)); then return 2; fi
# Test captive portal
local status=$(echo $response | head -1 | cut -d' ' -f 2)
((status == "204"))
}
is_online && echo online || echo offline

Resources