Check connectivity with multiple hosts - BASH script available here


 
Thread Tools Search this Thread
Top Forums Shell Programming and Scripting Check connectivity with multiple hosts - BASH script available here
# 1  
Old 03-04-2015
Wrench Check connectivity with multiple hosts - BASH script available here

Hi everyone!

Some time ago, I had to check connectivity with a big list of hosts, using different formats (protocol://server:port/path/, server:port, ....).
I developed a script that checks the connectivity using different commands (ping, telnet, nc, curl).
It worked for me so I'm sharing it now, hope you find it useful :)

Here's the code for connect.sh (v1.7 update: some fixes added, suggested by Scrutinizer):
Code:
#!/bin/bash

#SCRIPT FOR TESTING CONNECTIVITY WITH A LIST OF URLs by Fr3dY
#
#1.7: Misc. fixes suggested by 'Scrutinizer' (thanks!) at
#     https://www.unix.com/shell-programming-and-scripting/255925-check-connectivity-multiple-hosts-bash-script-available-here.html
#     IFS backup and restore
#     Modified curl parameters from array to string
#     Modified temp file location to /tmp
#     Fixed ignored errors detection
#
#1.6: Added support for PING
#
#1.5: Added support for NC
#
#1.4: Added error messages when using TELNET
#     Added error codes to be ignored when using CURL
#
#1.3: When using CURL, it shows a OK or ERROR message
#     The URL parser also admits the host:port format (and even host without port, using a default one)
#     Included the script version in the app messages
#     Misc. fixes
#
#1.2: Modified TELNET usage, no CTRL-C capture anymore (it connects automatically now)
#     Misc. fixes
#
#1.1: Added a URL text file as input parameter
#     Added CURL as alternate method
#     Added a parser to use complete connection strings (they're converted automatically when using TELNET)
#     Misc. fixes
#
#1.0: Initial version
#

#Version
version=1.7

#Number of parameters
numberParameters=$#

#Command for testing URLs (right now it accepts 'telnet' and 'curl')
command=$1

#URL file to load
file=$2

#List of errors to be ignored when using CURL
ignoredErrors=(52 403 404 500)

#Iterator
i=0

#PID of the child process to be killed when using CTRL-C (obsolete)
pid=999999999999

#Connection timeout
timeout=3

#Default Port if not specified in the host:port format
defaultPort=80

#CURL parameters
curlParameters="--connect-timeout $timeout --insecure -S -s -f -o /dev/null"

#List of host and ports to check (now they're loaded as a file)
#HOSTS_LIST is defined in f_checkCall, after verifying the syntax

#List size
#LIST_SIZE is defined in f_checkCall, after verifying the syntax

#Temporal file
randomNumber=$RANDOM
tempfile=/tmp/$randomNumber.temp

#Variable init
host=0
port=0
hostport=0
proto=http
IFSbackup=$IFS

#Function that process the list of hosts and execute the connectivity check (telnet, curl, ...)
#PID of each background process is kept, to kill it if remains active
f_processList () {
  if [ ${i} -lt ${LIST_SIZE} ]
  then
    #Format list if TELNET is used (sintax is 'telnet host port' and not 'telnet host:port')
    #Any other conversion functions could be added if necessary
    if [ $command = "telnet" ]
    then
      f_convertFormat
      f_executeTelnet
      f_killTimeout
    elif [ $command = "nc" ]
    then
      f_convertFormat
      f_executeNC
    elif [ $command = "ping" ]
    then
      f_convertFormat
      f_executePING
    else
      f_restoreIFS
      f_executeCurl
    fi
    i=$(($i + 1))
    f_processList
  fi
}

#Function to convert the URL format to HOST PORT (for using TELNET, NC...)
#Admits 'host:port' and 'protocol://host:port/path' --> they're converted to 'host port')
f_convertFormat () {

  COMPLETE_URL=${HOSTS_LIST[$i]}

  if [[ $COMPLETE_URL == */* ]]
  then
    format=url;
  else
    format=hostport;
  fi

  if [ $format = "url" ]
  then
    #Obtain the protocol
    proto="`echo $COMPLETE_URL | grep '://' | sed -e's,^\(.*://\).*,\1,g'`"
    #Get the URL after removing the protocol
    url=`echo $COMPLETE_URL | sed -e s,$proto,,g`
    #Extract user and password (if any)
    userpass="`echo $url | grep @ | cut -d@ -f1`"
    pass=`echo $userpass | grep : | cut -d: -f2`
    if [ -n "$pass" ]; then
        user=`echo $userpass | grep : | cut -d: -f1`
    else
        user=$userpass
    fi
    #Get the host. If no port is defined, the default one is used (attending to the protocol)
    hostport=`echo $url | sed -e s,$userpass@,,g | cut -d/ -f1`
    port=`echo $hostport | grep : | cut -d: -f2`
    if [ -n "$port" ]; then
        host=`echo $hostport | grep : | cut -d: -f1`
    else
        if [ $proto = "http://" ]
        then
           port=80
        elif [ $proto = "https://" ]
        then
           port=443
        elif [ $proto = "ftp://" ]
        then
           port=21
        elif [ $proto = "ftps://" ]
        then
           port=22
        fi
        host=$hostport
        hostport=$host:$port
    fi
  else
    host=`echo $COMPLETE_URL|cut -d ":" -f1`
    port=`echo $COMPLETE_URL|cut -d ":" -f2`
    #If no port is defined, the default one is used
    if [ $host = $port ]
    then
      port=$defaultPort
    fi
  fi
  #The $host and $port variables are set for their incoming usage by using TELNET
}

#Function that executes TELNET on the selected host
f_executeTelnet () {
  CURRENTHOST="$host $port"
  echo "Checking connection with $host:$port"
  $command $host $port > $tempfile 2>/dev/null &
  #Keep the child process PID, to stop it later if it remains active
  pid=$!
  sleep $timeout
  output=`tail -1 $tempfile`
  if [ -z $output ] || [ $output = "" ]
  then
    echo "ERROR WHEN CONNECTING WITH $CURRENTHOST !!!!!!!!"
  else
    if [ $output = "Escape character is '^]'." ]
    then
      echo "CONNECTION OK"
    else
      echo "ERROR WHEN CONNECTING WITH $CURRENTHOST !!!!!!!!"
    fi
  fi
  echo
  echo
}

#Function that executes NC on the selected host
f_executeNC () {
  CURRENTHOST="$host $port"
  echo "Checking connection with $host:$port"
  $command -v -w ${timeout} -z $host $port
  status=$?
  if [ $status = 0 ]
  then
    echo "CONNECTION OK"
  else
    echo "ERROR WHEN CONNECTING WITH $CURRENTHOST"
  fi
  echo
  echo
}

#Function that executes PING on the selected host
f_executePING () {
  CURRENTHOST="$host"
  echo "Checking connection with $host"
  $command -c 1 -W ${timeout} $host >/dev/null 2>/dev/null
  status=$?
  if [ $status = 0 ]
  then
    echo "CONNECTION OK"
  else
    echo "ERROR WHEN CONNECTING WITH $CURRENTHOST"
  fi
  echo
  echo
}

#Function that stops the TELNET processes that have been launched in background and are still active (didn't connect)
f_killTimeout () {
  #Check the process is still active and it's a 'telnet'
  process=`ps -ef|grep $pid|awk '{print $8}'|grep -v grep`
  if [ ! -z $process ]
  then
    if [ "$process" = "telnet" ]
    then
      #Remove this PID from BASH control before killing it, to avoid the 'Killed...' message
      disown $pid
      kill $pid
    fi
  fi
}

#Check if element $1 exists in array $2
f_contains () {
  for elem in "${@:2}"; do [[ "$elem" = "$1" ]] && return 0; done; return 1;
}

#Function that execute CURL on the selected host
f_executeCurl () {
  CURRENTHOST=${HOSTS_LIST[$i]}
  echo "Checking connection with $CURRENTHOST"
  $command ${curlParameters} $CURRENTHOST
  status=$?
  if [ $status = 0 ]
  then
    echo "CONNECTION OK"
  else
    #if [[ "${ignoredErrors[@]}" =~ "$status" ]]
    f_contains $status ${ignoredErrors[@]}
    contain=$?
    if [ $contain -eq 0 ]
    then
      echo "CONNECTION OK (ERROR $status IGNORED)"
    else
      echo "ERROR CONNECTING WITH $CURRENTHOST, ERROR CODE: $status !!!!!!!!"
    fi
  fi
  echo
  echo
}

#Delete temp files
f_deleteTemp () {
  rm -f $tempfile
}

#Check correct syntax
f_callCheck () {
  if [ ! $numberParameters -eq 2 ]
  then
    echo "*** Script for Connectivity Testing, version $version ***"
    echo ""
    echo "Syntax:"
    echo "$0 <command> <file>"
    echo ""
    echo "IMPORTANT: The file must be a complete URL list, each line must be like any of these:"
    echo "protocol://host/path"
    echo "protocol://host:port/path"
    echo "host:port"
    echo "host (when no port is defined, a default one will be used)"
    echo ""
    echo "Accepted commands are: telnet , curl, nc, ping"
    echo ""
    exit 0;
  fi

  if [ ! -f "$file" ]
  then
    echo "ERROR: Can't find the specified file: $file"
    exit 1;
  else
    IFS=$'\r\n' HOSTS_LIST=($(cat $file))
    LIST_SIZE=${#HOSTS_LIST[@]}
  fi

  if [ ! $command = "telnet" ] && [ ! $command = "curl" ] && [ ! $command = "nc" ] && [ ! $command = "ping" ]
  then
    echo "ERROR: Command not supported: $command"
    exit 1;
  fi

}

#Initial message
f_initialMessage () {
  echo
  echo "####################################################"
  echo "###                                              ###"
  echo "### Connectivity Tester, version $version             ###"
  if [ $command = "telnet" ]
  then
    echo "### Using TELNET for connection testing          ###"
  elif [ $command = "curl" ]
  then
    echo "### Using CURL for connection testing            ###"
  elif [ $command = "ping" ]
  then
    echo "### Using PING for connection testing            ###"
  else
    echo "### Using NC for connection testing              ###"
  fi
  echo "###                                              ###"
  echo "####################################################"
  echo
  echo
}

f_restoreIFS () {
  IFS=${IFSbackup}
}

################
## MAIN BLOCK ##
################
f_callCheck
f_initialMessage
f_processList
f_deleteTemp
f_restoreIFS


Last edited by Fr3dY; 03-08-2015 at 05:07 PM.. Reason: v1.7: Misc. fixes added, as suggested by Scrutinizer
# 2  
Old 03-04-2015
I have found a simple alternative to your telnet test:
Code:
timeout(){
to=$1; shift
perl -e "alarm $to; exec @ARGV" "$@"
}
f_telnet(){
# pass the terminator char to telnet, join its stderr to stdout, and grep for "Connection closed"
printf "%d\n" 0x1d | timeout 3 telnet $1 $2 2>&1 | grep "^Connection .*closed" >/dev/null
}

It returns 0 (the status of the grep) if localhost listens on port 25.
For example
Code:
if f_telnet localhost 25; then echo positive; else echo negative; fi


Last edited by MadeInGermany; 03-04-2015 at 06:27 PM.. Reason: timeout added
# 3  
Old 03-05-2015
Quote:
Originally Posted by MadeInGermany
I have found a simple alternative to your telnet test:
Code:
timeout(){
to=$1; shift
perl -e "alarm $to; exec @ARGV" "$@"
}
f_telnet(){
# pass the terminator char to telnet, join its stderr to stdout, and grep for "Connection closed"
printf "%d\n" 0x1d | timeout 3 telnet $1 $2 2>&1 | grep "^Connection .*closed" >/dev/null
}

It returns 0 (the status of the grep) if localhost listens on port 25.
For example
Code:
if f_telnet localhost 25; then echo positive; else echo negative; fi

I'll take a look later, but I think I used the terminal output to experiment with it Smilie


Thanks!
# 4  
Old 03-06-2015
Thanks for the script. Here are a couple of observations:
Code:
IFS=$'\r\n' HOSTS_LIST=($(cat $file))

Note: this permanently changes IFS throughout the script, not just local to the arrays assignment.
Code:
if [[ "${ignoredErrors[@]}" =~ "$status" ]]

This does not do what you want, check what happens when status equals 2 vs. when it equals 3.

Code:
curlParameters=(--connect-timeout $timeout --insecure -S -s -f -o /dev/null)

Why not just use a simple variable with a string?
Code:
curlParameters="--connect-timeout $timeout --insecure -S -s -f -o /dev/null"

Code:
tempfile=$randomNumber.temp

This means that in order for the script to run, it has to be able to write in the current directory. The script does not check if it can write to the file...

f_processList does not loop throught the process, but calls itself recursively, which is not efficient, while using global variables which is not good practise..

Consider using case statements rather than if then elif then elif constructs...
This User Gave Thanks to Scrutinizer For This Post:
# 5  
Old 03-06-2015
Quote:
Originally Posted by Scrutinizer
Thanks for the script. Here are a couple of observations:
Code:
IFS=$'\r\n' HOSTS_LIST=($(cat $file))

Note: this permanently changes IFS throughout the script, not just local to the arrays assignment.
Code:
if [[ "${ignoredErrors[@]}" =~ "$status" ]]

This does not do what you want, check what happens when status equals 2 vs. when it equals 3.

Code:
curlParameters=(--connect-timeout $timeout --insecure -S -s -f -o /dev/null)

Why not just use a simple variable with a string?
Code:
curlParameters="--connect-timeout $timeout --insecure -S -s -f -o /dev/null"

Code:
tempfile=$randomNumber.temp

This means that in order for the script to run, it has to be able to write in the current directory. The script does not check if it can write to the file...

f_processList does not loop throught the process, but calls itself recursively, which is not efficient, while using global variables which is not good practise..

Consider using case statements rather than if then elif then elif constructs...
Thanks for the advice! I'll implement your suggestions ASAP Smilie
# 6  
Old 03-08-2015
Hi,

I've implemented some of your suggestions, but have a couple doubts:

Code:
if [[ "${ignoredErrors[@]}" =~ "$status" ]]

What's the problem with that? I just check if the returned status exists in the ignoredErrors array

Code:
curlParameters=(--connect-timeout $timeout --insecure -S -s -f -o /dev/null)

Replacing ( ) by " " makes curl think the whole thing is a single parameter. I couldn't make it work that way (does it really matter?)


Thanks!
# 7  
Old 03-08-2015
Quote:
Originally Posted by Fr3dY
Hi,

I've implemented some of your suggestions, but have a couple doubts:

Code:
if [[ "${ignoredErrors[@]}" =~ "$status" ]]

What's the problem with that? I just check if the returned status exists in the ignoredErrors array
It does, but it does more than that. If status equals 2 for example, the result is also TRUE and it should not be..

Quote:
Code:
curlParameters=(--connect-timeout $timeout --insecure -S -s -f -o /dev/null)

Replacing ( ) by " " makes curl think the whole thing is a single parameter. I couldn't make it work that way (does it really matter?)


Thanks!
Yes you would need to call it unquoted, just like you did when you called it as an array, so $curlParameters instead of $curlParameters[@]
Login or Register to Ask a Question

Previous Thread | Next Thread

10 More Discussions You Might Find Interesting

1. IP Networking

Help with to check the tcp network connectivity between servers and hosts

ello, i am new to the networking side. I have a requirement to check the tcp network connectivity between server it's running on and the list of host's and ports combination. please help me where i am going wrong. my code: #!/bin/bash #read the file line by line cd "$1" cat... (17 Replies)
Discussion started by: sknovice
17 Replies

2. Shell Programming and Scripting

Help with shell script to check the tcp network connectivity between server

Hello, I have a requirement to check the tcp network connectivity between server it's running on and the list of host's and ports combination. i have written the below code but it doesn't work, but when i execute the nc command outside the script it works fine. please help me where i am... (8 Replies)
Discussion started by: sknovice
8 Replies

3. Shell Programming and Scripting

Bash script to detect nonpingable hosts

I have a script to detect if a host is pingable or not. The problem is that I would like it to put the nonpingable hosts in one file and the pingable hosts in another. I have come up with this so far: for ip in `cat /tmp/testlist2`; do ping -c 3 $ip >/dev/null && echo "$ip is up" || echo "$ip... (5 Replies)
Discussion started by: newbie2010
5 Replies

4. Shell Programming and Scripting

How to write bash script for creating user on multiple Linux hosts?

I wonder whether someone can help me with what I'm trying to achieve Basically, the objective is one script to create new user on more than 70 linux hosts if required. Everything works apart from the highlighted part. It gave me an output passwd: Unknown user name ''. when try to set... (35 Replies)
Discussion started by: fugeulu
35 Replies

5. Shell Programming and Scripting

Check the connectivity of the DB through script, exit if no connection

check the connectivity of the DBs through script, script should exit if no connection and display the output as below. connectivity for DB1 is OK connectivity for DB2 is OK connectivity for DB3 is FAILED for DB in 1 2 3 do (sqlplus -s... (5 Replies)
Discussion started by: only4satish
5 Replies

6. Solaris

Sybase Connectivity Check through Shell Script

Hi, I need to check the sysbase database connectivity through the Unix Shell Script. Can you help me on the how to write the script to test the sysbase database connection. Thanks in Advance Nandha (0 Replies)
Discussion started by: nandha2387
0 Replies

7. Shell Programming and Scripting

Check connectivity script

This past weekend I had some issues with my ISP. So for future purpose I'm going to have some logging on my internet so I'm able to attach log files to my complaint email if this issue reoccurs. Decided to do a simple ping script that runs every 5 or 10 min with crontab if ping fail write date... (5 Replies)
Discussion started by: chipmunken
5 Replies

8. Shell Programming and Scripting

Linux: Writing a tricky script to check connectivity

So, first and foremost, I'm having issues with my internet connection. Periodically, the connection drops across the network. The fix is simple enough: restart the modem. However, this gets old when the connection dies out every hour. I can hit my surfboard on 192.168.100.1, and navigate to a... (5 Replies)
Discussion started by: kungfujoe
5 Replies

9. Shell Programming and Scripting

Script to check connectivity

I want to write a script to check if a unix box say abc.tdc.cin.net can be connected or not on certain port say 22. right know i have to telnet them manually from DOS prompt and if it is successful then isay it is connected. Also to check Database connectivity I am using tnsping From DOS prompt.... (3 Replies)
Discussion started by: kukretiabhi13
3 Replies

10. Shell Programming and Scripting

script for df output from multiple hosts

I am trying get "df -k" output from multiple hosts along with their hostnames via ssh, my script is appending the "df -k" output from all the nodes to a single file but not getting the hostnames for those nodes, just wondering how to pass more than one command via ssh or may be someone could come... (6 Replies)
Discussion started by: barkath
6 Replies
Login or Register to Ask a Question