Compare commits

..

6 Commits

Author SHA1 Message Date
2940f443bc Merge pull request #99 from stuehmer/patch-2
Merge changes fom master and add workaround for DASH shell problems
2019-04-12 22:47:04 +02:00
2d6bf078b1 Avoid syntax error in dash by specifically using bash
Error was:
`/usr/sbin/zfs-auto-snapshot: 207: /usr/sbin/zfs-auto-snapshot: Syntax error: redirection unexpected`
2018-10-22 17:21:22 +02:00
f44d47fb4d Merge changes from master into leecallan 2018-10-22 17:11:52 +02:00
87aec9039a Merge branch 'master' into leecallen
Conflicts:
	src/zfs-auto-snapshot.sh
2014-04-16 16:15:01 -04:00
087ac3eba2 Update zfs-auto-snapshot.sh 2014-02-10 17:20:22 -05:00
33d53d8cec Update zfs-auto-snapshot.sh
Remove 'label' from snapshot name and place in a snapshot property
2014-02-04 09:59:58 -05:00
10 changed files with 45 additions and 155 deletions

View File

@ -1,9 +0,0 @@
[ 1.2.5 (Unreleased) ]
* Start a changelog
* Accept PR#94 from aimileus/macos
- Replace --utc longform option with -u for macos compatibility
* Accept PR#107 from ArakniD/master
- Add optional label for snap removals
-- Jonathan Carter 2019-09-25, 15:24 SAST

View File

@ -1,28 +1,17 @@
PREFIX := /usr/local
all: all:
install: install:
install -d $(DESTDIR)/etc/cron.d install -d $(DESTDIR)$(PREFIX)/etc/cron.d
install -d $(DESTDIR)/etc/cron.daily install -d $(DESTDIR)$(PREFIX)/etc/cron.daily
install -d $(DESTDIR)/etc/cron.hourly install -d $(DESTDIR)$(PREFIX)/etc/cron.hourly
install -d $(DESTDIR)/etc/cron.weekly install -d $(DESTDIR)$(PREFIX)/etc/cron.weekly
install -d $(DESTDIR)/etc/cron.monthly install -d $(DESTDIR)$(PREFIX)/etc/cron.monthly
install -m 0644 etc/zfs-auto-snapshot.cron.frequent $(DESTDIR)/etc/cron.d/zfs-auto-snapshot install etc/zfs-auto-snapshot.cron.frequent $(DESTDIR)$(PREFIX)/etc/cron.d/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.hourly $(DESTDIR)/etc/cron.hourly/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)/etc/cron.daily/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)/etc/cron.weekly/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)/etc/cron.monthly/zfs-auto-snapshot install etc/zfs-auto-snapshot.cron.monthly $(DESTDIR)$(PREFIX)/etc/cron.monthly/zfs-auto-snapshot
install -d $(DESTDIR)$(PREFIX)/share/man/man8 install -d $(DESTDIR)$(PREFIX)/share/man/man8
install -m 0644 src/zfs-auto-snapshot.8 $(DESTDIR)$(PREFIX)/share/man/man8/zfs-auto-snapshot.8 install src/zfs-auto-snapshot.8 $(DESTDIR)$(PREFIX)/share/man/man8/zfs-auto-snapshot.8
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
uninstall:
rm $(DESTDIR)/etc/cron.d/zfs-auto-snapshot
rm $(DESTDIR)/etc/cron.hourly/zfs-auto-snapshot
rm $(DESTDIR)/etc/cron.daily/zfs-auto-snapshot
rm $(DESTDIR)/etc/cron.weekly/zfs-auto-snapshot
rm $(DESTDIR)/etc/cron.monthly/zfs-auto-snapshot
rm $(DESTDIR)$(PREFIX)/share/man/man8/zfs-auto-snapshot.8
rm $(DESTDIR)$(PREFIX)/sbin/zfs-auto-snapshot

9
README
View File

@ -10,12 +10,3 @@ 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.
Installation:
-------------
wget https://github.com/zfsonlinux/zfs-auto-snapshot/archive/upstream/1.2.4.tar.gz
tar -xzf 1.2.4.tar.gz
cd zfs-auto-snapshot-upstream-1.2.4
make install

View File

