17 Commits

Author SHA1 Message Date
cc9f1802ed Fix pool exclusions.
Use $ZPOOL_STATUS instead of $ZFS_STATUS for pool exclusions.

A mistaken variable name made the pool exclusion logic a no-op and
broke the --skip-scrub switch.

Also correct a trivial comment typo.
2012-01-21 16:32:30 -06:00
d77af5a902 Update the README file to be more descriptive. 2012-01-10 08:10:05 -06:00
b5bf1149ca Merge pull request #2 from ulope/master
Fixed --prefix and --sep regexes
2011-12-28 10:06:04 -08:00
5fc395c2bb Fixed --prefix and --sep regexes
Fixes: #1
2011-12-28 17:06:34 +01:00
d99147db7a Set keep=8 in the weekly cron job.
A default of keep=8 for weekly snapshot is more useful than keep=4,
which is no more protective than the default daily schedule.
2011-12-21 20:29:27 -06:00
c8507a0da9 Change objects to datasets in help and comments.
In the usage synopsis and program comments, change "objects" to
"datasets" for consistency with upstream ZFS terminology.
2011-12-18 07:34:40 -06:00
74359e51a7 Fix the hourly-daily transposition in the Makefile.
The hourly cron job was installed to cron.daily, and the daily cron
job was installed to cron.hourly, which caused incorrect scheduling.
2011-11-28 00:07:30 -06:00
afdae86271 Remove redundant lines in the cron.d file. 2011-11-28 00:05:43 -06:00
4b7609791b Split the cron file for anacron compatibilty.
Split the cron file so that the hourly, daily, weekly, and monthly
zfs automatic snapshots still happen even if the system is offline
temporarily during the scheduled event, such as during a reboot.

On Debian systems, if anacron is installed, then it is used to run
the /etc/cron.{hourly,daily,weekly,monthly} directories, but not
the /etc/cron.d directory. This means that /etc/cron.d jobs are not
run if the system is offline when crond would usually invoke them.
2011-11-25 14:26:08 -06:00
5ce7a23384 Implement the --event option for :auto-snap-desc.
Set the com.sun:auto-snap-desc property on each snapshot to an
arbitrary value.  On Solaris, this property is set to the dash
character by default -- which is convention for NULL -- but it
can contain an FMRI status comment or other user data.
2011-11-23 10:44:32 -06:00
2e26499f6a Rebase exit codes to above 127.
Most standard shell utilities return errors less than 128. Rebase the
exit codes of this script to reduce the liklihood of overlap.
2011-11-23 10:03:56 -06:00
f7ceb28963 Implement --prefix parameter checking.
Also add -p to the getopt list of short options.
2011-11-23 10:00:24 -06:00
4c14da4130 Consistently quote literal strings.
Apply some syntax hygiene. Quoting everything is a safe habit because
unquoted things can have subtle side-effects.
2011-11-22 23:36:51 -06:00
249e6a4cb3 Fold the main loops into a do_snapshots function.
The two primary `for` loops were identical except for the '-r' flag.
2011-11-22 23:27:24 -06:00
a0a43a42af Invert the --skip-scrub test.
The $opt_skip_scrub option was tested incorrectly, which caused
the --skip-scrub option to operate opposite intent.
2011-11-22 22:09:23 -06:00
d8b1730015 Add an explicit exit 0 to the end of the script.
The implicit return is "$?", which can be set non-zero by
an earlier command.
2011-11-21 21:44:37 -06:00
adebbd6fee Add -q to the getopt list of short options.
This corresponds to the --quiet option.
2011-11-21 21:40:04 -06:00
9 changed files with 178 additions and 147 deletions

View File

