1#! /bin/sh 2# This program is part of GNU tar 3# Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. 4# 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 1, or (at your option) 8# any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program; if not, write to the Free Software 17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18# 02110-1301, USA. 19 20PROGNAME=`basename $0` 21CONFIGPATH="$SYSCONFDIR/backup" 22REMOTEBACKUPDIR="$SYSCONFDIR/tar-backup" 23CONFIGFILE=${CONFIGPATH}/backup-specs 24DIRLIST=${CONFIGPATH}/dirs 25FILELIST=${CONFIGPATH}/files 26LOGPATH=${CONFIGPATH}/log 27 28# Default functions for running various magnetic tape commands 29mt_begin() { 30 $MT -f "$1" retension 31} 32 33mt_rewind() { 34 $MT -f "$1" rewind 35} 36 37mt_offline() { 38 $MT -f "$1" offl 39} 40 41mt_status() { 42 $MT -f "$1" status 43} 44 45# The main configuration file may override any of these variables 46MT_BEGIN=mt_begin 47MT_REWIND=mt_rewind 48MT_OFFLINE=mt_offline 49MT_STATUS=mt_status 50 51# Insure `mail' is in PATH. 52PATH="/usr/ucb:${PATH}" 53export PATH 54# Put startdate in the subject line of mailed report, since if it happens 55# to run longer than 24 hours (as may be the case if someone forgets to put 56# in the next volume of the tape in adequate time), the backup date won't 57# appear too misleading. 58startdate="`date`" 59here="`pwd`" 60# Save local hostname 61localhost="`hostname | sed -e 's/\..*//' | tr A-Z a-z`" 62 63# Produce a diagnostic output 64message() { 65 if [ "$VERBOSE" != "" ]; then 66 if [ $VERBOSE -ge $1 ]; then 67 shift 68 echo "$@" >&2 69 fi 70 fi 71} 72 73# Bail out and exit. 74bailout() { 75 echo "$PROGNAME: $*" >&2 76 exit 1 77} 78 79# Return current date 80now() { 81#IF_DATE_FORMAT_OK 82 date +%Y-%m-%d 83#ELSE_DATE_FORMAT_OK 84 LC_ALL=C date | \ 85 sed 's/[^ ]* *\([^ ]*\) *\([^ ]*\).* \([^ ]*\)$/\3-\1-\2/ 86 /-[0-9]$/s/\([0-9]\)$/0\1/ 87 /Jan/{s/Jan/01/p;q;} 88 /Feb/{s/Feb/02/p;q;} 89 /Mar/{s/Mar/03/p;q;} 90 /Apr/{s/Apr/04/p;q;} 91 /May/{s/May/05/p;q;} 92 /Jun/{s/Jun/06/p;q;} 93 /Jul/{s/Jul/07/p;q;} 94 /Aug/{s/Aug/08/p;q;} 95 /Sep/{s/Sep/09/p;q;} 96 /Oct/{s/Oct/10/p;q;} 97 /Nov/{s/Nov/11/p;q;} 98 /Dec/{s/Dec/12/p;q;}' 99#ENDIF_DATE_FORMAT_OK 100} 101 102# Bail out if we don't have root privileges. 103test_root() { 104 if [ ! -w ${ROOT_FS-/} ]; then 105 bailout "The backup must be run as root or else some files will fail to be dumped." 106 fi 107} 108 109root_fs() { 110 echo "${ROOT_FS}$1" | tr -s / 111} 112 113advice() { 114 echo "Directory $1 is not found." >&2 115 cat >&2 <<EOF 116The following directories and files are needed for the backup to function: 117 1181. Directory with configuration files and file lists: 119$CONFIGPATH 1202. Directory for backup log files 121$LOGPATH 1223. Main configuration file 123$CONFIGFILE 124 125Please, create these and invoke the script again. 126EOF 127} 128 129init_common() { 130 # Check if the necessary directories exist 131 if [ ! -d $CONFIGPATH ]; then 132 advice $CONFIGPATH 133 exit 1 134 fi 135 if [ ! -d $LOGPATH ]; then 136 if mkdir $LOGPATH; then 137 : 138 else 139 advice $LOGPATH 140 exit 1 141 fi 142 fi 143 # Get the values of BACKUP_DIRS, BACKUP_FILES, and other variables. 144 if [ ! -r $CONFIGFILE ]; then 145 echo "$PROGNAME: cannot read $CONFIGFILE. Stop." >&2 146 exit 1 147 fi 148 . $CONFIGFILE 149 150 # Environment sanity check 151 152 test_root 153 154 if [ x"${ADMINISTRATOR}" = x ]; then 155 bailout "ADMINISTRATOR not defined" 156 fi 157 158 [ x"$TAR" = x ] && TAR=tar 159 [ x"$SLEEP_TIME" = x ] && SLEEP_TIME=60 160 161 if [ x$VOLNO_FILE = x ]; then 162 bailout "VOLNO_FILE not specified" 163 fi 164 165 if [ -r $DIRLIST ]; then 166 BACKUP_DIRS="$BACKUP_DIRS `cat $DIRLIST`" 167 fi 168 if [ -r $FILELIST ]; then 169 BACKUP_FILES="$BACKUP_FILES `cat $FILELIST`" 170 fi 171 172 if [ \( x"$BACKUP_DIRS" = x \) -a \( x"$BACKUP_FILES" = x \) ]; then 173 bailout "Neither BACKUP_DIRS nor BACKUP_FILES specified" 174 fi 175 if [ -z "$RSH" ]; then 176 RSH=rsh 177 MT_RSH_OPTION= 178 else 179 MT_RSH_OPTION="--rsh-command=$RSH" 180 fi 181 if [ -z "$TAPE_FILE" ]; then 182 TAPE_FILE=/dev/tape 183 fi 184 185 # If TAPE_FILE is a remote device, update mt invocation accordingly 186 : ${MT:=mt} 187 case $TAPE_FILE in 188 *:*) MT="$MT $MT_RSH_OPTION";; 189 *) ;; 190 esac 191 192 POSIXLY_CORRECT=1 193 export POSIXLY_CORRECT 194} 195 196init_backup() { 197 init_common 198 TAR_PART1="${TAR} -c --format=gnu --multi-volume --one-file-system --sparse --volno-file=${VOLNO_FILE}" 199 if [ "x$XLIST" != x ]; then 200 TAR_PART1="${TAR_PART1} \`test -r $REMOTEBACKUPDIR/$XLIST && echo \"--exclude-from $REMOTEBACKUPDIR/$XLIST\"\`" 201 fi 202 if [ "$RSH_COMMAND" != "" ]; then 203 TAR_PART1="${TAR_PART1} --rsh-command=$RSH_COMMAND" 204 fi 205 if [ x$BLOCKING != x ]; then 206 TAR_PART1="${TAR_PART1} --blocking=${BLOCKING}" 207 fi 208 209 # Only use --info-script if DUMP_REMIND_SCRIPT was defined in backup-specs 210 if [ "x${DUMP_REMIND_SCRIPT}" != "x" ]; then 211 TAR_PART1="${TAR_PART1} --info-script='${DUMP_REMIND_SCRIPT}'" 212 fi 213 # Set logfile name 214 # Logfile name should be in the form ``log-1993-03-18-level-0'' 215 # They go in the directory `@sysconfdir@/log'. 216 # i.e. year-month-date. This format is useful for sorting by name, since 217 # logfiles are intentionally kept online for future reference. 218 LOGFILE="${LOGPATH}/log-`now`-level-${DUMP_LEVEL}" 219} 220 221init_restore() { 222 init_common 223 # FIXME: Replace --list with --extract 224 TAR_PART1="${TAR} --extract --multi-volume" 225 if [ "$RSH_COMMAND" != "" ]; then 226 TAR_PART1="${TAR_PART1} --rsh-command=$RSH_COMMAND" 227 fi 228 if [ x$BLOCKING != x ]; then 229 TAR_PART1="${TAR_PART1} --blocking=${BLOCKING}" 230 fi 231 232 # Only use --info-script if DUMP_REMIND_SCRIPT was defined in backup-specs 233 if [ "x${DUMP_REMIND_SCRIPT}" != "x" ]; then 234 TAR_PART1="${TAR_PART1} --info-script='${DUMP_REMIND_SCRIPT}'" 235 fi 236 LOGFILE="${LOGPATH}/restore-`now`" 237} 238 239wait_time() { 240 if [ "${1}" != "now" ]; then 241 if [ "${1}x" != "x" ]; then 242 spec="${1}" 243 else 244 spec="${BACKUP_HOUR}" 245 fi 246 247 pausetime="`date | awk -v spec=\"${spec}\" ' 248 BEGIN { 249 split(spec, time, ":") 250 } 251 { 252 split($4, now, ":") 253 diff = 3600 * (time[1] - now[1]) + 60 * (time[2] - now[2]); 254 if (diff < 0) 255 diff += 3600 * 24 256 print diff 257 }'`" 258 clear 259 echo "${SLEEP_MESSAGE}" 260 sleep "${pausetime}" 261 fi 262} 263 264level_log_name() { 265 echo "$REMOTEBACKUPDIR/${1}.level-${2-$DUMP_LEVEL}" 266} 267 268# Prepare a temporary level logfile 269# usage: make_level_log HOSTNAME 270make_level_log() { 271 if [ "z${localhost}" != "z$1" ] ; then 272 $RSH "$1" mkdir $REMOTEBACKUPDIR > /dev/null 2>&1 273 $RSH "$1" rm -f `level_log_name temp` 274 else 275 mkdir $REMOTEBACKUPDIR > /dev/null 2>&1 276 rm -f `level_log_name temp` 277 fi 278} 279 280# Rename temporary log 281# usage: flush_level_log HOSTNAME FSNAME 282flush_level_log() { 283 message 10 "RENAME: `level_log_name temp` --> `level_log_name $2`" 284 if [ "z${localhost}" != "z$1" ] ; then 285 $RSH "$1" mv -f `level_log_name temp` "`level_log_name $2`" 286 else 287 mv -f `level_log_name temp` "`level_log_name $2`" 288 fi 289} 290 291# Return the timestamp of the last backup. 292# usage: get_dump_time LEVEL 293get_dump_time() { 294 ls -r ${LOGPATH}/log-*-level-$1 \ 295 | head -n 1 \ 296 | sed "s,.*log-\(.*\)-level-$1,\1," 297} 298 299# Do actual backup on a host 300# usage: backup_host HOSTNAME [TAR_ARGUMENTS] 301backup_host() { 302 message 10 "ARGS: $@" 303 rhost=$1 304 shift 305 if [ "z${localhost}" != "z$rhost" ] ; then 306 $RSH "$rhost" ${TAR_PART1} -f "${localhost}:${TAPE_FILE}" $@ 307 else 308 # Using `sh -c exec' causes nested quoting and shell substitution 309 # to be handled here in the same way rsh handles it. 310 CMD="exec ${TAR_PART1} -f \"${TAPE_FILE}\" $@" 311 message 10 "CMD: $CMD" 312 sh -c "$CMD" 313 message 10 "RC: $?" 314 fi 315} 316 317print_level() { 318 if [ ${1-$DUMP_LEVEL} -eq 0 ]; then 319 echo "Full" 320 else 321 echo "Level ${1-$DUMP_LEVEL}" 322 fi 323} 324 325prev_level() { 326 print_level `expr $DUMP_LEVEL - 1` | tr A-Z a-z 327} 328 329remote_run() { 330 rhost=$1 331 shift 332 message 10 "REMOTE $rhost: $@" 333 if [ "x$rhost" != "x${localhost}" ] ; then 334 $RSH "${rhost}" "$@" 335 else 336 $* 337 fi 338} 339 340license() { 341 cat - <<EOF 342Copyright (C) 2006 Free Software Foundation, Inc. 343This is free software. You may redistribute copies of it under the terms of 344the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. 345There is NO WARRANTY, to the extent permitted by law. 346EOF 347} 348