Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

I have a large iptables ruleset that I manage with my own bash script. Most of the commands in the script are simple, single-statment iptables commands. I am trying to improve the script by adding success/failure output as the script executes.

I have the script broken out into different sections. One example would be the FORWARD chain section, where all the rules are applied to the FORWARD chain. At the beginning of the section, I output that the script has started applying the FORWARD rules, and at the end, I want to output whether or not all the rules were applied successfully, or if any of them didn't work. Here is the basic idea:

#Start FORWARD section
echo -ne "Applying FORWARD rules..."

#rule 1
/sbin/iptables -A FOWRARD...

#rule 2
/sbin/iptables -A FORWARD...

echo -ne "\t\t\t[OK]\n"

What I'm wanting to do is catch any output or errors that may result from each iptables command and store them in an array or something. Then at the end of the block, use an if statement to evaluate the array to see if there were any errors. If not, output the [OK] status, if there were, output the [FAILED] status and display the related error.

Is there a way I can do this for the entire block of rules without wrapping each iptables rule in an if [ $? != 0 ] expression?

share|improve this question
    
This seems like it's better fit for Unix&Linux, or StackOverflow, due to its Linux-related nature, but not Ubuntu-related nature. –  Thomas W. Jul 19 '11 at 18:14
    
If your rules are static or can be grouped, consider iptables-apply as it will not apply the rules if one of them fails. –  Lekensteyn Jul 19 '11 at 19:15

3 Answers 3

You could use the trap shell builtin command to have a handler function called if a command has a non-zero exit status. You can pass necessary information, like the line number and the exit status to your error handler function.

Example:

#!/bin/bash

handle_error() {
    echo "FAILED: line $1, exit code $2"
    exit 1
}

trap 'handle_error $LINENO $?' ERR 

# your commands here
# ...

echo "OK"
share|improve this answer
    
In all my years of bash I've never heard of the trap command! –  Marco Ceppi Jul 20 '11 at 13:39
    
you may have also to : set -o errtrace at the top-level of the script, if you also want that script's functions to call that trap in case of errors (otherwise, inside functions, that trap would not be called) –  Olivier Dulac Jul 4 at 9:12

The below script will define the recordfailure function which should be prepended to commands as shown in the example after the function definition. If the command returns non-zero, the standard error stream is logged. Finally, on the end, the error log is checked and [OK] or [FAIL] is printed accordingly.

#!/bin/bash

# Redirect file descriptor #3 to standard output (used in recordfailure)
exec 3>&1

# create an array for holding failures
declare -a failures

# recordfailure command arg1 arg2 ... argN
recordfailure() {
    local error retval
    # Run the command and store error messages (output to the standard error
    # stream in $error, but send regular output to file descriptor 3 which
    # redirects to standard output
    error="$("$@" 2>&1 >&3)"
    retval=$?
    # if the command failed (returned a non-zero exit code)
    if [ $retval -gt 0 ]; then
        if [ -z "$error" ]; then
            # create an error message if there was none
            error="Command failed with exit code $retval"
        fi
        # uncomment if you want the command in the error message
        #error="Command $* failed: $error"

        # append the error to $failures, ${#failures[@]} is the length of
        # the array and since array start at index 0, a new item is created
        failures[${#failures[@]}]="$error"
        # uncomment if you want to show the error immediately
        #echo "$error"
    fi
}
recordfailure iptables -A FORWARD ...
recordfailure iptables -A FORWARD ...
# if the length of the failures array equals 0 (no items) everything is OK
if [ ${#failures[@]} -eq 0 ]; then
    echo "[OK]"
else
    echo "[FAIL]"
    # list every error
    for failure in "${failures[@]}"; do
        # optionally color it, format it or whatever you want to do with it
        echo "error: $failure"
    done
fi

If you've no desire for showing standard output, remove exec 3>&1 and replace >&3 by >/dev/null.

share|improve this answer
    
Don't test $? for true or false, just run the command in the if in the first place. if ! error=$("$@" ...); then .... Oh and you have a comma in the local command. –  geirha Jul 20 '11 at 13:10
    
@geirha: I use the return value in case there is no error message. If I use if ! error=$("$@" 2>&1 >&3), I cannot evaluate another if and display the return value. –  Lekensteyn Jul 20 '11 at 13:16
    
Sure you can, if checks the return value of the command you run, not whether it outputs anything to stderr. –  geirha Jul 20 '11 at 16:00

You mean something like this?

ok=1

#Start FORWARD section
echo -ne "Applying FORWARD rules..."

#rule 1
/sbin/iptables -A FOWRARD... || ok=0

#rule 2 
/sbin/iptables -A FORWARD... || ok=0

echo -ne "\t\t\t"
if [ $ok -eq 1 ]; then
  echo "[OK]"
else
  echo "[fail]"
fi
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.