@ -2,6 +2,14 @@ all:
install: install:
install -d $(DESTDIR)$(PREFIX)/etc/cron.d install -d $(DESTDIR)$(PREFIX)/etc/cron.d
install etc/zfs-auto-snapshot.cron $(DESTDIR)$(PREFIX)/etc/cron.d/zfs-auto-snapshot install -d $(DESTDIR)$(PREFIX)/etc/cron.daily
install -d $(DESTDIR)$(PREFIX)/etc/cron.hourly
install -d $(DESTDIR)$(PREFIX)/etc/cron.weekly
install -d $(DESTDIR)$(PREFIX)/etc/cron.monthly
install etc/zfs-auto-snapshot.cron.frequent $(DESTDIR)$(PREFIX)/etc/cron.d/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.hourly $(DESTDIR)$(PREFIX)/etc/cron.hourly/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.daily $(DESTDIR)$(PREFIX)/etc/cron.daily/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.weekly $(DESTDIR)$(PREFIX)/etc/cron.weekly/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.monthly $(DESTDIR)$(PREFIX)/etc/cron.monthly/zfs-auto-snapshot
install -d $(DESTDIR)$(PREFIX)/sbin install -d $(DESTDIR)$(PREFIX)/sbin
install src/zfs-auto-snapshot.sh $(DESTDIR)$(PREFIX)/sbin/zfs-auto-snapshot install src/zfs-auto-snapshot.sh $(DESTDIR)$(PREFIX)/sbin/zfs-auto-snapshot

10
README
View File

@ -1,4 +1,12 @@
An alternative implementation of the zfs-auto-snapshot service for Linux. zfs-auto-snapshot:
An alternative implementation of the zfs-auto-snapshot service for Linux
that is compatible with zfs-linux and zfs-fuse.
Automatically create, rotate, and destroy periodic ZFS snapshots. This is
the utility that creates the @zfs-auto-snap_frequent, @zfs-auto-snap_hourly,
@zfs-auto-snap_daily, @zfs-auto-snap_weekly, and @zfs-auto-snap_monthly
snapshots if it is installed.
This program is a posixly correct bourne shell script. It depends only on This program is a posixly correct bourne shell script. It depends only on
the zfs utilities and cron, and can run in the dash shell. the zfs utilities and cron, and can run in the dash shell.

View File

@ -1,7 +0,0 @@
PATH="/usr/bin:/bin:/usr/sbin:/sbin"
*/15 * * * * root zfs-auto-snapshot -q -g --label=frequent --keep=4 //
@hourly root zfs-auto-snapshot -q -g --label=hourly --keep=24 //
@daily root zfs-auto-snapshot -q -g --label=daily --keep=31 //
@weekly root zfs-auto-snapshot -q -g --label=weekly --keep=4 //
@monthly root zfs-auto-snapshot -q -g --label=monthly --keep=12 //

View File

@ -0,0 +1,2 @@
#!/bin/sh
exec zfs-auto-snapshot --quiet --syslog --label=daily --keep=31 //

View File

@ -0,0 +1,3 @@
PATH="/usr/bin:/bin:/usr/sbin:/sbin"
*/15 * * * * root zfs-auto-snapshot -q -g --label=frequent --keep=4 //

View File

@ -0,0 +1,2 @@
#!/bin/sh
exec zfs-auto-snapshot --quiet --syslog --label=hourly --keep=24 //

View File

@ -0,0 +1,2 @@
#!/bin/sh
exec zfs-auto-snapshot --quiet --syslog --label=monthly --keep=12 //

View File

@ -0,0 +1,2 @@
#!/bin/sh
exec zfs-auto-snapshot --quiet --syslog --label=weekly --keep=8 //

View File

