# -*- mode: sh; sh-basic-offset: 3; indent-tabs-mode: nil; -*- # vim: set filetype=sh sw=3 sts=3 expandtab autoindent: # # rdiff-backup handler script for backupninja # requires rdiff-backup # ### FUNCTIONS ### function test_connection() { # given a user and host, # tests the connection. # if user or host is missing, returns 0 # (ie, assume it's a local connection). if [ $# -lt 2 ]; then debug "(local is assumed to be a good connection)" return 0 fi local user=$1 local host=$2 debug "ssh $sshoptions -o PasswordAuthentication=no $host -l $user 'echo -n 1'" local ret=`ssh $sshoptions -o PasswordAuthentication=no $host -l $user 'echo -n host is alive'` if echo $ret | grep "host is alive"; then debug "Connected to $host as $user successfully" else fatal "Can't connect to $host as $user." fi } function get_version() { # given no arguments, returns the local version. # given a user and host, returns the remote version. # if user or host is missing, returns the local version. local version if [ "$#" -lt 2 ]; then debug "$RDIFFBACKUP -V" echo `$RDIFFBACKUP -V` else local user=$1 local host=$2 debug "ssh $sshoptions $host -l $user '$RDIFFBACKUP -V'" version=`ssh $sshoptions $host -l $user "$RDIFFBACKUP -V"` if [ $? = 127 ]; then fatal "Unable to execute rdiff-backup on remote server. It probably isn't installed" else echo "$version" | grep rdiff-backup fi fi } function check_consistency() { local section=$1 local type=$2 local user=$3 local host=$4 if [ "$type" == "local" ]; then if [ "$user" != "" ]; then warning "User should not be specified for local $section." fi if [ "$host" != "" ]; then warning "Host should not be specified for local $section." fi fi if [ "$type" == "remote" ]; then if [ "$user" == "" ]; then fatal "User must be specified for remote $section." fi if [ "$host" == "" ]; then fatal "Host must be specifed for remote $section." fi fi } function check_cstream() { local cstream=$1 if [ ! -x $cstream ]; then fatal "Can't find your cstream binary (trying: $cstream). If you use bwlimit you must have cstream installed." fi } ### GET CONFIG ### getconf options getconf testconnect yes getconf nicelevel 0 getconf ionicelevel getconf bwlimit getconf ignore_version no getconf output_as_info no setsection source getconf type; sourcetype=$type getconf user; sourceuser=$user getconf host; sourcehost=$host check_consistency "source" "$type" "$user" "$host" getconf label getconf keep 60 getconf include getconf vsnames all getconf vsinclude getconf exclude setsection dest getconf directory; destdir=$directory # strip trailing / destdir=${destdir%/} getconf type; desttype=$type getconf user; destuser=$user getconf host; desthost=$host getconf sshoptions check_consistency "destination" "$type" "$user" "$host" if [ -n "$sshoptions" ] && echo $options | grep -qv "remote-schema"; then options="$options --remote-schema 'ssh -C $sshoptions %s rdiff-backup --server'" fi ### CHECK CONFIG ### # If vservers are configured, check that the ones listed in $vsnames do exist. usevserver=no if [ $vservers_are_available = yes ]; then if [ "$vsnames" = all ]; then vsnames="$found_vservers" else if ! vservers_exist "$vsnames" ; then fatal "At least one of the vservers listed in vsnames ($vsnames) does not exist." fi fi if [ -n "$vsinclude" ]; then info "Using vservers '$vsnames'" usevserver=yes fi else [ -z "$vsinclude" ] || warning 'vservers support disabled in backupninja.conf, vsincludes configuration lines will be ignored' fi # Check that the ionicelevel is valid if [ -n "$ionicelevel" ] && echo "$ionicelevel" | grep -vq "^[0-7]$"; then fatal "The value of ionicelevel is expected to be either empty or an integer from 0 to 7. Got: $ionicelevel" fi # Only use ionice if ionicelevel is not empty nice="nice -n $nicelevel" if [ -n "$ionicelevel" ]; then nice="ionice -c2 -n $ionicelevel $nice" fi # check the connection at the source and destination [ -n "$test" ] || test=0 if [ "$testconnect" = "yes" ] || [ "${test}" -eq 1 ]; then test_connection $sourceuser $sourcehost test_connection $destuser $desthost fi if [ "$ignore_version" != "yes" ]; then # see that rdiff-backup has the same version at the source and destination sourceversion=`get_version $sourceuser $sourcehost` destversion=`get_version $destuser $desthost` if [ "$sourceversion" != "$destversion" ]; then fatal "rdiff-backup does not have the same version at the source and at the destination." fi fi # source specific checks case $sourcetype in remote ) execstr_sourcepart="$sourceuser@$sourcehost::/" ;; local ) execstr_sourcepart="/" ;; * ) fatal "sourcetype '$sourcetype' is neither local nor remote" ;; esac # destination specific checks [ "$destdir" != "" ] || fatal "Destination directory not set" case $desttype in remote ) execstr_destpart="$destuser@$desthost::$destdir/$label" ;; local ) execstr_destpart="$destdir/$label" ;; * ) fatal "desttype '$desttype' is neither local nor remote" ;; esac ### REMOVE OLD BACKUPS ### if [ "$keep" != yes ]; then if [ "`echo $keep | tr -d 0-9`" == "" ]; then # add D if no other date unit is specified keep="${keep}D" fi removestr="$RDIFFBACKUP $options --force --remove-older-than $keep " if [ "$desttype" == "remote" ]; then removestr="${removestr}${destuser}@${desthost}::" fi removestr="${removestr}${destdir}/${label}"; debug "$removestr" if [ $test = 0 ]; then output="`su -c "$removestr" 2>&1`" if [ $? = 0 ]; then if [ "$output_as_info" == "yes" ]; then info "$output" else debug "$output" fi info "Removing backups older than $keep days succeeded." else warning "$output" warning "Failed removing backups older than $keep." fi fi fi # Add cstream if [ ! -z $bwlimit ]; then check_cstream $CSTREAM; if [ "$desttype" = "remote" ]; then RDIFFBACKUP="$RDIFFBACKUP --remote-schema 'cstream -t $bwlimit | ssh %s \''rdiff-backup --server\'''" elif [ "$sourcetype" = "remote" ]; then RDIFFBACKUP="$RDIFFBACKUP --remote-schema 'ssh %s \''rdiff-backup --server\'' | cstream -t $bwlimit'" else fatal "You specified a bandwidth limit but neither your source nor destination types are remote." fi fi ### EXECUTE ### execstr="$RDIFFBACKUP $options --print-statistics " set -o noglob symlinks_warning="Maybe you have mixed symlinks and '*' in this statement, which is not supported." # TODO: order the includes and excludes # excludes SAVEIFS=$IFS IFS=$(echo -en "\n\b") for i in $exclude; do str="${i//__star__/*}" case "$str" in @*) execstr="${execstr}--exclude-globbing-filelist '${str#@}' " ;; *) execstr="${execstr}--exclude '$str' " ;; esac done IFS=$SAVEIFS # includes SAVEIFS=$IFS IFS=$(echo -en "\n\b") for i in $include; do [ "$i" != "/" ] || fatal "Sorry, you cannot use 'include = /'" str="${i//__star__/*}" case "$str" in @*) execstr="${execstr}--include-globbing-filelist '${str#@}' " ;; *) execstr="${execstr}--include '$str' " ;; esac done IFS=$SAVEIFS # vsinclude if [ $usevserver = yes ]; then for vserver in $vsnames; do SAVEIFS=$IFS IFS=$(echo -en "\n\b") for vi in $vsinclude; do str="${vi//__star__/*}" str="$VROOTDIR/$vserver$str" if [ -n "$str" ]; then execstr="${execstr}--include '$str' " else warning "vsinclude statement '${vi//__star__/*}' will be ignored for VServer $vserver. $symlinks_warning" fi done IFS=$SAVEIFS done fi set +o noglob # exclude everything else [ "$include" != "" -o "$vsinclude" != "" ] && execstr="${execstr}--exclude '/*' " # include client-part and server-part execstr="${execstr}$execstr_sourcepart $execstr_destpart" debug "$nice $execstr" if [ $test = 0 ]; then output=`$nice su -c "$execstr" 2>&1` if [ $? = 0 ]; then if [ "$output_as_info" == "yes" ]; then info "$output" else debug "$output" fi info "Successfully finished backing up source $label" else error "$output" fatal "Failed backup up source $label" fi fi return 0