X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=backupninja;h=ac504d605af123ae0f471f9f3e2ce457e511d90d;hb=fb77e731737f3ca96f1295f9ee1f9f58278da84e;hp=c8764ab651d3ea3402d1a4192ee47d5070ab28fc;hpb=a7bf628e6129254d0abb47e00fbb0df2d588cf34;p=matthijs%2Fupstream%2Fbackupninja.git diff --git a/backupninja b/backupninja index c8764ab..ac504d6 100755 --- a/backupninja +++ b/backupninja @@ -16,45 +16,37 @@ # GNU General Public License for more details. # -##################################################### -## DEFAULTS - -DEBUG=${DEBUG:=0} -CONFFILE="/etc/backupninja.conf" -USECOLOURS=1 - ##################################################### ## FUNCTIONS function setupcolors() { - if [ "$USECOLOURS" == 1 ] - then - BLUE="\033[34;01m" - GREEN="\033[32;01m" - YELLOW="\033[33;01m" - PURPLE="\033[35;01m" - RED="\033[31;01m" - OFF="\033[0m" - CYAN="\033[36;01m" - fi + BLUE="\033[34;01m" + GREEN="\033[32;01m" + YELLOW="\033[33;01m" + PURPLE="\033[35;01m" + RED="\033[31;01m" + OFF="\033[0m" + CYAN="\033[36;01m" + COLORS=($BLUE $GREEN $YELLOW $RED $PURPLE) } -function run() { - RUNERROR=0 - debug 0 "$@" - returnstring=`$@ 2>&1` - RUNERROR=$? - RUNERRORS=$[RUNERRORS+RUNERROR] - if [ "$RUNERROR" != 0 ]; then - debug 3 "Exitcode $RUNERROR returned when running: $@" - debug 3 "$returnstring" +function colorize() { + if [ "$usecolors" == "yes" ]; then + local typestr=`echo "$@" | sed 's/\(^[^:]*\).*$/\1/'` + [ "$typestr" == "Debug" ] && type=0 + [ "$typestr" == "Info" ] && type=1 + [ "$typestr" == "Warning" ] && type=2 + [ "$typestr" == "Error" ] && type=3 + [ "$typestr" == "Fatal" ] && type=4 + color=${COLORS[$type]} + endcolor=$OFF + echo -e "$color$@$endcolor" else - debug 0 "$returnstring" + echo -e "$@" fi - return $RUNERROR } -# We have the following debug levels: +# We have the following message levels: # 0 - debug - blue # 1 - normal messages - green # 2 - warnings - yellow @@ -66,33 +58,58 @@ function run() { # used to capture output from handlers echo_debug_msg=0 -function debug() { +usecolors=yes +function printmsg() { [ ${#@} -gt 1 ] || return - - TYPES=(Debug Info Warning Error Fatal) - COLOURS=($BLUE $GREEN $YELLOW $RED $PURPLE) + type=$1 - colour=${COLOURS[$type]} shift + if [ $type == 100 ]; then + typestr=`echo "$@" | sed 's/\(^[^:]*\).*$/\1/'` + [ "$typestr" == "Debug" ] && type=0 + [ "$typestr" == "Info" ] && type=1 + [ "$typestr" == "Warning" ] && type=2 + [ "$typestr" == "Error" ] && type=3 + [ "$typestr" == "Fatal" ] && type=4 + typestr="" + else + types=(Debug Info Warning Error Fatal) + typestr="${types[$type]}: " + fi + print=$[4-type] - if [ "$print" -lt "$loglevel" -o "$DEBUG" == 1 ]; then - if [ -z "$logfile" ]; then - echo -e "${colour}${TYPES[$type]}: $@${OFF}" >&2 - else - if [ "$DEBUG" == 1 -o "$type" == 4 ]; then - echo -e "${colour}${TYPES[$type]}: $@${OFF}" >&2 - fi - echo -e "${colour}${TYPES[$type]}: $@${OFF}" >> $logfile - fi + + if [ $echo_debug_msg == 1 ]; then + echo -e "$typestr$@" >&2 + elif [ $debug ]; then + colorize "$typestr$@" >&2 fi - if [ "$echo_debug_msg" != "0" -a "$type" -gt "1" ]; then - echo -e "${TYPES[$type]}: $@" + + if [ $print -lt $loglevel ]; then + if [ -w "$logfile" ]; then + colorize "$typestr$@" >> $logfile + fi fi } +function passthru() { + printmsg 100 "$@" +} +function debug() { + printmsg 0 "$@" +} +function info() { + printmsg 1 "$@" +} +function warning() { + printmsg 2 "$@" +} +function error() { + printmsg 3 "$@" +} function fatal() { - debug 4 "$@" + printmsg 4 "$@" exit 2 } @@ -205,60 +222,172 @@ function isnow() { return 1 } -##################################################### -## MAIN +function usage() { + cat << EOF +$0 usage: +This script allows you to coordinate system backup by dropping a few +simple configuration files into /etc/backup.d/. Typically, this +script is run hourly from cron. -## process command line options +The following options are available: +-h, --help This usage message +-d, --debug Run in debug mode, where all log messages are + output to the current shell. +-f, --conffile FILE Use FILE for the main configuration instead + of /etc/backupninja.conf +-t, --test Run in test mode, no actions are actually taken. +-n, --now Perform actions now, instead of when they + might be scheduled. + --run FILE Execute the specified action file and then exit. +When using colored output, there are: +EOF + debug=1 + debug "Debugging info (when run with -d)" + info "Informational messages (verbosity level 4)" + warning "Warnings (verbosity level 3 and up)" + error "Errors (verbosity level 2 and up)" + fatal "Fatal, halting errors (always shown)" +} -if [ "$1" == "--help" ]; then - HELP=1;DEBUG=1;loglevel=4 -else - while getopts h,f:,d,t option - do - case "$option" in - h) HELP=1;DEBUG=1;loglevel=4;; - d) DEBUG=1;loglevel=4;; - f) CONFFILE="$OPTARG";; - t) test=1;DEBUG=1;; - esac - done -fi +## +## this function handles the running of a backup action +## +## these globals are modified: +## fatals, errors, warnings, actions_run, errormsg +## + +function process_action() { + local file="$1" + local suffix="$2" + + setfile $file + + # skip over this config if "when" option + # is not set to the current time. + getconf when "$defaultwhen" + if [ "$processnow" == 1 ]; then + info "running $file because of --now" + else + IFS=$'\t\n' + for w in $when; do + IFS=$' \t\n' + isnow "$w" + ret=$? + IFS=$'\t\n' + if [ $ret == 0 ]; then + debug "skipping $file because it is not $w" + return + else + info "running $file because it is $w" + fi + done + IFS=$' \t\n' + fi -setupcolors + let "actions_run += 1" + + # call the handler: + local bufferfile="/tmp/backupninja.buffer.$$" + echo "" > $bufferfile + echo_debug_msg=1 + ( + . $scriptdir/$suffix $file + ) 2>&1 | ( + while read a; do + echo $a >> $bufferfile + [ $debug ] && colorize "$a" + done + ) + retcode=$? + # ^^^^^^^^ we have a problem! we can't grab the return code "$?". grrr. + echo_debug_msg=0 + + _warnings=`cat $bufferfile | grep "^Warning: " | wc -l` + _errors=`cat $bufferfile | grep "^Error: " | wc -l` + _fatals=`cat $bufferfile | grep "^Fatal: " | wc -l` + + ret=`grep "\(^Warning: \|^Error: \|^Fatal: \)" $bufferfile` + rm $bufferfile + if [ $_fatals != 0 ]; then + msg "*failed* -- $file" + errormsg="$errormsg\n== failures from $file ==\n\n$ret\n" + elif [ $_errors != 0 ]; then + msg "*error* -- $file" + errormsg="$errormsg\n== errors from $file ==\n\n$ret\n" + elif [ $_warnings != 0 ]; then + msg "*warning* -- $file" + errormsg="$errormsg\n== warnings from $file ==\n\n$ret\n" + else + msg "success -- $file" +# elif [ $retcode == 0 ]; then +# msg "success -- $file" +# else +# msg "unknown -- $file" + fi -## Print help + let "fatals += _fatals" + let "errors += _errors" + let "warnings += _warnings" +} -if [ "$HELP" == 1 ]; then -cat << EOF -$0 usage: -This script allows you to coordinate system backup by dropping a few -simple configuration files into /etc/backup.d/. In general, this script -is run from a cron job late at night. +##################################################### +## MAIN -The following options are available: --h This help message --d Run in debug mode, where all log messages are output to the current shell. --f Use for the main configuration instead of /etc/backupninja.conf --t Run in test mode, no actions are actually taken. +setupcolors +conffile="/etc/backupninja.conf" +loglevel=3 -When using colored output, there are: -EOF -debug 0 "Debugging info (when run with -d)" -debug 1 "Informational messages (verbosity level 4)" -debug 2 "Warnings (verbosity level 3 and up)" -debug 3 "Errors (verbosity level 2 and up)" -debug 4 "Fatal, halting errors (always shown)" -exit 0 -fi +## process command line options + +while [ $# -ge 1 ]; do + case $1 in + -h|--help) usage;; + -d|--debug) debug=1;; + -t|--test) test=1;debug=1;; + -n|--now) processnow=1;; + -f|--conffile) + if [ -f $2 ]; then + conffile=$2 + else + fatal "-f|--conffile option must be followed by an existing filename" + usage + fi + # we shift here to avoid processing the file path + shift + ;; + --run) + debug=1 + if [ -f $2 ]; then + singlerun=$2 + processnow=1 + else + fatal "--run option must be fallowed by a backupninja action file" + usage + fi + shift + ;; + *) + debug=1 + fatal "Unknown option $1" + usage + exit + ;; + esac + shift +done + +#if [ $debug ]; then +# usercolors=yes +#fi ## Load and confirm basic configuration values # bootstrap -[ -r "$CONFFILE" ] || fatal "Configuration file $CONFFILE not found." -scriptdir=`grep scriptdirectory $CONFFILE | awk '{print $3}'` -[ -n "$scriptdir" ] || fatal "Cound not find entry 'scriptdirectory' in $CONFFILE." +[ -r "$conffile" ] || fatal "Configuration file $conffile not found." +scriptdir=`grep scriptdirectory $conffile | awk '{print $3}'` +[ -n "$scriptdir" ] || fatal "Cound not find entry 'scriptdirectory' in $conffile" [ -d "$scriptdir" ] || fatal "Script directory $scriptdir not found." -setfile $CONFFILE +setfile $conffile # get global config options (second param is the default) getconf configdirectory /etc/backup.d @@ -269,87 +398,64 @@ getconf loglevel 3 getconf when "Everyday at 01:00" defaultwhen=$when getconf logfile /var/log/backupninja.log +getconf usecolors "yes" getconf SLAPCAT /usr/sbin/slapcat getconf RDIFFBACKUP /usr/bin/rdiff-backup getconf MYSQL /usr/bin/mysql getconf MYSQLHOTCOPY /usr/bin/mysqlhotcopy getconf MYSQLDUMP /usr/bin/mysqldump getconf GZIP /bin/gzip +getconf RSYNC /usr/bin/rsync [ -d "$configdirectory" ] || fatal "Configuration directory '$configdirectory' not found." -[ `id -u` == "0" ] || fatal "Can only be run as root" -## Process each configuration file +if [ "$UID" != "0" ]; then + echo "$0 can only be run as root" + exit 1 +fi -debug 1 "====== starting at "`date`" ======" +## Process each configuration file # by default, don't make files which are world or group readable. umask 077 +# these globals are set by process_action() +fatals=0 errors=0 +warnings=0 +actions_run=0 +errormsg="" + +if [ "$singlerun" ]; then + files=$singlerun +else + files=`find $configdirectory -mindepth 1 | sort -n` +fi -for file in $configdirectory/*; do - [ -f $file ] || continue; +for file in $files; do + [ -f "$file" ] || continue check_perms $file suffix="${file##*.}" base=`basename $file` if [ "${base:0:1}" == "0" ]; then - debug 1 "Skipping $file" + info "Skipping $file" continue - else - debug 1 "Processing $file" fi if [ -e "$scriptdir/$suffix" ]; then - setfile $file - - # skip over this config if "when" option - # is not set to the current time. - getconf when "$defaultwhen" - IFS=$'\t\n' - for w in $when; do - IFS=$' \t\n' - isnow "$w" - ret=$? - IFS=$'\t\n' - if [ $ret == 0 ]; then - debug 0 "skipping $file because it is not $w" - continue - else - debug 0 "running $file because it is $w" - continue - fi - done - IFS=$' \t\n' - - echo_debug_msg=1 - # call the handler: - ret=`( . $scriptdir/$suffix $file )` - retcode="$?" - warnings=`echo $ret | grep -e "^Warning: " | wc -l` - errors=`echo $ret | grep -e "^Error: \|^Fatal: " | wc -l` - if [ $errors != 0 ]; then - msg "*failed* -- $file" - errormsg="$error\n== errors from $file ==\n\n$ret\n" - elif [ $warnings != 0 ]; then - msg "*warning* -- $file" - errormsg="$error\n== warnings from $file ==\n\n$ret\n" - elif [ $retcode == 0 ]; then - msg "success -- $file" - else - msg "unknown -- $file" - fi - echo_debug_msg=0 + process_action $file $suffix else - debug 3 "Can't process file '$file': no handler script for suffix '$suffix'" + error "Can't process file '$file': no handler script for suffix '$suffix'" msg "*missing handler* -- $file" fi done ## mail the messages to the report address -if [ "$reportemail" == "" ]; then doit=0 +if [ $actions_run == 0 ]; then doit=0 +elif [ "$reportemail" == "" ]; then doit=0 +elif [ $fatals != 0 ]; then doit=1 elif [ $errors != 0 ]; then doit=1 elif [ "$reportsuccess" == "yes" ]; then doit=1 elif [ "$reportwarning" == "yes" -a $warnings != 0 ]; then doit=1 @@ -357,15 +463,17 @@ else doit=0 fi if [ $doit == 1 ]; then + debug "send report to $reportemail" hostname=`hostname` + [ $warnings == 0 ] || subject="WARNING" + [ $errors == 0 ] || subject="ERROR" + [ $fatals == 0 ] || subject="FAILED" + { for ((i=0; i < ${#messages[@]} ; i++)); do echo ${messages[$i]} done echo -e "$errormsg" - } | mail $reportemail -s "backupninja: $hostname" + } | mail $reportemail -s "backupninja: $hostname $subject" fi -debug 1 "====== finished at "`date`" ======" - -############################################################