@ -28,6 +28,7 @@ opt_backup_full=''
opt_backup_incremental='' opt_backup_incremental=''
opt_default_exclude='' opt_default_exclude=''
opt_dry_run='' opt_dry_run=''
opt_event='-'
opt_keep='' opt_keep=''
opt_label='' opt_label=''
opt_prefix='zfs-auto-snap' opt_prefix='zfs-auto-snap'
@ -38,12 +39,21 @@ opt_syslog=''
opt_skip_scrub='' opt_skip_scrub=''
opt_verbose='' opt_verbose=''
# Global summary statistics.
DESTRUCTION_COUNT='0'
SNAPSHOT_COUNT='0'
WARNING_COUNT='0'
# Other global variables.
SNAPSHOTS_OLD=''
print_usage () print_usage ()
{ {
echo "Usage: $0 [options] [-l label] <'//' | name [name...]> echo "Usage: $0 [options] [-l label] <'//' | name [name...]>
--default-exclude Exclude objects if com.sun:auto-snapshot is unset. --default-exclude Exclude datasets if com.sun:auto-snapshot is unset.
-d, --debug Print debugging messages. -d, --debug Print debugging messages.
-e, --event=EVENT Set the com.sun:auto-snapshot-desc property to EVENT.
-n, --dry-run Print actions without actually doing anything. -n, --dry-run Print actions without actually doing anything.
-s, --skip-scrub Do not snapshot filesystems in scrubbing pools. -s, --skip-scrub Do not snapshot filesystems in scrubbing pools.
-h, --help Print this usage message. -h, --help Print this usage message.
@ -57,7 +67,7 @@ print_usage ()
-g, --syslog Write messages into the system log. -g, --syslog Write messages into the system log.
-r, --recursive Snapshot named filesystem and all descendants. -r, --recursive Snapshot named filesystem and all descendants.
-v, --verbose Print info messages. -v, --verbose Print info messages.
name Filesystem and volume names, or '//' for all ZFS objects. name Filesystem and volume names, or '//' for all ZFS datasets.
" "
} }
@ -108,7 +118,7 @@ print_log () # level, message, ...
} }
do_run () do_run () # [argv]
{ {
if [ -n "$opt_dry_run" ] if [ -n "$opt_dry_run" ]
then then
@ -117,7 +127,7 @@ do_run ()
else else
eval $* eval $*
RC="$?" RC="$?"
if [ "$RC" -eq 0 ] if [ "$RC" -eq '0' ]
then then
print_log debug "$*" print_log debug "$*"
else else
@ -127,40 +137,101 @@ do_run ()
return "$RC" return "$RC"
} }
do_snapshots () # properties, flags, snapname, oldglob, [targets...]
{
local PROPS="$1"
local FLAGS="$2"
local NAME="$3"
local GLOB="$4"
local TARGETS="$5"
local KEEP=''
# global DESTRUCTION_COUNT
# global SNAPSHOT_COUNT
# global WARNING_COUNT
# global SNAPSHOTS_OLD
for ii in $TARGETS
do
if do_run "zfs snapshot $PROPS $FLAGS '$ii@$NAME'"
then
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT + 1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
continue
fi
# Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created.
test -z "$opt_keep" && continue
KEEP="$opt_keep"
# ASSERT: The old snapshot list is sorted by increasing age.
for jj in $SNAPSHOTS_OLD
do
# Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$ii@$GLOB}" ]
then
KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le '0' ]
then
if do_run "zfs destroy $FLAGS '$jj'"
then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT + 1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
fi
fi
fi
done
done
}
# main () # main ()
# { # {
DATE=$(date +%F-%H%M)
GETOPT=$(getopt \ GETOPT=$(getopt \
--longoptions=default-exclude,dry-run,skip-scrub,recursive \ --longoptions=default-exclude,dry-run,skip-scrub,recursive \
--longoptions=keep:,label:,prefix:,sep: \ --longoptions=event:,keep:,label:,prefix:,sep: \
--longoptions=debug,help,quiet,syslog,verbose \ --longoptions=debug,help,quiet,syslog,verbose \
--options=dnshl:k:rs:gv \ --options=dnshe:l:k:p:rs:qgv \
-- "$@" ) \ -- "$@" ) \
|| exit 1 || exit 128
eval set -- "$GETOPT" eval set -- "$GETOPT"
while [ "$#" -gt 0 ] while [ "$#" -gt '0' ]
do do
case "$1" in case "$1" in
(-d|--debug) (-d|--debug)
opt_debug=1 opt_debug='1'
opt_quiet='' opt_quiet=''
opt_verbose=1 opt_verbose='1'
shift 1 shift 1
;; ;;
(--default-exclude) (--default-exclude)
opt_default_exclude='1' opt_default_exclude='1'
shift 1 shift 1
;; ;;
(-e|--event)
if [ "${#2}" -gt '1024' ]
then
print_log error "The $1 parameter must be less than 1025 characters."
exit 139
elif [ "${#2}" -gt '0' ]
then
opt_event="$2"
fi
shift 2
;;
(-n|--dry-run) (-n|--dry-run)
opt_dry_run='1' opt_dry_run='1'
shift 1 shift 1
;; ;;
(-s|--skip-scrub) (-s|--skip-scrub)
opt_skip_scrub=1 opt_skip_scrub='1'
shift 1 shift 1
;; ;;
(-h|--help) (-h|--help)
@ -168,10 +239,10 @@ do
exit 0 exit 0
;; ;;
(-k|--keep) (-k|--keep)
if ! test "$2" -gt 0 2>/dev/null if ! test "$2" -gt '0' 2>/dev/null
then then
print_log error "The $1 parameter must be a positive integer." print_log error "The $1 parameter must be a positive integer."
exit 2 exit 129
fi fi
opt_keep="$2" opt_keep="$2"
shift 2 shift 2
@ -181,8 +252,19 @@ do
shift 2 shift 2
;; ;;
(-p|--prefix) (-p|--prefix)
# @TODO: Parameter validation. See --sep below for the regex.
opt_prefix="$2" opt_prefix="$2"
while test "${#opt_prefix}" -gt '0'
do
case $opt_prefix in
([![:alnum:]_.:\ -]*)
print_log error "The $1 parameter must be alphanumeric."
exit 130
;;
esac
opt_prefix="${opt_prefix#?}"
done
opt_prefix="$2"
shift 2
;; ;;
(-q|--quiet) (-q|--quiet)
opt_debug='' opt_debug=''
@ -191,33 +273,33 @@ do
shift 1 shift 1
;; ;;
(-r|--recursive) (-r|--recursive)
opt_recursive=1 opt_recursive='1'
shift 1 shift 1
;; ;;
(--sep) (--sep)
case "$2" in case "$2" in
([[:alnum:]_-.:\ ]) ([[:alnum:]_.:\ -])
: :
;; ;;
('') ('')
print_log error "The $1 parameter must be non-empty." print_log error "The $1 parameter must be non-empty."
exit 3 exit 131
;; ;;
(*) (*)
print_log error "The $1 parameter must be one alphanumeric character." print_log error "The $1 parameter must be one alphanumeric character."
exit 4 exit 132
;; ;;
esac esac
opt_sep="$2" opt_sep="$2"
shift 2 shift 2
;; ;;
(-g|--syslog) (-g|--syslog)
opt_syslog=1 opt_syslog='1'
shift 1 shift 1
;; ;;
(-v|--verbose) (-v|--verbose)
opt_quiet='' opt_quiet=''
opt_verbose=1 opt_verbose='1'
shift 1 shift 1
;; ;;
(--) (--)
@ -227,10 +309,10 @@ do
esac esac
done done
if [ "$#" -eq 0 ] if [ "$#" -eq '0' ]
then then
print_log error "The filesystem argument list is empty." print_log error "The filesystem argument list is empty."
exit 5 exit 133
fi fi
# Count the number of times '//' appears on the command line. # Count the number of times '//' appears on the command line.
@ -240,10 +322,10 @@ do
test "$ii" = '//' && SLASHIES=$(( $SLASHIES + 1 )) test "$ii" = '//' && SLASHIES=$(( $SLASHIES + 1 ))
done done
if [ "$#" -gt 1 -a "$SLASHIES" -gt 0 ] if [ "$#" -gt '1' -a "$SLASHIES" -gt '0' ]
then then
print_log error "The // must be the only argument if it is given." print_log error "The // must be the only argument if it is given."
exit 6 exit 134
fi fi
# These are the only times that `zpool status` or `zfs list` are invoked, so # These are the only times that `zpool status` or `zfs list` are invoked, so
@ -251,14 +333,14 @@ fi
# Solaris implementation. # Solaris implementation.
ZPOOL_STATUS=$(env LC_ALL=C zpool status 2>&1 ) \ ZPOOL_STATUS=$(env LC_ALL=C zpool status 2>&1 ) \
|| { print_log error "zpool status $?: $ZPOOL_STATUS"; exit 7; } || { print_log error "zpool status $?: $ZPOOL_STATUS"; exit 135; }
ZFS_LIST=$(env LC_ALL=C zfs list -H -t filesystem,volume -s name \ ZFS_LIST=$(env LC_ALL=C zfs list -H -t filesystem,volume -s name \
-o name,com.sun:auto-snapshot,com.sun:auto-snapshot:"$opt_label") \ -o name,com.sun:auto-snapshot,com.sun:auto-snapshot:"$opt_label") \
|| { print_log error "zfs list $?: $ZFS_LIST"; exit 8; } || { print_log error "zfs list $?: $ZFS_LIST"; exit 136; }
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -S creation -o name) \ SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -S creation -o name) \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 9; } || { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
# Verify that each argument is a filesystem or volume. # Verify that each argument is a filesystem or volume.
@ -272,30 +354,30 @@ do
$ZFS_LIST $ZFS_LIST
HERE HERE
print_log error "$ii is not a ZFS filesystem or volume." print_log error "$ii is not a ZFS filesystem or volume."
exit 10 exit 138
done done
# Get a list of pools that are being scrubbed. # Get a list of pools that are being scrubbed.
ZPOOLS_SCRUBBING=$(echo "$ZFS_STATUS" | awk -F ': ' \ ZPOOLS_SCRUBBING=$(echo "$ZPOOL_STATUS" | awk -F ': ' \
'$1 ~ /^ *pool$/ { pool = $2 } ; \ '$1 ~ /^ *pool$/ { pool = $2 } ; \
$1 ~ /^ *scan$/ && $2 ~ /scrub in progress/ { print pool }' \ $1 ~ /^ *scan$/ && $2 ~ /scrub in progress/ { print pool }' \
| sort ) | sort )
# Get a list of pools that cannot do a snapshot. # Get a list of pools that cannot do a snapshot.
ZPOOLS_NOTREADY=$(echo "$ZFS_STATUS" | awk -F ': ' \ ZPOOLS_NOTREADY=$(echo "$ZPOOL_STATUS" | awk -F ': ' \
'$1 ~ /^ *pool$/ { pool = $2 } ; \ '$1 ~ /^ *pool$/ { pool = $2 } ; \
$1 ~ /^ *state$/ && $2 !~ /ONLINE|DEGRADED/ { print pool } ' \ $1 ~ /^ *state$/ && $2 !~ /ONLINE|DEGRADED/ { print pool } ' \
| sort) | sort)
# Get a list of objects for which snapshots are explicitly disabled. # Get a list of datasets for which snapshots are explicitly disabled.
NOAUTO=$(echo "$ZFS_LIST" | awk -F '\t' \ NOAUTO=$(echo "$ZFS_LIST" | awk -F '\t' \
'tolower($2) ~ /false/ || tolower($3) ~ /false/ {print $1}') 'tolower($2) ~ /false/ || tolower($3) ~ /false/ {print $1}')
# If the --default-exclude flag is set, then exclude all objects that lack # If the --default-exclude flag is set, then exclude all datasets that lack
# an explicit com.sun:auto-snapshot* property. Otherwise, include them. # an explicit com.sun:auto-snapshot* property. Otherwise, include them.
if [ -n "$opt_default_exclude" ] if [ -n "$opt_default_exclude" ]
then then
# Get a list of objects for which snapshots are explicitly enabled. # Get a list of datasets for which snapshots are explicitly enabled.
CANDIDATES=$(echo "$ZFS_LIST" | awk -F '\t' \ CANDIDATES=$(echo "$ZFS_LIST" | awk -F '\t' \
'tolower($2) ~ /true/ || tolower($3) ~ /true/ {print $1}') 'tolower($2) ~ /true/ || tolower($3) ~ /true/ {print $1}')
else else
@ -304,20 +386,20 @@ else
'tolower($2) !~ /false/ && tolower($3) !~ /false/ {print $1}') 'tolower($2) !~ /false/ && tolower($3) !~ /false/ {print $1}')
fi fi
# Initialize the list of objects that will get a recursive snapshot. # Initialize the list of datasets that will get a recursive snapshot.
TARGETS_RECURSIVE='' TARGETS_RECURSIVE=''
# Initialize the list of objects that will get a non-recursive snapshot. # Initialize the list of datasets that will get a non-recursive snapshot.
TARGETS_REGULAR='' TARGETS_REGULAR=''
for ii in $CANDIDATES for ii in $CANDIDATES
do do
# Qualify object names so variable globbing works properly. # Qualify dataset names so variable globbing works properly.
# Suppose ii=tanker/foo and jj=tank sometime during the loop. # Suppose ii=tanker/foo and jj=tank sometime during the loop.
# Just testing "$ii" != ${ii#$jj} would incorrectly match. # Just testing "$ii" != ${ii#$jj} would incorrectly match.
iii="$ii/" iii="$ii/"
# Exclude objects that are not named on the command line. # Exclude datasets that are not named on the command line.
IN_ARGS='0' IN_ARGS='0'
for jj in "$@" for jj in "$@"
do do
@ -331,13 +413,13 @@ do
continue continue
fi fi
# Exclude objects in pools that cannot do a snapshot. # Exclude datasets in pools that cannot do a snapshot.
for jj in $ZPOOLS_NOTREADY for jj in $ZPOOLS_NOTREADY
do do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# Check whether the pool name is a prefix of the object name. # Check whether the pool name is a prefix of the dataset name.
if [ "$iii" != "${iii#$jjj}" ] if [ "$iii" != "${iii#$jjj}" ]
then then
print_log info "Excluding $ii because pool $jj is not ready." print_log info "Excluding $ii because pool $jj is not ready."
@ -345,13 +427,13 @@ do
fi fi
done done
# Exclude objects in scrubbing pools if the --skip-scrub flag is set. # Exclude datasets in scrubbing pools if the --skip-scrub flag is set.
test -z "$opt_skip_scrub" && for jj in $ZPOOLS_SCRUBBING test -n "$opt_skip_scrub" && for jj in $ZPOOLS_SCRUBBING
do do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# Check whether the pool name is a prefix of the object name. # Check whether the pool name is a prefix of the dataset name.
if [ "$iii" != "${iii#$jjj}" ] if [ "$iii" != "${iii#$jjj}" ]
then then
print_log info "Excluding $ii because pool $jj is scrubbing." print_log info "Excluding $ii because pool $jj is scrubbing."
@ -364,17 +446,17 @@ do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# The --recusive switch only matters for non-wild arguments. # The --recursive switch only matters for non-wild arguments.
if [ -z "$opt_recursive" -a "$1" != '//' ] if [ -z "$opt_recursive" -a "$1" != '//' ]
then then
# Snapshot this object non-recursively. # Snapshot this dataset non-recursively.
print_log debug "Including $ii for regular snapshot." print_log debug "Including $ii for regular snapshot."
TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t
continue 2 continue 2
# Check whether the candidate name is a prefix of any excluded object name. # Check whether the candidate name is a prefix of any excluded dataset name.
elif [ "$jjj" != "${jjj#$iii}" ] elif [ "$jjj" != "${jjj#$iii}" ]
then then
# Snapshot this object non-recursively. # Snapshot this dataset non-recursively.
print_log debug "Including $ii for regular snapshot." print_log debug "Including $ii for regular snapshot."
TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t
continue 2 continue 2
@ -386,7 +468,7 @@ do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# Check whether any included object is a prefix of the candidate name. # Check whether any included dataset is a prefix of the candidate name.
if [ "$iii" != "${iii#$jjj}" ] if [ "$iii" != "${iii#$jjj}" ]
then then
print_log debug "Excluding $ii because $jj includes it recursively." print_log debug "Excluding $ii because $jj includes it recursively."
@ -405,20 +487,19 @@ do
TARGETS_RECURSIVE="${TARGETS_RECURSIVE:+$TARGETS_RECURSIVE }$ii" # nb: \t TARGETS_RECURSIVE="${TARGETS_RECURSIVE:+$TARGETS_RECURSIVE }$ii" # nb: \t
done done
# Summary statistics. # Linux lacks SMF and the notion of an FMRI event, but always set this property
DESTRUCTION_COUNT='0' # because the SUNW program does. The dash character is the default.
SNAPSHOT_COUNT='0' SNAPPROP="-o com.sun:auto-snapshot-desc='$opt_event'"
WARNING_COUNT='0'
# Linux lacks SMF and the notion of an FMRI event. # ISO style date; fifteen characters: YYYY-MM-DD-HHMM
FMRI_EVENT='-' # On Solaris %H%M expands to 12h34.
DATE=$(date +%F-%H%M)
# Create the snapshot using these arguments. # The snapshot name after the @ symbol.
SNAPSHOT_PROPERTIES="-o com.sun:auto-snapshot-desc='$FMRI_EVENT'" SNAPNAME="$opt_prefix${opt_label:+$opt_sep$opt_label-$DATE}"
SNAPSHOT_NAME="$opt_prefix${opt_label:+$opt_sep$opt_label-$DATE}"
# The expression for old snapshots. -YYYY-MM-DD-HHMM # The expression for matching old snapshots. -YYYY-MM-DD-HHMM
SNAPSHOT_MATCH="$opt_prefix${opt_label:+?$opt_label}????????????????" SNAPGLOB="$opt_prefix${opt_label:+?$opt_label}????????????????"
test -n "$TARGETS_REGULAR" \ test -n "$TARGETS_REGULAR" \
&& print_log info "Doing regular snapshots of $TARGETS_REGULAR" && print_log info "Doing regular snapshots of $TARGETS_REGULAR"
@ -429,83 +510,13 @@ test -n "$TARGETS_RECURSIVE" \
test -n "$opt_dry_run" \ test -n "$opt_dry_run" \
&& print_log info "Doing a dry run. Not running these commands..." && print_log info "Doing a dry run. Not running these commands..."
do_snapshots "$SNAPPROP" "" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_REGULAR"
do_snapshots "$SNAPPROP" "-r" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_RECURSIVE"
for ii in $TARGETS_REGULAR print_log notice "@$SNAPNAME," \
do "$SNAPSHOT_COUNT created," \
if do_run "zfs snapshot $SNAPSHOT_PROPERTIES '$ii@$SNAPSHOT_NAME'" "$DESTRUCTION_COUNT destroyed," \
then "$WARNING_COUNT warnings."
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT +1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT +1 ))
continue
fi
# Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created.
if [ -z "$opt_keep" ]
then
continue
fi
KEEP="$opt_keep"
for jj in $SNAPSHOTS_OLD
do
# Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$ii@$SNAPSHOT_MATCH}" ]
then
KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le 0 ]
then
if do_run "zfs destroy '$jj'"
then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT +1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
fi
fi
fi
done
done
for ii in $TARGETS_RECURSIVE
do
if do_run "zfs snapshot $SNAPSHOT_PROPERTIES -r '$ii@$SNAPSHOT_NAME'"
then
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT +1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT +1 ))
continue
fi
# Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created.
if [ -z "$opt_keep" ]
then
continue
fi
KEEP="$opt_keep"
# ASSERT: The old snapshot list is sorted by increasing age.
for jj in $SNAPSHOTS_OLD
do
# Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$ii@$SNAPSHOT_MATCH}" ]
then
KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le 0 ]
then
if do_run "zfs destroy -r '$jj'"
then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT +1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
fi
fi
fi
done
done
print_log notice "@$SNAPSHOT_NAME, \
$SNAPSHOT_COUNT created, $DESTRUCTION_COUNT destroyed, $WARNING_COUNT warnings."
exit 0
# } # }