@ -1,6 +1,2 @@
#!/bin/sh #!/bin/sh
# Only call zfs-auto-snapshot if it's available
which zfs-auto-snapshot > /dev/null || exit 0
exec zfs-auto-snapshot --quiet --syslog --label=daily --keep=31 // exec zfs-auto-snapshot --quiet --syslog --label=daily --keep=31 //

View File

@ -1,3 +1,3 @@
PATH="/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin" PATH="/usr/bin:/bin:/usr/sbin:/sbin"
*/15 * * * * root which zfs-auto-snapshot > /dev/null || exit 0 ; zfs-auto-snapshot --quiet --syslog --label=frequent --keep=4 // */15 * * * * root zfs-auto-snapshot -q -g --label=frequent --keep=4 //

View File

@ -1,6 +1,2 @@
#!/bin/sh #!/bin/sh
# Only call zfs-auto-snapshot if it's available
which zfs-auto-snapshot > /dev/null || exit 0
exec zfs-auto-snapshot --quiet --syslog --label=hourly --keep=24 // exec zfs-auto-snapshot --quiet --syslog --label=hourly --keep=24 //

View File

@ -1,6 +1,2 @@
#!/bin/sh #!/bin/sh
# Only call zfs-auto-snapshot if it's available
which zfs-auto-snapshot > /dev/null || exit 0
exec zfs-auto-snapshot --quiet --syslog --label=monthly --keep=12 // exec zfs-auto-snapshot --quiet --syslog --label=monthly --keep=12 //

View File

@ -1,6 +1,2 @@
#!/bin/sh #!/bin/sh
# Only call zfs-auto-snapshot if it's available
which zfs-auto-snapshot > /dev/null || exit 0
exec zfs-auto-snapshot --quiet --syslog --label=weekly --keep=8 // exec zfs-auto-snapshot --quiet --syslog --label=weekly --keep=8 //

View File

@ -64,27 +64,6 @@ Snapshot named filesystem and all descendants.
\fB\-v\fR, \fB\-\-verbose\fR \fB\-v\fR, \fB\-\-verbose\fR
Print info messages. Print info messages.
.TP .TP
\fB\-\-pre-snapshot\fR=\fICOMMAND\fR
Command to run before each dataset is snapshotted.
It is passed the dataset and snapshot name. If it
returns non-zero, snapshotting this dataset is
aborted.
.TP
\fB\-\-post-snapshot\fR=\fICOMMAND\fR
Command to run after each dataset is snapshotted.
It is passed the dataset and snapshot name.
.TP
\fB\-\-destroy-only\fR
Do not create new snapshots, but do destroy older
snapshots. Has no effect unless used with \fB\-k\fR.
.IP
A non-obvious use may be construction of cron jobs or
scripts that run pre-snapshot command(s), then run
zfs-auto-snapshot (without \fB\-k\fR) to quickly
snapshot all datasets, then run post-snapshot
command(s) and clean up with zfs-auto-snapshot
\fB\-\-destroy-only\fR.
.TP
name name
Filesystem and volume names, or '//' for all ZFS datasets. Filesystem and volume names, or '//' for all ZFS datasets.
.SH SEE ALSO .SH SEE ALSO

