#!/bin/ksh ### # This script is called by an Amanda client to run GNU tar. We look # through the arguments for what is being processed and (optionally) # run extra things before and/or after GNU tar, or alter the actual # command run (e.g. different GNU tar flags and options or a completely # different program). # # Remember that this script runs as root under the Amanda runtar program. # Exercise appropriate caution. # # To test, run with the DEBUG environment variable set to "echo". # The command line need not look exactly like what Amanda issues, but # must include the --directory and --file # flags as well as the trailing ".". To test an estimate, set the # output file to "/dev/null": # # env DEBUG=echo ./gtar-wrapper.ksh --directory /whatever --file /dev/null . # # and to test the real dump, set it to "-": # # env DEBUG=echo ./gtar-wrapper.ksh --directory /whatever --file - . # # John R. Jackson (jrj AT purdue DOT edu) # Purdue University Computing Center # # 24-Mar-00: Initial version. # # 12-Sep-01: Fixed several bugs (sigh) and converted the code so it would # work under bash as well as ksh. ### PN=${0##*/} if [[ -z $DEBUG ]] then log=/tmp/amanda/$PN.$$ # <<< change as needed rm -f $log else log=/dev/tty fi GTAR=${GTAR:-/opt/tar/bin/gtar} # <<< change as needed ### # Define functions to be called at various execution points. The name # determines when the function is called: # # pre_estimate_* called before a GNU tar estimate # pre_real_* called before a GNU tar real run # run_estimate_* called to run a GNU tar estimate # run_real_* called to run a GNU tar real run # post_estimate_* called after a GNU tar estimate # post_real_* called after a GNU tar real run # # The rest of the function name comes from the directory being processed # by converting everything other than alphanumerics and the underscore # to an underscore, e.g. /home/abc becomes _home_abc. # # If a function does not exist for a particular execution point, nothing # special is done. ### ### # The following set of functions (*_some_db) show how you might set # up the execution points to shut down and restart a database around # the Amanda steps. ### ### # This function is called before we get the estimate of /some/db. ### function pre_estimate__some_db { echo "$PN: shutting down the database" >> $log } ### # This function is called after we get the estimate of /some/db. ### function post_estimate__some_db { echo "$PN: starting the database" >> $log } ### # This function is called before we dump /some/db. ### function pre_real__some_db { echo "$PN: shutting down the database" >> $log } ### # This function is called after we dump /some/db. ### function post_real__some_db { echo "$PN: starting the database" >> $log } ### # The following set of function are called to process groups of home # directories. The Amanda disklist entries are "faked" to indicate # a range of directories to be processed instead of a single item. # For instance, /home/HomeAC (which should not actually exist) would be # converted into all the directories in /home that start with a, b or c. # # This might be used to get around the current limit on the number of # "disks" Amanda can handle on a given client or to make it easier to # set up groups of things to back up without exclusion lists. ### function run_estimate__home_HomeAC { echo "$PN: preparing to estimate homes a* through c*" >> $log do_home a c "$@" return $? } function run_real__home_HomeAC { echo "$PN: preparing to dump homes a* through c*" >> $log do_home a c "$@" return $? } ### # The first and second args to do_home are the start and end names. # The remaining args are passed to GNU tar pretty much as is, except # --directory which is shortened to the home directory base. ### function do_home { ### # Copy all the args except the last one (which should be ".") and # tweak the --directory value. ### start=$1 shift end=$1 shift typeset -i do_directory=0 new_args= typeset -i first_arg=1 for arg do if ((first_arg == 0)) then a=$last_arg if ((do_directory)) then base_directory=${last_arg%/*} a=$base_directory do_directory=0 elif [[ X"$last_arg" = X"--directory" ]] then do_directory=1 # --directory dir-to-back-up fi new_args="$new_args $a" fi first_arg=0 last_arg=$arg done if [[ X"$base_directory" = X"" ]] then echo "$PN: cannot parse args: --directory not found" >> $log echo "$PN: cannot parse args: --directory not found" 1>&2 return 1 elif [[ X"$last_arg" != X"." ]] then echo "$PN: cannot parse args: . not at end" >> $log echo "$PN: cannot parse args: . not at end" 1>&2 return 1 fi \cd $base_directory || return 1 typeset -i s e s=36#$start e=36#$end ((s = s - 10)) # 'a' -> 0 ((e = e - 10)) # 'z' -> 25 alpha[0]=a alpha[1]=b alpha[2]=c alpha[3]=d alpha[4]=e alpha[5]=f alpha[6]=g alpha[7]=h alpha[8]=i alpha[9]=j alpha[10]=k alpha[11]=l alpha[12]=m alpha[13]=n alpha[14]=o alpha[15]=p alpha[16]=q alpha[17]=r alpha[18]=s alpha[19]=t alpha[20]=u alpha[21]=v alpha[22]=w alpha[23]=x alpha[24]=y alpha[25]=z typeset -i found_homes=0 while (($s <= $e)) do for dir in $(ls -1d ${alpha[$s]}* 2>/dev/null) do if [[ -d $dir ]] then new_args="$new_args $dir" found_homes=1 fi done ((s = s + 1)) done \cd - > /dev/null if ((found_homes == 0)) then echo "$PN: no homes to process in range $start..$end" >> $log echo "$PN: no homes to process in range $start..$end" 1>&2 return 1 fi set -- $new_args echo "$PN: running $GTAR" "$@" >> $log $DEBUG $GTAR "$@" } ### # Utility functions. ### function IsFunction { f=$1 echo "$PN: looking for $f" >> $log if [[ -n "$BASH_VERSION" ]] then t=$(type -t "$f" 2> /dev/null) r=$? else t=$(whence -v "$f" 2> /dev/null) r=$? fi if [[ $r -eq 0 ]] then if [[ "$t" != *function* ]] then r=1 fi fi return $r } ### # Start of main code. ### ### # Set up a log file in /tmp/amanda. ### echo "$PN: start: $(date)" >> $log echo "$PN: args:" "$@" >> $log ### # Find the directory Amanda is asking us to back up. Also figure out # if we are doing an estimate or the real thing. ### typeset -i get_directory=0 typeset -i get_file=0 directory_arg= file_arg= for arg do if ((get_directory)) then directory_arg=$arg get_directory=0 elif ((get_file)) then file_arg=$arg get_file=0 elif [[ X"$arg" = X"--directory" ]] then get_directory=1 # --directory dir-to-back-up elif [[ X"$arg" = X"--file" ]] then get_file=1 # --file file-to-write-to fi done echo "$PN: directory: $directory_arg" >> $log echo "$PN: file: $file_arg" >> $log if [[ X"$file_arg" = X"-" ]] then type=real # real dump if output is stdout elif [[ X"$file_arg" = X"/dev/null" ]] then type=estimate # estimate if output is /dev/null else type=unknown # no idea what is going on fi ### # Make the directory name into something we can use as part of a function # name by converting the slashes to underscores. Hopefully there is # not anything else annoying in the name. ### d=$(echo $directory_arg | /bin/sed 's/[^a-zA-Z0-9_]/_/g') ### # See if there is something to be done before we call GNU tar. ### if IsFunction pre_${type}_$d then echo "$PN: running pre_${type}_$d" >> $log pre_${type}_$d else echo "$PN: pre_${type}_$d not found so nothing special run" >> $log fi ### # Run GNU tar or a private function. ### if IsFunction run_${type}_$d then echo "$PN: running run_${type}_$d" >> $log run_${type}_$d "$@" exit_code=$? else echo "$PN: running $GTAR" >> $log $DEBUG $GTAR "$@" exit_code=$? fi ### # See if there is something to be done after we ran GNU tar. ### if IsFunction post_${type}_$d then echo "$PN: running post_${type}_$d" >> $log post_${type}_$d else echo "$PN: post_${type}_$d not found so nothing special run" >> $log fi ### # Exit with the GNU tar exit code. ### echo "$PN: end: $(date)" >> $log exit $exit_code