Monday 19 September 2011

fork and timeout control in shell script

I want to run a system command (network commands like ping , nslookup and traceroute etc...) and see if they are successful or kill them if they take long time to execute:

 Here is the command function and sample commands i have written to do that:

# Execute the passed Unix command in background
_Command()
{
    command=$@

    rm -f $TMP_FILE 2>/dev/null
    rm -f $TMP_ERROR_FILE 2>/dev/null
    rm -f $TMP_RC_FILE 2>/dev/null

    _Report "## Executing command [$command] in backgound to monitor execution time..."

    # Executing $command, redirecting stderr to $TMP_ERROR_FILE, saving stdout to
    # $TMP_FILE and the return code to $TMP_RC_FILE
   
    (eval $command >$TMP_FILE 2>$TMP_ERROR_FILE && echo $? >$TMP_RC_FILE || echo $? >$TMP_RC_FILE) &

    i="0"

    while true
    do
        jobstring=`jobs |grep 'eval $command'`

        if [[ `echo $jobstring` != "" ]]
        then
            # Or command did not complete yet, or it is stopped or in a bad status.
            # Let's check if we exceeded the maximum execution time allowed for the command.

            if [[ $i -gt $max_exec_time ]]
            then
                _Report "## Timeout [$max_exec_time seconds] exceeded, killing background command..."

                bg_process_string=`ps -ef |grep $! |grep "$command"`

                bg_pid=`echo $bg_process_string |cut -f2 -d" "`

                _Report "## Background command process is [PID=$bg_pid]..."
                _Report "## Killing background process [PID=$bg_pid]..."

                kill -9 $bg_pid

                _Report "## Done."

                _Report "BEGIN Command Output : $command "
                _Report "[$command] Command timed out."
                _Report "END [Timeout]"

                rc=$Cmd_failed

                break
            else
                _Report "## Waiting for background command to complete [$i second(s) elapsed]..."

                sleep 1

                (( i = i + 1 ))
            fi
        else
            _Report "## Background command completed, checking command return code..."

            cmd_rc=`cat $TMP_RC_FILE`

            if [[ $cmd_rc -ne 0 ]]
            then
                if [[ `cat $TMP_ERROR_FILE` == "" ]]
                then
                    _Report "## Background command terminated in error with [RC=$cmd_rc] and no error message..."

                    # Collecting all return codes of commands into a variable
                    Returns=$Returns,$cmd_rc

                    _Report "BEGIN Command Output : $command "
                    _Report "[$command] Command not successful."
                    _Report "END [RC=$cmd_rc]"

                    rc=$Cmd_failed

                    break
                else
                    _Report "## Background command terminated in error with [RC=$cmd_rc] and the following error message:"

                    # Log the command stderr to logfile
                    _Log
                    cat $TMP_ERROR_FILE >> $LOGDIR/$logfile
                    _Log

                    # Collecting all return codes of commands into a variable
                    Returns=$Returns,$cmd_rc

                    _Report "BEGIN Command Output : $command "
                    _Report "[$command] Command not successful."
                    _Report "END [RC=$cmd_rc]"

                    rc=$Cmd_failed

                    break
                fi
            else
                _Report "## Background command terminated successfully [RC=$cmd_rc]..."

                _Report "BEGIN Command Output : $command "
                _Log

                # Log the command stdout to logfile
                cat $TMP_FILE >> $LOGDIR/$logfile

                _Log
                _Report "END [RC=$cmd_rc]"

                # Do not set/override $rc to 0 here as it is unknown if previous commands failed

                break
            fi
        fi
    done

    rm -f $TMP_FILE
    rm -f $TMP_ERROR_FILE
    rm -f $TMP_RC_FILE
}


# Run first ckeck command
_Command "ping -c 3 $resourcename"

_Report

# Run second command
_Command "traceroute $resourcename"

_Report

# Run third command
_Command "nslookup $resourcename"

Note : _Report function and _Log function would be like this:

# Write plain text to logfile
_Log()
{
    echo "$@" >> $LOGDIR/$logfile
}

# Write info to logfile
_Report()
{
    #echo $@
    echo "`date +'%m/%d/%Y %H:%M:%S'` [$$] : $@" >> $LOGDIR/$logfile
}





No comments:

Post a Comment

Tweets by @sriramperumalla