108
src/zfs-auto-snapshot.sh Normal file → Executable file
View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
# zfs-auto-snapshot for Linux # zfs-auto-snapshot for Linux
# Automatically create, rotate, and destroy periodic ZFS snapshots. # Automatically create, rotate, and destroy periodic ZFS snapshots.
@ -42,7 +42,6 @@ opt_verbose=''
opt_pre_snapshot='' opt_pre_snapshot=''
opt_post_snapshot='' opt_post_snapshot=''
opt_do_snapshots=1 opt_do_snapshots=1
opt_min_size=0
# Global summary statistics. # Global summary statistics.
DESTRUCTION_COUNT='0' DESTRUCTION_COUNT='0'
@ -111,7 +110,7 @@ print_log () # level, message, ...
;; ;;
(inf*) (inf*)
# test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.info $* # test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.info $*
test -z "$opt_quiet" && test -n "$opt_verbose" && echo $* test -z ${opt_quiet+x} && test -n "$opt_verbose" && echo $*
;; ;;
(deb*) (deb*)
# test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.debug $* # test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.debug $*
@ -153,6 +152,7 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...]
local GLOB="$4" local GLOB="$4"
local TARGETS="$5" local TARGETS="$5"
local KEEP='' local KEEP=''
local LABEL="$6"
local RUNSNAP=1 local RUNSNAP=1
# global DESTRUCTION_COUNT # global DESTRUCTION_COUNT
@ -162,23 +162,7 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...]
for ii in $TARGETS for ii in $TARGETS
do do
# Check if size check is > 0 if [ -n "$opt_do_snapshots" ]
size_check_skip=0
if [ "$opt_min_size" -gt 0 ]
then
bytes_written=`zfs get -Hp -o value written $ii`
kb_written=$(( $bytes_written / 1024 ))
if [ "$kb_written" -lt "$opt_min_size" ]
then
size_check_skip=1
if [ $opt_verbose -gt 0 ]
then
echo "Skipping target $ii, only $kb_written kB written since last snap. opt_min_size is $opt_min_size"
fi
fi
fi
if [ -n "$opt_do_snapshots" -a "$size_check_skip" -eq 0 ]
then then
if [ "$opt_pre_snapshot" != "" ] if [ "$opt_pre_snapshot" != "" ]
then then
@ -191,24 +175,28 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...]
else else
WARNING_COUNT=$(( $WARNING_COUNT + 1 )) WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
continue continue
fi fi
fi fi
# Retain at most $opt_keep number of old snapshots of this filesystem, # Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created. # including the one that was just recently created.
test -z "$opt_keep" && continue test -z "$opt_keep" && continue
KEEP="$opt_keep" KEEP="$opt_keep"
# ASSERT: The old snapshot list is sorted by increasing age. # ASSERT: The old snapshot list is sorted by increasing age.
for jj in $SNAPSHOTS_OLD # Match on both $GLOB *and* label.
while IFS=' ' read -ra arrjj
do do
# Check whether this is an old snapshot of the filesystem. # Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$ii@$GLOB}" ] if [ -z "${arrjj[1]#$ii@$GLOB}" ]
then then
# Check whether the snapshot has the same label property
test "${arrjj[0]}" = "$LABEL" || continue
KEEP=$(( $KEEP - 1 )) KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le '0' ] if [ "$KEEP" -le '0' ]
then then
if do_run "zfs destroy -d $FLAGS '$jj'" if do_run "zfs destroy -d $FLAGS '${arrjj[1]}'"
then then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT + 1 )) DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT + 1 ))
else else
@ -216,7 +204,7 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...]
fi fi
fi fi
fi fi
done done <<< "$SNAPSHOTS_OLD"
done done
} }
@ -224,19 +212,12 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...]
# main () # main ()
# { # {
if [ "$(uname)" = "Darwin" ]; then GETOPT=$(getopt \
GETOPT_BIN="$(brew --prefix gnu-getopt 2> /dev/null || echo /usr/local)/bin/getopt"
else
GETOPT_BIN="getopt"
fi
GETOPT=$($GETOPT_BIN \
--longoptions=default-exclude,dry-run,fast,skip-scrub,recursive \ --longoptions=default-exclude,dry-run,fast,skip-scrub,recursive \
--longoptions=event:,keep:,label:,prefix:,sep: \ --longoptions=event:,keep:,label:,prefix:,sep: \
--longoptions=debug,help,quiet,syslog,verbose \ --longoptions=debug,help,quiet,syslog,verbose \
--longoptions=pre-snapshot:,post-snapshot:,destroy-only \ --longoptions=pre-snapshot:,post-snapshot:,destroy-only \
--longoptions=min-size: \ --options=dnshe:l:k:p:rs:qgv \
--options=dnshe:l:k:p:rs:qgvm: \
-- "$@" ) \ -- "$@" ) \
|| exit 128 || exit 128
@ -295,10 +276,6 @@ do
opt_label="$2" opt_label="$2"
shift 2 shift 2
;; ;;
(-m|--min-size)
opt_min_size="$2"
shift 2
;;
(-p|--prefix) (-p|--prefix)
opt_prefix="$2" opt_prefix="$2"
while test "${#opt_prefix}" -gt '0' while test "${#opt_prefix}" -gt '0'
@ -325,7 +302,7 @@ do
shift 1 shift 1
;; ;;
(--sep) (--sep)
case "$2" in case "$2" in
([[:alnum:]_.:\ -]) ([[:alnum:]_.:\ -])
: :
;; ;;
@ -373,7 +350,7 @@ if [ "$#" -eq '0' ]
then then
print_log error "The filesystem argument list is empty." print_log error "The filesystem argument list is empty."
exit 133 exit 133
fi fi
# Count the number of times '//' appears on the command line. # Count the number of times '//' appears on the command line.
SLASHIES='0' SLASHIES='0'
@ -402,26 +379,14 @@ ZFS_LIST=$(env LC_ALL=C zfs list -H -t filesystem,volume -s name \
if [ -n "$opt_fast_zfs_list" ] if [ -n "$opt_fast_zfs_list" ]
then then
# Check if a snapshot label is being used, in which case restrict the old SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -o com.sun:auto-snapshot-label,name -s name | \
# snapshot removal to only snapshots with the same label format grep $opt_prefix | \
if [ -n "$opt_label" ] awk '{ print substr( $0, length($0) - 14, length($0) ) " " $0}' | \
then sort -r -k1,1 -k3,2 | \
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -o name -s name | \ awk '{ print substr( $0, 17, length($0) )}') \
grep "$opt_prefix"_"$opt_label" | \ || { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
awk '{ print substr( $0, length($0) - 14, length($0) ) " " $0}' | \
sort -r -k1,1 -k2,2 | \
awk '{ print substr( $0, 17, length($0) )}') \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
else
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -o name -s name | \
grep $opt_prefix | \
awk '{ print substr( $0, length($0) - 14, length($0) ) " " $0}' | \
sort -r -k1,1 -k2,2 | \
awk '{ print substr( $0, 17, length($0) )}') \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
fi
else else
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 com.sun:auto-snapshot-label,name) \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; } || { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
fi fi
@ -482,19 +447,11 @@ do
iii="$ii/" iii="$ii/"
# Exclude datasets # Exclude datasets that are not named on the command line.
# * that are not named on the command line or
# * those whose prefix is not on the command line (if --recursive flag is set)
IN_ARGS='0' IN_ARGS='0'
for jj in "$@" for jj in "$@"
do do
# Ibid regarding iii.
jjj="$jj/"
if [ "$jj" = '//' -o "$jj" = "$ii" ] if [ "$jj" = '//' -o "$jj" = "$ii" ]
then
IN_ARGS=$(( $IN_ARGS + 1 ))
elif [ -n "$opt_recursive" -a "$iii" != "${iii#$jjj}" ]
then then
IN_ARGS=$(( $IN_ARGS + 1 )) IN_ARGS=$(( $IN_ARGS + 1 ))
fi fi
@ -580,18 +537,17 @@ done
# Linux lacks SMF and the notion of an FMRI event, but always set this property # Linux lacks SMF and the notion of an FMRI event, but always set this property
# because the SUNW program does. The dash character is the default. # because the SUNW program does. The dash character is the default.
SNAPPROP="-o com.sun:auto-snapshot-desc='$opt_event'" SNAPPROP="-o com.sun:auto-snapshot-desc='$opt_event' -o com.sun:auto-snapshot-label=$opt_label"
# ISO style date; fifteen characters: YYYY-MM-DD-HHMM # ISO style date; fifteen characters: YYYY-MM-DD-HHMM
# On Solaris %H%M expands to 12h34. # On Solaris %H%M expands to 12h34.
# We use the shortfirm -u here because --utc is not supported on macos. DATE=$(date --utc +%F-%H%M)
DATE=$(date -u +%F-%H%M)
# The snapshot name after the @ symbol. # The snapshot name after the @ symbol.
SNAPNAME="${opt_prefix:+$opt_prefix$opt_sep}${opt_label:+$opt_label}-$DATE" SNAPNAME="$opt_prefix$opt_sep$DATE"
# The expression for matching old snapshots. -YYYY-MM-DD-HHMM # The expression for matching old snapshots. -YYYY-MM-DD-HHMM
SNAPGLOB="${opt_prefix:+$opt_prefix$opt_sep}${opt_label:+$opt_label}-???????????????" SNAPGLOB="${opt_prefix}????????????????"
if [ -n "$opt_do_snapshots" ] if [ -n "$opt_do_snapshots" ]
then then
@ -619,8 +575,8 @@ fi
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" "" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_REGULAR" "$opt_label"
do_snapshots "$SNAPPROP" "-r" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_RECURSIVE" do_snapshots "$SNAPPROP" "-r" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_RECURSIVE" "$opt_label"
print_log notice "@$SNAPNAME," \ print_log notice "@$SNAPNAME," \
"$SNAPSHOT_COUNT created," \ "$SNAPSHOT_COUNT created," \