changed and improved the log file output
[matthijs/upstream/backupninja.git] / backupninja
index 962ff899543336621a37896cf9204fc10a9613b8..6b888cb006e85609f04bbc2a369184510aaec687 100755 (executable)
@@ -3,7 +3,7 @@
 # B A C K U P N I N J A   /()/
 #                         `\|
 #
-# Copyright (C) 2004 riseup.net -- property is theft.
+# Copyright (C) 2004-05 riseup.net -- property is theft.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -27,22 +27,24 @@ function setupcolors() {
        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"
-#      else
-#              debug 0 "$returnstring"
-#      fi
-#      return $RUNERROR
-#}
+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
+               echo -e "$@"
+       fi
+}
 
 # We have the following message levels:
 # 0 - debug - blue
@@ -56,38 +58,48 @@ function setupcolors() {
 # used to capture output from handlers
 echo_debug_msg=0
 
-usecolor=yes
+usecolors=yes
 
 function printmsg() {
        [ ${#@} -gt 1 ] || return
-        
-       types=(Debug Info Warning Error Fatal)
+
        type=$1
-       print=$[4-type]
-       typestr=${types[$type]}
-       if [ "$usecolor" == "yes" ]; then
-               colors=($BLUE $GREEN $YELLOW $RED $PURPLE)
-               color=${colors[$type]}
-               endcolor=$OFF
-       fi
-       
        shift
-       
-       if [ "$echo_debug_msg" != "0" -a "$type" -gt "1" ]; then
-               echo -e "$typestr: $@"
+       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
        
-       if [ "$debug" == 1 ]; then
-               echo -e "${color}$typestr: $@${endcolor}" >&2
+       print=$[4-type]
+       
+       if [ $echo_debug_msg == 1 ]; then
+               echo -e "$typestr$@" >&2
+       elif [ $debug ]; then
+               colorize "$typestr$@" >&2
        fi
        
        if [ $print -lt $loglevel ]; then
-               if [ -w "$logfile" ]; then
-                       echo -e "${color}$typestr: $@${endcolor}" >> $logfile
-               fi
+               logmsg "$typestr$@"
+       fi
+}
+
+function logmsg() {
+       if [ -w "$logfile" ]; then
+               echo -e `date "+%h %d %H:%M:%S"` "$@" >> $logfile
        fi
 }
 
+function passthru() {
+       printmsg 100 "$@"
+}
 function debug() {
        printmsg 0 "$@"
 }
@@ -151,9 +163,11 @@ function check_perms() {
        local perms=`ls -ld $file`
        perms=${perms:4:6}
        if [ "$perms" != "------" ]; then
+               echo "Configuration files must not be group or world readable! Dying on file $file"
                fatal "Configuration files must not be group or world readable! Dying on file $file"
        fi
        if [ `ls -ld $file | awk '{print $3}'` != "root" ]; then
+               echo "Configuration files must be owned by root! Dying on file $file"
                fatal "Configuration files must be owned by root! Dying on file $file"
        fi
 }
@@ -191,9 +205,9 @@ function isnow() {
        whendayofweek=$1; at=$2; whentime=$3;
        whenday=`toint "$whendayofweek"`
        whendayofweek=`tolower "$whendayofweek"`
-       whentime=`echo "$whentime" | sed 's/:[0-9][0-9]$//'`
+       whentime=`echo "$whentime" | sed 's/:[0-9][0-9]$//' | sed -r 's/^([0-9])$/0\1/'`
 
-       if [ "$whendayofweek" == "everyday" ]; then
+       if [ "$whendayofweek" == "everyday" -o "$whendayofweek" == "daily" ]; then
                whendayofweek=$nowdayofweek
        fi
 
@@ -230,6 +244,7 @@ The following options are available:
 -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
@@ -240,6 +255,13 @@ EOF
        fatal   "Fatal, halting errors (always shown)"
 }
 
+##
+## 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"
@@ -250,7 +272,9 @@ function process_action() {
        # is not set to the current time.
        getconf when "$defaultwhen"
        if [ "$processnow" == 1 ]; then
-               info "running $file because of --now"
+               info ">>>> starting action $file (because of --now)"
+       elif [ "$when" == "hourly" ]; then
+               info ">>>> starting action $file (because 'when = hourly')"
        else
                IFS=$'\t\n'
                for w in $when; do
@@ -262,30 +286,55 @@ function process_action() {
                                debug "skipping $file because it is not $w"
                                return
                        else
-                               info "running $file because it is $w"
+                               info ">>>> starting action $file (because it is $w)"
                        fi
                done
                IFS=$' \t\n'
        fi
        
-       echo_debug_msg=1
+       let "actions_run += 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
+       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"
+               passthru "Fatal: <<<< finished action $file: FAILED"
+       elif [ $_errors != 0 ]; then
+               msg "*error* -- $file"
                errormsg="$errormsg\n== errors from $file ==\n\n$ret\n"
-       elif [ $warnings != 0 ]; then
+               error "<<<< finished action $file: ERROR"
+       elif [ $_warnings != 0 ]; then
                msg "*warning* -- $file"
                errormsg="$errormsg\n== warnings from $file ==\n\n$ret\n"
-       elif [ $retcode == 0 ]; then
-               msg "success -- $file"
+               warning "<<<< finished action $file: WARNING"
        else
-               msg "unknown -- $file"
+               msg "success -- $file"
+               info "<<<< finished action $file: SUCCESS"
        fi
-       echo_debug_msg=0
+
+       let "fatals += _fatals"
+       let "errors += _errors"
+       let "warnings += _warnings"     
 }
 
 #####################################################
@@ -307,27 +356,59 @@ while [ $# -ge 1 ]; do
                        if [ -f $2 ]; then
                                conffile=$2
                        else
+                               echo "-f|--conffile option must be followed by an existing filename"
                                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
+                               echo "--run option must be fallowed by a backupninja action file"
+                               fatal "--run option must be fallowed by a backupninja action file"
+                               usage
+                       fi
+                       shift
+                       ;;
                *)
+                       debug=1
+                       echo "Unknown option $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."
+if [ ! -r "$conffile" ]; then
+       echo "Configuration file $conffile not found." 
+       fatal "Configuration file $conffile not found."
+fi
+
 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."
+if [ ! -n "$scriptdir" ]; then
+       echo "Cound not find entry 'scriptdirectory' in $conffile" 
+       fatal "Cound not find entry 'scriptdirectory' in $conffile"
+fi
+
+if [ ! -d "$scriptdir" ]; then
+       echo "Script directory $scriptdir not found." 
+       fatal "Script directory $scriptdir not found."
+fi
+
 setfile $conffile
 
 # get global config options (second param is the default)
@@ -341,30 +422,55 @@ defaultwhen=$when
 getconf logfile /var/log/backupninja.log
 getconf usecolors "yes"
 getconf SLAPCAT /usr/sbin/slapcat
+getconf LDAPSEARCH /usr/bin/ldapsearch
 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
+getconf vservers no
+getconf VSERVERINFO /usr/sbin/vserver-info
+getconf VSERVER /usr/sbin/vserver
+getconf VROOTDIR `if [ -f "$VSERVERINFO" ]; then $VSERVERINFO info SYSINFO |grep vserver-Rootdir | awk '{print $2}'; fi`
+
+if [ ! -d "$configdirectory" ]; then
+       echo "Configuration directory '$configdirectory' not found."
+       fatal "Configuration directory '$configdirectory' not found."
+fi
 
-[ -d "$configdirectory" ] || fatal "Configuration directory '$configdirectory' not found."
+[ -f "$logfile" ] || touch $logfile
 
 if [ "$UID" != "0" ]; then
        echo "$0 can only be run as root"
        exit 1
 fi
 
-## Process each configuration file
+if [ "$vservers" == "yes" -a ! -d "$VROOTDIR" ]; then
+       echo "vservers option set in config, but $VROOTDIR is not a directory!"
+       fatal "vservers option set in config, but $VROOTDIR is not a directory!"
+fi
 
-info "====== 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##*.}"
@@ -384,7 +490,9 @@ 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
@@ -392,15 +500,20 @@ 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
 
-info "====== finished at "`date`" ======"
-
-############################################################
+if [ $actions_run != 0 ]; then
+       info "FINISHED: $actions_run actions run. $fatals fatal. $errors error. $warnings warning."
+fi