Bash script to (more or less) reliably check if the Internet is up - linux

I need a Bash (or a plain shell) script to put in a cronjob that every minute checks if the Internet is up.
This is how I did it:
#! /bin/sh
host1=google.com
host2=wikipedia.org
curr_date=`date +"%Y%m%d%H%M"`
echo -n "${curr_date};"
((ping -w5 -c3 $host1 || ping -w5 -c3 $host2) > /dev/null 2>&1) &&
echo "up" || (echo "down" && exit 1)
How would you do it? Which hosts would you ping?
Clarifications:
By "internet is up", I mean my internet connection.
By "up", I mean to have usable connection (doesn't really matter if we are talking about the DNS being down or the connection is really really slow [mind the -w for timeout]). That is also why I didn't include any IP but only hosts.
Should I also ping Stack Overflow? I mean, if I can't access Google, Wikipedia or Stack Overflow, I don't want Internet :p

That one seems like a good solution. Just add a few more hosts, and maybe some pure IP hosts so you don't rely on DNS functioning (which in itself depends on your definition of "up").

Thanks for your code, it works great, I've left only one line actually:
((ping -w5 -c3 8.8.8.8 || ping -w5 -c3 4.2.2.1) > /dev/null 2>&1) && echo "up" || (echo "down" && exit 1)

What portion of Internet connectivity are you looking to check? DHCP? DNS? Physically being plugged into a jack? Kernel recognizing the presence of the NIC?
You can manually query your ISP's DNS server(s) by using the host(1) command. This is generally a good indication of whether your router has lost its connection to the ISP.
You can query what interfaces your kernel has by using netstat(8) or ifconfig(8).
You can get detailed statistics about the interface using ifstat.

Related

identify correct interface in Linux among others

I have some physical servers with multiple interface, each interface is assigned to a separate vlan. The correct mapping of interface number to vlan from network admins usually differs from what I see inside my console and the common procedure is to check MAC of interface he has configured. I tried to find a solution to resolve this. The first solution I tried was to get tcpdump from the interface to see what type of ip address is broadcasting which it is cumbersome. The second solution was to arping the gw to see if this is true interface with a command like:
arping -c 1 -D -q -I $i $SGW1
if [ $? == 1 ]; then echo "$i $SGW1" ;SIF+=("$i"); fi
like
arping -c 1 -D -q -I ens160 172.20.29.158
but it take time and sometimes with wrong answers. Could you please suggest a better way?
(we are not running DHCP by the way)

Why echo isn't able to access a variable in Ubuntu 20.04

I can't find anything that helps with this issue even if the answer may be very simple.
I'm trying to get squid to redirect traffic through a 3g dongle, therefore I need to table the IP and change it in the squid.conf every time it changes.
I'm using this code here on "/etc/ppp/ip-up.local" so it's launched every time a new IP connects (or reconnects) to the machine.
#!/bin/bash
if [[ "$PPP_IFACE" == "ppp0" ]] ; then
TABLE=uplink2
/bin/sed -i "s&\(tcp_outgoing_address\).*\(very1\)&\1 $PPP_LOCAL \2&" /etc/squid/squid.conf
fi
##generate ip subnet for route
baseip=`echo "$PPP_LOCAL" | cut -d"." -f1-3`
/usr/sbin/ip route add "$baseip".0/24 dev "$PPP_IFACE" src "$PPP_LOCAL" table "$TABLE"
/usr/sbin/ip route add default via "$PPP_REMOTE" table "$TABLE"
/usr/sbin/ip rule add from "$PPP_LOCAL" table "$TABLE"
/usr/sbin/squid/ -k reconfigure
/usr/bin/systemctl squid restart
exit 0
The problem is that baseip=echo "$PPP_LOCAL" | cut -d"." -f1-3 cannot use $PPP_LOCAL
I tried to add echo $PPP_LOCAL >> file.txt but it just adds an empty line.
It's awkward to me that sed instead accesses the variable and modify correctly the squid.conf file with the new address
How can I fix this??
I also have a "sub-question", I'm a complete newbie just starting to learn and I'm not sure whether or not I should add an ip-down code to remove the table rules
Thanks everyone for the help

"killall wpa_supplicant" affects "ip route add ..." in a strange way

Not sure if the title explains my situation correctly, but in details it looks like this:
I'm writing a simple bash script to set up a wireless network, using wlp2s0 interface.
ip route flush dev wlp2s0
ip addr flush dev wlp2s0
ip link set wlp2s0 down
killall wpa_supplicant
ip link set wlp2s0 up
ip addr add 192.168.1.200/24 dev wlp2s0
ip route add default via 192.168.1.1
wpa_supplicant -B -D wext -i wlp2s0 -c wireless.conf
It kills all previously started wpa_supplicants and then starts a new one.
Now, the problem is that the killall call causes ip route add to cry:
RTNETLINK answers: Network is unreachable
no matter if wpa_supplicant was actually started before.
It can be "fixed" by adding a sleep 1 call after killall, but of course I'd like to avoid this. It can also be fixed by removing the killall command and calling it manually before the script.
So the question is - how can I work around this strange behaviour of killall? Maybe someone has any idea why are these strange things happening.
EDIT: ip route add does not give that error if killall isn't called before it.
Why did you think it was strange? Successful return of killall doesn't necessarily mean wpa_supplicant has finished processing the incoming SIGTERM signal. It's only that the signal was delivered to the wpa_supplicant process, at best. Pehaps wpa_supplicant needed some more time (such as sleep 1) to finish execution of its clean-up handler (wpa_supplicant_terminate_proc() in wpa_supplicant.c)
http://hostap.epitest.fi/cgit/hostap/tree/src/utils/eloop.c#n753
http://hostap.epitest.fi/cgit/hostap/tree/wpa_supplicant/wpa_supplicant.c#n4033
So, I think you really need sleep 1.
Update
I always rely on polling method like this
TIMEO=5
for ((i=0; i<TIMEO; ++i)); do
if pidof -s wpa_supplicant > /dev/null; then
sleep 1
else
break
fi
done
if ((TIMEO==i)); then
echo "timeout"
else
echo "it's gone"
fi
assuming there will not be multiple instances of wpa_supplicant.

Domain or IP exists check in Linux

I want to know if a given website or IP address is online or offline. I researched a lot, but all I can find is to install some software or using the ping command.
I did this test:
ping -c 5 -n example.com
It outputs the expected result, but when I do the following where a website does not ext, the result is almost the same as if website existed, with 0% packet loss. Please see the screenshot attached.
ping -c 5 -n examplesurenotexists.com
I am confused by this. Is there a better way to do this task?
If you want to know if a website is online of offline, simply check the website:
if curl -s http://www.alfe.de >/dev/null
then
echo "online"
else
echo "offline"
fi
Using ping instead would not test the HTTP protocol (which is for websites) but the ICMP protocol; one is merely independent from the other (but of course, if the host is down, both won't work). There are sites which still react on ICMP while the HTTP server is down (this is rather typical) and there are sites which won't react on ICMP although the HTTP server is up and running functioning perfectly well.

How to check if a server is running

I want to use ping to check to see if a server is up. How would I do the following:
ping $URL
if [$? -eq 0]; then
echo "server live"
else
echo "server down"
fi
How would I accomplish the above? Also, how would I make it such that it returns 0 upon the first ping response, or returns an error if the first ten pings fail? Or, would there be a better way to accomplish what I am trying to do above?
I'ld recommend not to use only ping. It can check if a server is online in general but you can not check a specific service on that server.
Better use these alternatives:
curl
man curl
You can use curl and check the http_response for a webservice like this
check=$(curl -s -w "%{http_code}\n" -L "${HOST}${PORT}/" -o /dev/null)
if [[ $check == 200 || $check == 403 ]]
then
# Service is online
echo "Service is online"
exit 0
else
# Service is offline or not working correctly
echo "Service is offline or not working correctly"
exit 1
fi
where
HOST = [ip or dns-name of your host]
(optional )PORT = [optional a port; don't forget to start with :]
200 is the normal success http_response
403 is a redirect e.g. maybe to a login page so also accetable and most probably means the service runs correctly
-s Silent or quiet mode.
-L Defines the Location
-w In which format you want to display the response
-> %{http_code}\n we only want the http_code
-o the output file
-> /dev/null redirect any output to /dev/null so it isn't written to stdout or the check variable. Usually you would get the complete html source code before the http_response so you have to silence this, too.
nc
man nc
While curl to me seems the best option for Webservices since it is really checking if the service's webpage works correctly,
nc can be used to rapidly check only if a specific port on the target is reachable (and assume this also applies to the service).
Advantage here is the settable timeout of e.g. 1 second while curl might take a bit longer to fail, and of course you can check also services which are not a webpage like port 22 for SSH.
nc -4 -d -z -w 1 ${HOST} ${PORT} &> /dev/null
if [[ $? == 0 ]]
then
# Port is reached
echo "Service is online!"
exit 0
else
# Port is unreachable
echo "Service is offline!"
exit 1
fi
where
HOST = [ip or dns-name of your host]
PORT = [NOT optional the port]
-4 force IPv4 (or -6 for IPv6)
-d Do not attempt to read from stdin
-z Only listen, don't send data
-w timeout
If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. (In this case nc will exit 1 -> failure.)
(optional) -n If you only use an IP: Do not do any DNS or service lookups on any specified addresses, hostnames or ports.
&> /dev/null Don't print out any output of the command
You can use something like this -
serverResponse=`wget --server-response --max-redirect=0 ${URL} 2>&1`
if [[ $serverResponse == *"Connection refused"* ]]
then
echo "Unable to reach given URL"
exit 1
fi
Use the -c option with ping, it'll ping the URL only given number of times or until timeout
if ping -c 10 $URL; then
echo "server live"
else
echo "server down"
fi
Short form:
ping -c5 $SERVER || echo 'Server down'
Do you need it for some other script? Or are trying to hack some simple monitoring tool? In this case, you may want to take a look at Pingdom: https://www.pingdom.com/.
I using the following script function to check servers are online or not. It's useful when you want to check multiple servers. The function hide the ping output, and you can handle separately the server live or server down case.
#!/bin/bash
#retry count of ping request
RETRYCOUNT=1;
#pingServer: implement ping server functionality.
#Param1: server hostname to ping
function pingServer {
#echo Checking server: $1
ping -c $RETRYCOUNT $1 > /dev/null 2>&1
if [ $? -ne 0 ]
then
echo $1 down
else
echo $1 live
fi
}
#usage example, pinging some host
pingServer google.com
pingServer server1
One good solution is to use MRTG (a simple graphing tool for *NIX) with ping-probe script. look it up on Google.
read this for start.
Sample Graph:

Resources