` Linda Walsh wrote:
The latest error I got is a a simple type -- most of them probably are, with that many lines of code in ~3-4 weeks, there's bound to be -- trouble is I'm stubborn sometimes about 'punishing myself'' when I mess up...which isn't always productive! ;-)
Sometimes typo's used to work -- i.e. sometimes characters get randomly deleted out of my source files -- Win7 drops keys randomly, and I don't always notice the effects.... anyway, you wanted.... I wouldn't regard them as a finished product...
#!/bin/bash -exu # copyright(c) Linda Walsh 2011 ; # create_snap_rdiff Version: 0.1; initial writing # indended for eventual gnuish-open source license #set -xuhmeB export GLOBAL_SET_OPS="${-:-}" shopt -s expand_aliases extglob shift_verbose xpg_echo export PS4='>>${BASH_SOURCE:+${BASH_SOURCE[0]}}#${LINENO}${FUNCNAME:+(${FUNCNAME[0]})}> ' this="create_snap_rdiff" progpath="$0" prog="${progpath##*/}" progdir="${progpath%/*}" export progpid=$$ #base_mp="/home" shopt -s expand_aliases extglob alias sub=function alias unless='if !' typeset -xr sub unless declare -a SAVE_ARGS=( "$@" ) typeset -xr snapdir='snapdir' # in shell: white is black and black is white: export b_false=1 export b_true=0 export Debug=$b_false Verbose=$b_true Silent=$b_false export Quiet=$b_false Force="" loglevel=7 export base_mp="" . "$progdir/snaplib.sh" read progdir prog path export_blob <<<$(env_init "$progdir" "$prog" "$this") export PATH="$path" eval "$export_blob" #echo "progdir=$progdir, prog=$prog, path=$path" #echo "exportblob" export PATH="$path" eval "$export_blob" if [[ $#<1 ]]; then errx 50 "Need mount_path of fs to create snap_rdiff from" fi set "${SAVE_ARGS[@]}" declare -a args declare -a blob function process_args { # {{{ DebugPush ProcArgs args=( "${@}" ) local -i argi=0 # Arg Processing... while (($#>0)); do a="$1";shift; if [[ $a == "-v" || $a == "--verbose" ]]; then ((Verbose=b_true,loglevel<6?loglevel=6:0,0)) elif [[ $a == "-d" || $a == "--debug" ]]; then echo "Debug Set" ((Debug=b_true,loglevel<7?loglevel=7:0,0)) elif [[ $a == "-f" ]] ; then Force="$a";shift;continue; elif [[ $a =~ ^--force=.* ]]; then Force="${a#*=}" elif [[ $a == "--quiet" ]]; then ((Quiet=b_true,loglevel>2?loglevel=2:0,0)) elif [[ $a == "--silent" ]]; then ((Silent=b_true,loglevel=0)) elif [[ $a =~ --?.* ]]; then { echo "Unknown option \"$a\", use --help for help"; } >&1 exit -1 else args[$argi]="$a" ((++argi)) fi done if [[ ${#args[*]} -ge 1 ]]; then echo -e "${args[@]}" else echo fi if [[ -z "$base_mp" && ${#args[*]}<1 ]]; then errx 51 "Need mount_path of fs to create snap_rdiff from" fi DebugPop } args=( $(process_args "$@" ) ) set ${args[@]} ### allowed 'features' (to be set/queried, must be defined 1st) # (to prevent feature-misnaming/mispelling) # def_Provide base_mp base_vg base_lv base_mp_diff def_Provide clean_diff_dir def_Provide daily_snap def_Provide empty_snapvol def_Provide filled_snapvol def_Provide formatted_snapvol full_diff_dir def_Provide labeled_diff_dir def_Provide new_snapshot_wanted def_Provide osnap_mp osnap_vg osnap_lv osnap_devpath #def_Provide oldest_active_snapshot osnap_mp osnap_vg osnap_lv osnap_devpath def_Provide oldest_active_snapshot def_Provide root_dev def_Provide snap_vol snap_mp snap_mp_dir snap_copy_complete ## provides base_mp base_mp="${1:?}" add_provides "base_mp" #}}} #devel options export i_skip_mnt_clean_diff_dir=0 export i_skip_rsync=0 export i_skip_today_check=0 export i_skip_vol_create=0 export i_skip_new_snap=0 #needed init ## ## provides base_vg base_lv root_dev ## read base_vg base_lv <<<$(get_vg_lv_from_mp "$base_mp") root_dev="$(get_devnum / )" verb "root_dev=$root_dev, base_vg=$base_vg, base_lv=$base_lv" add_provides "base_vg" "base_lv" "root_dev" ## ## provides: new_snapshot_wanted ## requires: base_mp ## May-use: Force ## # won't return unless override is set or no backup today ((i_skip_today_check)) || { if have_dep "base_mp"; then chk_today_snap "$base_mp" "$Force" add_provides "new_snapshot_wanted" fi } base_mp_diff="$base_mp.diff" add_provides base_mp_diff # Attempt to read snap-label info set after a successful rsync read osnap_mp osnap_vg osnap_devpath <<<$(find_unsaved_snap_data "$base_mp_diff") if [[ ${osnap_mp:-} && ${osnap_vg:-} && ${osnap_devpath:-} ]]; then osnap_vg="$(getVG_by_MntDir "$osnap_devpath")" && { add_provides osnap_vg } add_provides labeled_diff_dir add_provides full_diff_dir add_provides osnap_mp osnap_lv osnap_devpath ## now check to see if this diff has been copied to corresponding snapdir! check_snap_copy_complete "$osnap_mp" fi if ! have_dep labeled_diff_dir || have_dep snap_copy_complete ; then # if doesn't exist we clean the 'diff' vol, and generate a new one # using 'rsync' # clean diff dir ## ## provides: clean_diff_dir ## depends: !unsaved_snap_data_check ## removes: full_diff_dir ## ((!i_skip_mnt_clean_diff_dir)) && { if ( ! have_dep "labeled_diff_dir" || have_dep snap_copy_complete ) ; then mnt_n_clean_diff_dir "$base_mp_diff" && { add_provides "clean_diff_dir" rm_provides "full_diff_dir" } fi } # find oldest active snapshot ## ## provides: oldest_active_snapshot ## provides: osnap_mp osnap_vg osnap_lv osnap_devpath ## unset osnap_mp osnap_vg osnap_lv read osnap_mp osnap_vg osnap_lv osnap_devpath \ <<<$( find_oactv_snapshot "$base_lv" ) if [[ ${osnap_mp:-} && ${osnap_vg:-} && ${osnap_lv:-} ]]; then add_provides "oldest_active_snapshot" add_provides "osnap_mp" "osnap_vg" "osnap_lv" "osnap_devpath" fi check_snap_copy_complete "${osnap_mp:-}" msg_level $_Info_ && { if have_dep snap_copy_complete; then info "Have snap_copy_complete" else info "snap_copy_complete is not set" fi } ## ## provides: daily_snap ## depends: base_mp ## if ((!i_skip_new_snap)); then if have_dep "base_mp" "new_snapshot_wanted"; then create_basemp_snap "$base_mp" && { add_provides "daily_snap" rm_provides "new_snapshot_wanted" } fi fi ## ## create diff vol ## ## provides: full_diff_dir ## provides: labeled_diff_dir ## Depends: osnap_mp osnap_vg osnap_vg ## if ((!i_skip_rsync)) ; then if have_dep -v osnap_mp base_mp base_mp_diff ; then # note, '**' must be quoted so bash's globstar won't expand it declare -a exclude_pats=( "--exclude='.recycle/**'" "--exclude='BLISS/law/**'" "--exclude='BLISS/athenae/**'" "--exclude='BLISS/law.V2/**'" "--exclude='BLISS/martin/**'" "--exclude='CPAN-ishtar-build-cache/**'" ) rsync_snapdiffs_to_base_mp_diff \ '\rsync_snapdiffs_to_base_callback' \ "$osnap_mp/." "$base_mp_diff" \ "$base_mp" "${exclude_pats[@]}" && { add_provides "full_diff_dir" } fi fi ## write label on diff-vol if rdiff finished ok ## ## write_osnap_identifier_file ## Provides: labeled diff dir" ## Depsends: base_mp_diff full_diff_dir osnap_mp osnap_lv osnap_devpath ## if have_dep -v base_mp_diff full_diff_dir osnap_mp osnap_lv osnap_devpath; then write_osnap_identifier_file \ "$base_mp_diff" "$osnap_mp" "$osnap_lv" "$osnap_devpath" && { add_provides "labeled_diff_dir" } fi ## if have labeled_diffdir, then delete snapshot it came from ## ## destroy_old_snapshot_by_mp_n_devpath ## Provides: ## Depends: labeled_diff_dir oldest_active_snapshot ## Deletes: if -v have_dep labeled_diff_dir osnap_mp osnap_devpath; then destroy_old_snapshot_by_mp_n_devpath "$osnap_mp" "$osnap_devpath" && { rm_provides "oldest_active_snapshot" } fi ### Getting here means we have a 'diff' vol that we need to move ### to a downwardly sized static vol that will contain the diff ### (a static snap) ## ## provides snap_vol ## provides osnap_devpath ## requires base_mp_diff base_vg osnap_lv osnap_vg ## if have_dep -v base_mp_diff base_vg osnap_lv osnap_vg ;then nextents="$(get_nextents_for_space_on_vg_w_minfree \ "$base_mp_diff" "$base_vg" 10 )" $_sudo $_lvcreate -l $nextents -r 256 -n "$osnap_lv" "$osnap_vg" && { add_provides snap_vol osnap_devpath="/dev/$osnap_vg/$osnap_lv" add_provides osnap_devpath } fi ## provides xfs_fs_for_oldest_snap_dir ## depends-on: oldest_snap_dir_lv if have_dep osnap_lv base_lv osnap_devpath; then osnap_xfs_label="${osnap_lv#"$base_lv-20"}" $_sudo make_xfs_snap_archive_vol \ "${osnap_xfs_label:0:11}" "$osnap_devpath" && { add_provides empty_snapvol add_provides formatted_snapvol } fi ## requires: base_mp base_lv osnap_lv ## provides snap_mp if have_dep base_mp base_lv osnap_lv; then obase_mp="/o${base_mp#/}" datedir="${osnap_lv#$base_lv-}" snap_mp="$base_mp/$snapdir/@GMT-$datedir" add_provides snap_mp if [[ -e $snap_mp ]]; then if [[ -d $snap_mp ]]; then if mp_dev_is_not_same_as_2nd_fs $snap_mp $base_mp/$snapdir; then $_umount $snap_mp || errx 54 "something mounted at $snap_mp and won't unmount" fi $_sudo $_rmdir $snap_mp || { errx 55 "won't mount on non-empty dir at $snap_mp" } else if [[ $Force =~ rm_osnapdir_contents ]]; then $rm -fr $snap_mp fi if [[ -e $snap_mp ]]; then errx 52 "$snap_mp is either not a dir, or can't be removed" fi fi fi fi ## provides: snap_mp_dir ## requires: snap_mp have_dep snap_mp && $_sudo mkdir $snap_mp && { add_provides snap_mp_dir } || errx 56 "Can't create mount point $snap_mp" #provides mounted_new_snap #depends: snap_mp_dir formatted_snapvol if have_dep snap_mp_dir formatted_snapvol osnap_devpath snap_mp; then $_sudo $_mount $osnap_devpath $snap_mp && { add_provides mounted_snapvol } fi if have_dep snap_mp; then check_snap_copy_complete "$snap_mp" fi ## provides: snap_copy_complete ## requires: mounted_new_snap if have_dep base_mp_diff snap_mp; then if ! have_dep snap_copy_complete; then $_sudo $_cp -a $base_mp_diff/. $snap_mp/. && { $_sudo $_touch "$snap_mp/snap_copy_complete" add_provides snap_copy_complete } fi fi function check_clean_diff_dir { local base_mp_diff="${1:?}" [[ -e $base_mp_diff/clean_diff_dir ]] && add_provides clean_diff_dir } if have_dep base_mp_diff ; then check_clean_diff_dir "$base_mp_diff" fi ## provides: clean_diff_dir ## requires: snap_copy_complete base_mp_diff if have_dep snap_copy_complete base_mp_diff;then mnt_n_clean_diff_dir "$base_mp_diff" && { $_sudo $_touch "$base_mp_diff/clean_diff_dir" add_provides clean_diff_dir } fi fi # vim: ts=2 sw=2 fdm=marker fen number
#!/bin/bash # # tracing active for now... # today_has_snap: 0.1 initial writing, # support routine for 'create_snap_rdiff' # copyright (c) 2011 Linda A. Walsh <la_walsh <at> tlinx <dot> org) # indended for eventual gnuish-open source license # return true if today has snap, # return non-zero in any failure (including existence of today-snap) this="today_has_snap" progpath="$0" prog="${progpath##*/}" progdir="${progpath%/*}" if [[ ${progdir:0:1} != '/' ]]; then preprog="$PWD" progdir="$preprog/$progdir" fi [[ $prog =~ bash ]] && prog="$this" . "$progdir/snaplib.sh" read progdir prog path export_blob <<<$(env_init "$progdir" "$prog" "$this") export PATH="$path" eval "$export_blob" if [[ $# < 1 ]]; then echo "$prog: need mount point" exit -2; fi Vol="$1" VG="$(getVG_by_MntDir $Vol)" LV="$(getLV_by_MntDir $Vol)" if [[ -z $VG || -z $LV ]]; then echo "$Vol not found (VG=$VG), (LV=$LV)" exit -3 fi SnapDir=snapdir Dev=/dev/$VG/$LV Prefix="snapdir/@GMT-" snap_today="$(date +%Y.%m.%d-)" snap_prefix="$Vol/$Prefix$snap_today*" today_snaps="$('ls' -1 ${snap_prefix} 2>/dev/null |tr "\n" " " )" if [[ -n $today_snaps && $today_snaps != $snap_prefix ]]; then echo "yes" exit 0 fi exit 1 # vim: ts=2 sw=2
#!/bin/bash # snaplib.sh: 0.1 initial writing, # support routiens for 'create_snap_rdiff' # copyright (c) 2011 Linda A. Walsh <la_walsh <at> tlinx <dot> org) # indended for eventual gnuish-open source license export SL_progdir"" SL_prog="" # why re-invent wheel; use syslog msg levels typeset -xra _Levels_=( Emergency Alert Critical Error Warn Note Info Verbose ) # # bash ext-patterns, <op>(content); # <op> := '?' | '*' | '+' | '@' (match one of '|' separted patterns) # and '!' (any but one of the '|' separated patterns) # typeset -xra _Level_pats=( 'Emerg?(ency)' 'Alert' 'Crit?(ical)' \ 'Err?(or)' 'Warn?(ing)?' 'Not@(e|ice)'\ ' Info' '@(Verbose|Debug?(ing))' ) typeset -ixr _Emerg_=0 _Alert_=1 _Crit_=2 _Error_=3 _Warn_=4 typeset -ixr _Note_=5 _Info_=6 _Verbose_=7 _Debug_=7 loglevel=$_Debug_ typeset -ix __DEVEL__=${__DEVEL__:-0} #Progs to get $_prog=path set in the parent ENV: # Sprogs are ones that probably need 'sudo' to run w/full feature set we need _Sprogs="bash cp dmsetup find lvcreate lvremove mount rmdir rsync" _Sprogs="$_Sprogs umount vgchange vgs xargs touch lvs" export _Progs="stat sudo perl cut grep rm $_Sprogs" shopt -s expand_aliases extglob alias sub=function alias unless='if !' # bitmask ops, need to be non-overlapping # note -i attr required for arith eval at assignement declare -ixr _D_=0 declare -ixr _D_Debug=0x01 declare -ixr _D_ProcArgs=0x02 declare -ixr _D_LowLevel=0x04 declare -ixr _D_Provides=0x08 declare -ixr _D_Define_Provides=0x10 declare -ixr _D_EnvInit=0x20 declare -ixr _D_PLists=0x40 # trace control for subs declare -ix Trace_off=$(( _D_Define_Provides | _D_LowLevel | _D_Provides | _D_Debug | _D_ProcArgs | _D_ )) declare -ix Trace_on=$(( _D_ )) # for setting a local '_Debug' flag in a routine declare -ix Local_Debug=$(( _D_ )) alias DebugPop='test -n "$save_ops" && set -$save_ops' alias DebugPop_preserve_status='local stat=$?;test -n "$save_ops" && { set -$save_ops; }; return $stat;' function DebugPush_helper { local dbgflgs="${-:-}";set +x; local flag="${1:?}" if chkflg Trace_off "$flag";then dbgflgs="${dbgflgs//x/}" ;fi if chkflg Trace_on "$flag" ;then dbgflgs="${dbgflgs}x" ;fi if chkflg Local_Debug "$flag" ; then _Debug=1; else _Debug=0; fi test -n "$dbgflgs" && set -$dbgflgs return 0; } alias DebugPush='local save_ops="${-:-}" _Debug=; DebugPush_helper' #function DebugPush { local save_ops="${-:-}"; debug $1 || set +x; } # called Global to check unprefixed 'flag' val in sub chkflg { local glob="${1:?}" op="${2:?}" chksave_ops="${-:-}"; if [[ $(((${!glob} & _D_Debug))) -eq 0 ]] ; then set +x fi test -z "${op:-}" && { test -n "$chksave_ops" && set -$chksave_ops return 1 } local dop="_D_$op" local -i vop="${!dop:-0}" local -i i_res (((i_res=vop&${!glob}),1)) test -n "$chksave_ops" && set -$chksave_ops if ((i_res)) ; then return 0; else $(exit -1;); return; fi } #function init_errnos { # {{{ # gcc -E -dM /usr/include/errno.h | # grep -P 'define\sE'| # sort -k3 -n|sed 's/^#define //; s/ /=/ ' | # ( coproc parselast { # cat | (usleep 100000;grep -P '^[A-Z]+=[A-Z]+$' ) | # sed 's/=/=$/' # } # tee /proc/self/fd/${parselast[1]}} | # egrep -P '^[A-Z]+=\d+$'; <$parselast[0] cat # ) # # }}} #} function env_init { # {{{ DebugPush EnvInit local progdir=$1 prog=$2 progname_dfl=$3 if [[ ${progdir:0:1} != '/' ]]; then local preprog="$PWD" progdir="$preprog/$progdir" fi progdir="${progdir%/.}" progdir="${progdir%/}" [[ $prog =~ bash ]] && prog="$progname_dfl" export PATH="$progdir:/usr/sbin:/sbin:/usr/bin:/bin:$PATH" SL_progdir="$progdir" SL_prog="$prog" declare -la export_blobs for p in $_Progs ;do pv="_$p" eval "typeset -xr $pv=\"$(type -p "$p")\"" [[ -z ${!pv} ]] && errx -1 "Cannot find \"$p\" in PATH" export_blobs[${#export_blobs[@]}]="export $pv=\"${!pv}\";" #" #here for a vim parsing error # local a=1 done # echo "$SL_progdir" "$SL_prog" "$PATH" "${export_blobs[*]}" >&2 echo "$SL_progdir" "$SL_prog" "$PATH" "${export_blobs[@]}" # }}} DebugPop } function _in { local x=${1:?};shift while [[ $# -gt 0 ]]; do if [[ $x == $1 ]]; then return $b_true; fi shift; done return $b_false; } # # three funcs in 1!!!: # 0 args: return cur loglevel on stdout # 1 arg: (will return true/false in status) # int: return true if (int < loglevel) # str: return true if (it matches a log_pat and it's lev < loglev) # else return false! # function msg_level { [[ $# == 0 ]] && { echo "$loglevel"; return 0; } local lev=${1:?} local -i ilev if i=$lev ; then ((lev<=loglevel)) && return $b_true else set -o nocasematch extglob #ensure set local -i i local pat for ((i=0; i<${#_Level_pats[*]}; ++i)); do pat="${_Level_pats[i]}" [[ $lev =~ $pat ]] && { if ((i<=loglevel)); then return $b_true; fi } done fi return $b_false } function msg { DebugPush LowLevel local -a internal_funcs=(warn verb info msg is_provided) if (( $#<1 )); then errx -2 "Internal Error to msg, Expected 1 or more params, got $#" fi local level="${1:?}" ((level>loglevel)) && return 0; shift; local passed_msg="$(printf "$@")" local funcs="${#FUNCNAME[*]}" if ((funcs>0)) ; then ((--funcs)); fi local caller=0 local src="${BASH_SOURCE[0]}" local bln="${BASH_LINENO[0]}" local func="${FUNCNAME[0]}" while ! _in $func "${internal_funcs[@]}" ;do ((++caller)) src="${BASH_SOURCE[$caller]}" bln="${BASH_LINENO[$caller]}" func="${FUNCNAME[$caller]}" done local ln="$LINENO" local eoln=$'\n' if [[ ${passed_msg:0-1:1} == $eoln ]] ; then eoln=""; fi printf "\n%s[%s] @ %s#%s(%s)@:\n\t%s%s" \ "$prog" "${_Levels_[$level]}" \ $src "$bln" "$func" \ "$passed_msg" "$eoln" >&2 DebugPop } function warn { msg _Warn_ "$@"; } function verb { msg _Verbose_ "$@"; } function info { msg _Info_ "$@"; } # a little used function function echov { printf "%s=%s\n" "$1" "${!1}"; } function errx { #{{{ if [[ $# -lt 2 ]]; then echo "$SL_prog: internal error, wrong# params to errx #=$# ($*)" fi err="$1" shift from_func=${FUNCNAME[1]} from_lineno=${BASH_LINENO[1]} from_source=${BASH_SOURCE[1]} passed_msg="$(printf "$@")" eoln=$'\n' if [[ ${passwd_msg:0-1:1} == $eoln ]] ; then eoln=""; fi printf "\n${SL_prog}: line %s in function %s: %s%s" \ "$from_lineno" "$from_func" "$passed_msg" "$eoln" >&2 kill -TERM -$progpid kill -9 -$progpid # one of these is bound to eventually work! ;-) #}}} } #### #### Mountpoint functions #### function get_devname_of_mp { # {{{ DebugPush MP_OPS if [[ $# != 1 ]]; then DebugPop errx -9 "Internal error: getLV_by_MntDir expects mount point as arg, got $# args" fi local mp="${1:?}" local dev="$($_grep -P '\s'"$mp"'\s' /proc/mounts|$_cut -d\ -f1)" echo "$dev" DebugPop MP_OPS # }}} } function get_vg_lv_from_mp { #{{{ DebugPush MP_OPS if [[ $# -lt 1 ]]; then errx -11 "Internal error: get_vg_lv_from_mp expects mount point as arg, got $# args" fi local mp="$1" local dev="$(get_devname_of_mp "$mp")" local lv vg local glob="$($_dmsetup --noheadings splitname "$dev")" glob="${glob%:}" vg="${glob%:*}" lv="${glob#*:}" vg="${vg#/dev}" vg="${vg#/mapper}" vg="${vg#/}" test -z "$vg" && { DebugPop errx 1 "dmsetup splitname returned no vg for mountpnt $mp" } test -z "$lv" && { DebugPop errx 2 "dmsetup splitname returned no lv for mountpnt $mp" } p="/dev/$vg"; test ! -d "$p" && { DebugPop errx 3 "Does not exist: vg $p (mountpnt $mp)" } p="$p/$lv"; test ! -e "$p" && { DebugPop errx 3 "Does not exist: vg/lv $p (mountpnt $mp)" } test -b "$p" || { DebugPop errx 4 "Not a device: vg/lv $p (mountpnt $mp)" } echo -e "$vg" "$lv" DebugPop #}}} } function getLV_by_MntDir { # {{{ if [[ $# != 1 ]]; then errx -7 "Internal error: getLV_by_MntDir expects mount point as arg, got $# args" fi read vg lv <<<$(get_vg_lv_from_mp "$1") echo "lv" # }}} } function getVG_by_MntDir { # {{{ if [[ $# != 1 ]]; then errx -8 "Internal error: getVG_by_MntDir expects mount point as arg, got $# args" fi read vg lv <<<$(get_vg_lv_from_mp "$1") echo "vg" # }}} } function mp_is_not_same_as_2nd_fs { # {{{ DebugPush MP_OPS local mp="$1" fs2="$2" local -i bad_param=2 bad_dev=3 [[ -z $mp || -z $fs2 ]] && { DebugPop; return $b_fail ;} [[ $mp == $fs2 ]] && { DebugPop; return $b_false; } local - i mp_dev="$(get_devnum $mp)" local -i fs2_dev="$(get_devnum $fs2)" ((mp_dev==0 || fs2_dev==0)) && { DebugPop; return $b_fail;} local -i res=$((mp_dev==fs2_dev)) DebugPop return $res #}}} } function mp_dev_is_not_on_root_fs { #{{{ mp="$1" mp_is_not_same_as_2nd_fs "$mp" "/" return $? #}}} } function mounted_fs_at { mp=${1?}; mp_parent="${mp%/*}" if [[ -z $mp_parent ]]; then mp_parent="/"; fi if mp_dev_is_not_same_as_2nd_fs $mp $mp_parent ; then return 0; #true fi return 1; } function get_most_recent_snapshot { # {{{ if [[ $# < 1 ]]; then errx -3 "Internal function 'get_most_recent_snapshot' called without mountpoint arg" fi local $snap_mp="$1" read snap_vg snap_lv <<<$(get_vg_lv_from_mp "$snap_mp" ) if [[ -z $snap_vg || -z $snap_lv ]]; then echo "$snap_mp not found (vg=$snap_vg), (lv=$snap_lv)" exit -3 fi $latest="$(find_active_snapshots|sort|last -1)" Prefix="snapdir/@GMT-" snap_today="$(date +%Y.%m.%d-)" snap_prefix="$snap_mp/$Prefix$snap_today*" today_snaps="$('ls' -1 ${snap_prefix} |tr "\n" " " )" if [[ -n $today_snaps && $today_snaps != $snap_prefix ]]; then echo "yes" exit 0 fi exit 1 # }}} } function chk_today_snap { # {{{ DebugPush LowLevel local base_mp="${1:?}" local Force="${2:-""}" today_has_snap "$base_mp" && { [[ $Force =~ .*snap_today.* ]] && { DebugPop return 0 } errx 10 "Error: already have a snap today; add 'snap_today' to force list (-f <comma-sep-list> ) to create anyway" } verb "No snap yet today" DebugPop return 0 # }}} } function get_devnum { #{{{ $_stat -c "%d" "$1" || errx -4 "stat on \"$1\" failed: \"$?\"" #}}} } function mnt_n_clean_diff_dir { #{{{ base_mp_diff=$1 mp_dev_is_not_on_root_fs "$base_mp_diff" || $_sudo $_umount "$base_mp_diff" || errx 5 "umount $base_mp_diff failed: $?" mp_dev="$(get_devnum "$base_mp_diff")" [[ $root_dev == $mp_dev ]] && { $_grep -s "$base_mp_diff" /etc/fstab || \ errx 6 "$base_mp_diff not found in /etc/fstab" $_sudo $_mount "$base_mp_diff" || errx $? "mount \"$base_mp_diff\" failed: $?" } mp_dev="$(get_devnum $base_mp_diff)" [[ $root_dev == $mp_dev ]] && \ errx 7 "Couldn't mount diff vol @ $base_mp_diff" cd "$base_mp_diff" || errx 11 "cd to $base_mp_diff, failed, stat=$?" curdir_dev="$(get_devnum . )" [[ $mp_dev != $curdir_dev ]] && \ errx 8 "cd to $base_mp_diff and dev of \".\" != base_dev ($mp_curdir != $curdir_dev)" [[ $curdir_dev == $root_dev || $PWD == / || $base_mp == / ]] && \ errx 9 \ "Sanity check failed, the diff dir looks like 'root'\n Cannot use root as diff dir." ## [[ $(/bin/pwd) != $base_mp_diff ]] && \ errx 13 "curdir $(/bin/pwd), != $base_mp_diff, not feel safe to proceed\n\ are softlinks involved; next step (rm -fr *) is dangerous is not in\n\ directory we expect to be in)." $_sudo $_rm --one-file-system -fr * verb "Line: $LINENO: mnt_n_clean_diff_dir: done" #}}} } function lv_names_attrs { $_sudo $_lvs --nosuffix --noheadings -o lv_name,lv_attr } function find_lv_by_filter { local perl_filt="${1:?}" lv_names_attrs | $_perl -ne "$perl_filt" } function find_attrs_by_lv { # {{{ local lv="${1:?}" if [[ $lv =~ / ]]; then errx -40 "IntErr, volnames can't have '/' in them, passed a mp?" fi local subpat="$lv"'\s*(.*)' local perl_prg=' m{^\s*'"$subpat"'} and print "$1\n"' find_lv_by_filter "$perl_prg" # }}} } function check_snap_attr { # {{{ local attr=${1:?} attr_name=${2:?} case "$attr_name" in [sS]tatic) test ${attr:0:1} == '-' && return 0 ;; [bB]ase|[oO]rigin) test ${attr:0:1} == 'o' && return 0 ;; [sS]napshot) test ${attr:0:1} == 's' && return 0 ;; [wW]ritable|[Ww]riteable) test ${attr:1,1} == 'w' && return 0 ;; [aA]ctive) test ${attr:4,1} == 'a' && return 0 ;; [sS]uspend) test ${attr:4,1} == 's' && return 0 ;; [iI]nvalid) test ${attr:4,1} == 'i' && return 0 ;; [oO]pen) test ${attr:5,1} == 'o' && return 0 ;; esac } function find_snap_by_lv_name_flags { # {{{ local base_lv=${1:?} flags=${2:?} local subpat="$base_lv"'-(20\d\d\.\d\d\.\d\d-\d\d\.\d\d\.\d\d)\s*'"$flags" local perl_prg=' m{^\s*'"$subpat"'} and print "$1\n"' find_lv_by_filter "$perl_prg" # }}} } function find_active_snapshots { # {{{ local base_lv=${1:?} find_snap_by_lv_name_flags "$base_lv" "swi-a." # }}} } function find_mounted_active_snapshots { # {{{ local base_lv=${1:?} find_snap_by_lv_name_flags "$base_lv" "swi-ao" # }}} } function find_unmounted_active_snapshots { # {{{ local base_lv=${1:?} find_snap_by_lv_name_flags "$base_lv" "swi-a-" # }}} } # snaps are fs-images based on a snapshot, that are now static copies function find_unmounted_snaps { # {{{ local base_lv=${1:?} find_snap_by_lv_name_flags "$base_lv" "-wi-a-" # }}} } function find_mounted_snaps { # {{{ local base_lv="${1:?}" find_snap_by_lv_name_flags "$base_lv" "-wi-ao" # }}} } function find_oactv_snapshot { # {{{ local base_lv="${1:?}" export perl_prg=' m{^\s*'"$base_lv"'-(20\d\d\.\d\d\.\d\d-\d\d\.\d\d\.\d\d)\s*swi-ao} and print "$1\n"' osnap_snapdate="$(find_active_snapshots "$base_lv"|sort -r|tail -1)" # above hs only the DT, so is nothing we neeed by itself # from it, we derive: osnap_mp_name="@GMT-$osnap_snapdate" osnap_lv="$base_lv-$osnap_snapdate" osnap_path_mp="$base_mp/$snapdir/$osnap_mp_name" read osnap_vg osnap_lv2 st <<<$(Mnt_to_VG+LV.pl "$osnap_path_mp";echo $?) ((st!=0)) && errx -5 "Mnt_to_VG_+LV for $osnap_path_mp failed with $?" osnap_devpath="/dev/$osnap_vg/$osnap_lv" echo "$osnap_path_mp" "$osnap_vg" "$osnap_lv" "$osnap_devpath" # }}} } export -f find_oactv_snapshot function clear_obase { # {{{ obase_mp=$1 if [[ ! -d $obase_mp ]]; then errx 19 "$obase_mp is not a directory" fi mp_dev_is_not_on_root_fs "$obase_mp" || { sudo umount "$obase_mp" || { errx 20 "Couldn't clear $obase_mp -- umount failed\n" } } return 0 # }}} } # no real reason to do this, (just made typing eaiser) # when it was done by hand...can diff directly from the old volume; # not sure of need to freeze....by maybe will anyway function bind_oldest_active_snap_to_obase { # {{{ mp_dev_is_not_on_root_fs $obase_mp || $_sudo $_umount $obase_mp || { errx 14 "could not clean mount point for old snapshot" } obase_mp="/o${base_mp#/}" #check snapdev!=rootdev, OR umount snapdev mp_dev_is_not_on_root_fs "$osnap_path_mp" || $_sudo $_umount "$osnap_path_mp" verb "Line: $LINENO: Before :mount --bind $osnap_path_mp " verb "Line: $LINENO: to $obase_mp: done" $_sudo $_mount --bind "$osnap_devpath" "$obase_mp" || \ errx 10 "%s",\ "rbind of old snap $osnap_path_mp "\ "to $obase_mp failed: status was $?" verb "Line: $LINENO: mount --bind $osnap_path_mp to $obase_mp: done" #NOTE: echo's output is read by caller, vs. 'verb[ose]' out goes to stderr echo "$osnap_path_mp" "$osnap_vg" "$osnap_lv" "$osnap_devpath" return 0 # }}} } function status_in_exit_params { # {{{ local status=$1; shift; local stat for stat in "$@"; do (($stat == $status)) && return 0 (exit -1);return done; # }}} } function create_basemp_snap { # {{{ local base_mp="${1:?}" $_sudo create_snap "$base_mp" || \ { errx 11 "create_snap returned bad exit code $?"; } # }}} } function trim_dirchs { # {{{ DebugPush LowLevel local str="$1" start="" while [[ $start != $str ]]; do start="$str" str="${str%/}" # make sure no trailing slash (foo/./././) str="${str%/.}" # no elim the normal case done # repeat in case passed nested string echo "$str" DebugPop # }}} } function rsync_snapdiffs_to_base_mp_diff { # {{{ local callback="${1:?}"; shift; local osnap_mp="${1:?}" base_mp_diff="${2:?}" base_mp="${3:?}" shift; shift; shift; local -a exclude_pats=( "$@" ) # clean up any suffixes...dunno who might call us...(Really!) osnap_mp="$(trim_dirchs "$osnap_mp")" local osnap_vg osnap_lv read osnap_vg osnap_lv <<<$(get_vg_lv_from_mp "$osnap_mp") base_mp_diff="$(trim_dirchs "$base_mp_diff")" base_mp="$(trim_dirchs "$base_mp")" local cmd # construct cmd so we can print what we are executing! printf -v cmd "%s " \ "$_sudo" "$_rsync" \ "-avHAX" "--one-file-system" \ "--compare-dest=\"$base_mp/.\"" \ "${exclude_pats[@]}" \ "$osnap_mp/." "$base_mp_diff/." "&>" "/tmp/rsync-log-$$.log" echo "$cmd" >&2 eval $cmd >&2 local status=$? local -a rsync_fuzzy_ok_exit_codes=(0 24) status_in_exit_params "$status" "${rsync_fuzzy_ok_exit_codes[@]}" || { local l1="rsync returned a 'shady' status ($status)," local l2=" so not proceeding log in /tmp/rsync-log-$$.log" errx 11 "$l1\n$l2" } # else remove log $_rm -f /tmp/rsync-log-$$.log # and remove empty dirs in diff dir ( $_sudo $_find $base_mp_diff/. -depth -type d -print0 | $_sudo $_xargs -0 $_rmdir >&2 2>/dev/null ) #ignore errs echo "$osnap_mp" "$osnap_vg" "$osnap_lv" return 0 # }}} } function destroy_old_snapshot_by_mp_n_devpath { # {{{ local osnap_mp="${1:?}" osnap_devpath="${2:?}" # now unmount old snap volume... $_sudo $_umount $osnap_mp || { note "umount failed, trying with force..." $_sudo $_umount $osnap_mp || { errx 30 "Couldn't unmount old snapshot dir" } } $_sudo $_dmsetup remove "$osnap_devpath" || { warn "dmsetup remove of $osnap_devpath failed" if [[ ! ($force =~ .*dmsetup_remove.* ) ]]; then errx 31 "Won't remove with force unless give 'dmsetup_remove' in force args" fi warn "using force to removee old snap\n" $_sudo $_dmsetup remove -f "$osnap_devpath" || { errx 32 "force remove w/dmsetup of $oshap_devpath failed!" } } $_sudo $_lvremove "$osnap_devpath" || { warn "lvremove of old snap failed" } # }}} } function osnap_identifier_filename { # "snap-<Vol>-<date-time>" local osnap_lv="${1:?}" # may be incomplete fn during searches echo "snap-$osnap_lv" } function write_osnap_identifier_file { local dir="${1:?}" local onap_mp="${2:?}" osnap_lv="${3:?}" osnap_devpath="${4:?}" local osnap_idfn="$(osnap_identifier_filename "$osnap_lv")" local echo_st="$(printf "echo -e \"%s\\\n%s\\\n%s\" >%s\n" \ "$osnap_mp" "$osnap_lv" "$osnap_devpath" "$osnap_idfn")" $_sudo bash -c "eval \"$echo_st\"" } function find_unsaved_snap_data { # {{{ local bdiff_mp="${1:?}" local bdiff_vg bdiff_lv read bdiff_vg bdiff_lv <<<$(get_vg_lv_from_mp "$bdiff_mp") local osnap_lv_base="$(osnap_identifier_filename "${bdiff_lv%.diff}" )" local osnap_lv_pat="$osnap_lv_base"'-+([-0-9\.])' datafile="$( echo $bdiff_mp/$osnap_lv_pat )" if [[ -f "$datafile" && -r "$datafile" ]]; then local -xa vals getval_cmds="readarray vals <$datafile; echo "'"${vals[@]}" ' read osnap_mp osnap_lv osnap_devpath <<<$(tr "\n" " " <"$datafile" ) if [[ -z $osnap_mp || -z $osnap_lv || -z $osnap_devpath ]]; then errx -6 "$( printf \ "Data file incomplete or corrupt; mp=%s, vg=%s, lv=%s, dev=%s\n" \ "$osnap_mp" "$osnap_lv" "$osnap_devpath" )" else echo "$osnap_mp" "$osnap_lv" "$osnap_devpath" return 0 fi else info "No unsaved snap data found on diff vol" echo "" return 1 fi # }}} } ## ## check_snap_copy_complete ## Checks & sets or removes if snap_copy_complete ## function check_snap_copy_complete { local snap_mp="${1:-}" # snap_mp of form parentVol/snapdir/@GMT-timestamp # need to find base parent vol for find_attrs_by_snap local vg lv read vg lv <<<$(get_vg_lv_from_mp "$snap_mp") attrs="$(find_attrs_by_lv "$lv")" if [[ -z $attrs || -z ${snap_mp:-} || ! -e $snap_mp/snap_copy_complete ]]; then rm_provides snap_copy_complete fi if check_snap_attr "$attrs" "snapshot"; then add_provides snap_copy_complete else rm_provides snap_copy_complete fi return 0 } function get_nextents_for_space_on_vg_w_minfree { # {{{ local base_mp_diff="${1:?}" base_vg="${2:?}" local -i minfree="${3:?}" local -i sizek="$(sudo du --one-file-system -k -s $base_mp_diff|cut -f1)" ((minsize=sizek*100/(100-minfree) )) ((sizek<minsize)) && # sanity check: should always be true sizek=$minsize #get allocation extent size of VG local extent_sz=$(sudo vgs --noheadings --units b \ --nosuffix -o vg_extent_size "$base_vg"); #determine how many full extents will hold the size we want set -x ((size=sizek*1024)) ((nextents=size/extent_sz)) ((m=size%extent_sz)) if ((m!=0));then ((nextents++)) || /bin/true fi echo "$nextents" # }}} } ## ## subs to handle 'defining' the names of allowed defines ## or test if a name is "allow" (defined); there is no Remove ## declare -axl defined_Provides # this holds names of 'vars' that contain white space separated names! # array names prefixed with '_plist_' declare -xr _plst_t='_plist_' declare -axl defined_PLists #test sub _defined_plist { DebugPush PLists if [[ ! "${defined_PLists:-}" || ${#defined_PLists[*]} -eq 0 ]]; then DebugPop return $b_false fi local -l def_plist="${1:?}" for ((i=0;i<${#defined_PLists[*]};++i)); do [[ $def_plist == ${defined_PLists[$i]} ]] && { DebugPop return $b_true } done DebugPop return $b_false } alias is_plist=_defined_plist sub add_PList_dep { DebugPush PLists local -l plist="${1:?}" local -l subdep="${2:?}" if ! _defined_plist "$plist"; then defined_PLists[${#defined_PLists[*]}]="$plist" fi plist="$_plst_t$plist" local -al plist_deps=( $plist ) local -i found=0 for ((i=0;i<${#plist_deps[*]};++i)); do if [[ $subdep == ${plist_deps[$i]} ]] ; then found=1 break; fi done if ! ((found)) ; then plist_deps[${#plist_deps[*]}]="$subdep" plist="$(echo "${plist_deps[@]}" )" fi DebugPop } # sub to handle testing 'definedness' sub _defined_provide { #{{{ DebugPush Define_Provides if [[ ! "${defined_Provides:-}"||${#defined_Provides[*]} -eq 0 ]]; then DebugPop return $b_false fi local -l def_provided="${1:?}" local -i i for ((i=0;i<${#defined_Provides[*]};++i)); do [[ $def_provided == ${defined_Provides[$i]} ]] && { DebugPop return $b_true } done DebugPop return $b_false } alias _is_defined=_defined_provide sub num_subdeps { DebugPush PLists local -l plist="${1:?}" if ! _defined_plist "$plist"; then defined_PLists[${defined_PLists[*]}]="$plist" fi plist="$_plst_t$plist" local -al plist_deps=( $plist ) echo "${#plist_deps[*]}" DebugPop } sub define_Provide { DebugPush Define_Provides echo "#defined_provides=${#defined_Provides[*]}" while [[ $# -gt 0 ]]; do local -l def_provide="${1:?}" shift local ch="$def_provide" ch="${ch:0:1}" if [[ $ch == @ ]]; then def_provide="${def_provide:1}" # rest of list are subdeps while [[ $# -gt 0 ]]; do local -l subdef_provide="${1:?}" shift define_Provide "?$subdef_provide" && { add_PList_dep "$def_provide" "$subdef_provide" } done if [[ $(num_subdeps "$def_provide") -lt 1 ]]; then errx -21 "IntErr def_Provide: list @$def_provide has no children!" fi break; elif [[ "$ch" == '?' ]]; then def_provide="${def_provide:1}" if ! _defined_provide "$def_provide"; then defined_Provides[${#defined_Provides[*]}]="$def_provide" fi else if ! _defined_provide "$def_provide"; then defined_Provides[${#defined_Provides[*]}]="$def_provide" else errx -20 \ "IntErr def_Provide: Attempt to redefine feature '$def_provide'" fi fi done DebugPop } alias def_Provide=define_Provide ## ## subs to handle if a feature or dep is actually defined or not; ## validity of names is checked in the above "define/_define provides" ## #need to handle list test&set declare -axl Provides=() sub is_provided { #{{{ #echo "#=$#, @=""$@" "" local -a args; args=( "$@" ) DebugPush Provides if [[ ! ${Provides:-} || ${#Provides[*]} -eq 0 ]]; then DebugPop return $b_false fi local provided="" local -i pVerbose=0 set -- "${args[@]}" while [[ $# -gt 0 ]]; do provided="${1:?}" if [[ $provided == -v ]]; then pVerbose=1 shift; continue; fi if [[ "${provided:0:1}" == "@" ]]; then provided="${provided:1}" local -al plist_deps=( $plist ) local -i i for ((i=0;i<${#plist_deps[*]};++i)); do if ((pVerbose)); then is_provided -v "${plist_deps[$i]}" else is_provided "${plist_deps[$i]}" fi done continue fi _is_defined $provided || { errx -21 "IntErr is_provided: feature '$provided' is not defined" } shift; for ((i=0;i<${#Provides[*]};++i)); do [[ $provided == ${Provides[$i]} ]] && continue 2; done if ((pVerbose)); then warn "Missing feature/dependency \"$provided\"" fi DebugPop return $b_false done; DebugPop return 0 } alias have_dep="is_provided" sub rm_provides { DebugPush Provides local -al nProvides local tbd_provide="${1:?}" _is_defined $tbd_provide || { errx -21 "IntErr rm_provides: feature '$tbd_provide' is not defined" } [[ -z "${Provides:-}" ]] && return if [[ "${tbd_provide:0:1}" == "@" ]]; then tbd_provide="${tbd_provide:1}" local -al plist_deps=( $plist ) local -i i for ((i=0;i<${#plist_deps[*]};++i)); do rm_provides "${plist_deps[$i]}" done else local -i n=0 p=0 local b_modified=$b_false while ((p<${#Provides[*]})); do if [[ $tbd_provide != ${Provides[$p]} ]]; then nProvides[$n]="${Provides[$p]}" ((++n, ++p)) else ((++p)) b_modified=$b_true fi done if [[ $b_modified ]]; then Provides=() for ((n=0;n<${#nProvides[*]};++n)); do Provides[$n]=${nProvides[$n]} done fi fi DebugPop } sub add_provides { DebugPush Provides local -l provided while [[ $# -gt 0 ]]; do provided="${1:?}" shift _is_defined $provided || { errx -21 "IntErr add_provides: feature '$provided' is not defined" } if [[ "${provided:0:1}" == "@" ]]; then provided="${provided:1}" local -al plist_deps=( $plist ) for ((i=0;i<${#plist_deps[*]};++i)); do add_provides "${plist_deps[$i]}" done else if ! is_provided "$provided"; then if [[ -z "${Provides:-}" ]]; then Provides[0]="$provided" else Provides[${#Provides[*]}]="$provided" fi fi fi done DebugPop } # vim: ts=2 sw=2 fdm=marker nofen number