19 Commits

Author SHA1 Message Date
45edfd3e7c Merge pull request #9 from mk01/zevo
Zevo
2013-01-14 11:05:37 -08:00
85dd8dad9a missing quotations, under bash ok, under dash critical 2013-01-14 11:13:47 +01:00
feea013d3c cosmetic bug which showed filesystem added for regular snapshot as recursive 2013-01-14 11:07:54 +01:00
ad9a57eeaf better snapshot consistency testing during --send-full and -R 2013-01-14 11:06:03 +01:00
82ff4b2f80 bug fix (wrong recursive filesystem list expansion)
in case of empty filesystem list going for recursive snapshot all
filesystems have been processed
2013-01-14 10:56:49 +01:00
9bf3d5b017 Revert "bug fix (wrong recursive filesystem list expansion)"
This reverts commit d6c05fb8f4.
2013-01-14 10:37:32 +01:00
ba171cc032 Revert "bug fix (snapshot list created for destroy in case of incremental send through replication)"
This reverts commit 8b155cf4da.
2013-01-14 10:37:13 +01:00
8b155cf4da bug fix (snapshot list created for destroy in case of incremental send through replication) 2013-01-14 10:35:32 +01:00
d6c05fb8f4 bug fix (wrong recursive filesystem list expansion)
in case of empty filesystem list going for recursive snapshot all
filesystems have been processed
2013-01-14 10:35:03 +01:00
4bbfa476b1 general cleanup, quotations updates etc 2013-01-09 14:17:49 -06:00
583fc20e28 error return codes renumbering, parameter checking for --send 2013-01-03 14:49:05 -06:00
2d789bdf1d detection of system (Linux/Darwin). proper detection of mounted snapshots prior to umount.
detection of system applies for local and remote operations. should run
on linux and macosx with no modifications in the code.
2013-01-03 14:49:05 -06:00
8aaf5f904b issue #8 should be solved by this patch
all filesystems with snapdir=visible and canmount=on are traversed for
snapshots going to be removed and umount -f is called.

on linux, on /proc/mounts such mounted snapshots are visible (not
listed with 'mount'), but under macosx no proc exists, 'mount' again
does not list them so umount -f is called with no mercy for all
specific combination of fs and snapname  (but should not cause any
problems).

there is no specific logic to utilize /proc/mount on linux, same
approach is taken.
2012-12-28 14:14:23 -06:00
6f91e10033 mktemp on macosx uses some .Xs by default, which is not the case on linux 2012-12-28 14:14:23 -06:00
1099c622cb comments update 2012-12-27 20:58:22 -06:00
70632c00b7 going back to standard /bin/sh (bash 3.x on macosx) 2012-12-27 20:58:22 -06:00
90825db245 README updates 2012-12-27 20:58:22 -06:00
676ded6217 fix for filesystem expansion when using '//' 2012-12-27 20:58:22 -06:00
4d20aa4c79 Zevo support. 2012-12-27 20:58:10 -06:00
12 changed files with 540 additions and 389 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.DS_Store

View File

@ -1,19 +1,15 @@
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 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

15
README
View File

@ -1,21 +1,14 @@
zfs-auto-snapshot: zfs-auto-snapshot:
An alternative implementation of the zfs-auto-snapshot service for Linux An alternative implementation of the zfs-auto-snapshot service for Macosx
that is compatible with zfs-linux and zfs-fuse. that is compatible with ZEVO community zfs.
Automatically create, rotate, and destroy periodic ZFS snapshots. This is Automatically create, rotate, and destroy periodic ZFS snapshots. This is
the utility that creates the @zfs-auto-snap_frequent, @zfs-auto-snap_hourly, 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 @zfs-auto-snap_daily, @zfs-auto-snap_weekly, and @zfs-auto-snap_monthly
snapshots if it is installed. snapshots if it is installed.
It can backup to remote systems utilizing zfs send command.
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/master.zip
unzip master.zip
cd zfs-auto-snapshot-master
make install

100
debian/changelog vendored
View File

@ -1,100 +0,0 @@
zfs-auto-snapshot (1.2.0-1) unstable; urgency=low
[ Roland Stühmer ]
* Fix SNAPNAME to contain the DATE if label is empty
[ Borut Mrak ]
* Remove PREFIX from cron scripts - they should always end up in /etc.
* Leave PREFIX for man page and program, and set it to /usr/local by default.
[ Maciej Małecki ]
* Add `--{pre,post}-snapshot` for pre and post creation hook
* Abort if pre snapshot hook returns non-zero
* Document `--{pre,post}-snapshot
[ Mike Baynton ]
* Make --{pre,post}-snapshot optional, add --destroy-only, revise docs
[ virtualguy ]
* Update README
[ Darik Horn ]
* Install the cron.d part without the execute bit.
* Update debian/rules for Makefile changes.
-- Darik Horn <dajhorn@vanadac.com> Mon, 13 Oct 2014 23:17:30 -0500
zfs-auto-snapshot (1.1.0-0ubuntu1) unstable; urgency=low
[ Markus Heberling ]
* Use only name property for zfs list
[ Darik Horn ]
* Add a switch for the fast zfs list invocation.
[ Mike Swanson ]
* Add a manpage
* Update Makefile for the manual page
[ HawkOwl ]
* Set the snapshots to use UTC time, which makes the date/time display correctly in Windows 7 Previous Versions.
-- Darik Horn <dajhorn@vanadac.com> Mon, 31 Mar 2014 01:48:58 -0500
zfs-auto-snapshot (1.0.8-0ubuntu2) unstable; urgency=low
* Update the debian/control file.
-- Darik Horn <dajhorn@vanadac.com> Sun, 31 Mar 2013 23:10:55 -0500
zfs-auto-snapshot (1.0.8-0ubuntu1) unstable; urgency=low
* Update the README file to be more descriptive.
* Fix pool exclusions.
-- Darik Horn <dajhorn@vanadac.com> Sat, 21 Jan 2012 16:37:28 -0600
zfs-auto-snapshot (1.0.7-0ubuntu1) unstable; urgency=low
[ Darik Horn ]
* Change objects to datasets in help and comments.
* Set keep=8 in the weekly cron job.
[ Ulrich Petri ]
* Fixed --prefix and --sep regexes
-- Darik Horn <dajhorn@vanadac.com> Wed, 28 Dec 2011 13:30:02 -0600
zfs-auto-snapshot (1.0.6-0ubuntu1) unstable; urgency=low
* Remove redundant lines in the cron.d file.
* Fix the hourly-daily transposition in the Makefile.
-- Darik Horn <dajhorn@vanadac.com> Mon, 28 Nov 2011 00:10:05 -0600
zfs-auto-snapshot (1.0.5-0ubuntu1) unstable; urgency=low
* Split the cron file for anacron compatibilty.
-- Darik Horn <dajhorn@vanadac.com> Fri, 25 Nov 2011 14:28:07 -0600
zfs-auto-snapshot (1.0.4-0ubuntu1) unstable; urgency=low
* Invert the --skip-scrub test.
* Fold the main loops into a do_snapshots function.
* Consistently quote literal strings.
* Implement --prefix parameter checking.
* Rebase exit codes to above 127.
* Implement the --event option for :auto-snap-desc.
-- Darik Horn <dajhorn@vanadac.com> Wed, 23 Nov 2011 10:49:14 -0600
zfs-auto-snapshot (1.0.3-0ubuntu1) unstable; urgency=low
* Add -q to the getopt list of short options.
* Add an explicit `exit 0` to the end of the script.
* Add gbp.conf file for git-buildpackage.
* Expand the options variable in the cron part file.
* Initial debian packaging.
-- Darik Horn <dajhorn@vanadac.com> Mon, 21 Nov 2011 21:52:11 -0600

1
debian/compat vendored
View File

@ -1 +0,0 @@
7

16
debian/control vendored
View File

@ -1,16 +0,0 @@
Source: zfs-auto-snapshot
Section: admin
Priority: extra
Maintainer: Darik Horn <dajhorn@vanadac.com>
Build-Depends: debhelper (>= 8)
Standards-Version: 3.9.4
Homepage: http://www.zfsonlinux.org/
Vcs-Git: git://github.com/zfsonlinux/zfs-auto-snapshot.git
Vcs-Browser: https://github.com/zfsonlinux/zfs-auto-snapshot/
Package: zfs-auto-snapshot
Architecture: all
Depends: ${misc:Depends}, cron, zfsutils
Description: ZFS Automatic Snapshot Service
Automatically create and destroy ZFS snapshots on an hourly, daily, weekly
and monthly schedule.

1
debian/copyright vendored
View File

@ -1 +0,0 @@
Copyright 2011 Darik Horn <dajhorn@vanadac.com>

3
debian/gbp.conf vendored
View File

@ -1,3 +0,0 @@
[DEFAULT]
upstream-branch = master
debian-branch = debian

7
debian/rules vendored
View File

@ -1,7 +0,0 @@
#!/usr/bin/make -f
%:
dh $@
override_dh_auto_install:
dh_auto_install -- PREFIX=/usr

2
debian/watch vendored
View File

@ -1,2 +0,0 @@
version=3
http://githubredir.debian.net/github/dajhorn/zfs-auto-snapshot/([\d\.].*).tar.gz

View File

@ -1,91 +0,0 @@
.TH ZFS-AUTO-SNAPSHOT "8" "June 16, 2013" "zfs-auto-snapshot.sh" "System Administration Commands"
.SH NAME
zfs-auto-snapshot \- take regular ZFS snapshots
.SH SYNOPSIS
.B zfs-auto-snapshot
[\fIoptions\fR] [\fI-l label\fR] \fI<'//' | name \fR[\fIname\fR...]\fI>\fR
.SH DESCRIPTION
.B zfs-auto-snapshot
automatically creates, rotates, and destroys snapshots for all your
ZFS datasets, and is compatible with both zfsonlinux and zfs-fuse.
.SH OPTIONS
.TP
\fB\-\-default\-exclude\fR
By default \fBzfs-auto-snapshot\fR will snapshot all datasets except
for those in which the user-property \fBcom.sun:auto-snapshot\fR is
set to \fBfalse\fR. This option reverses the behavior and requires
\fBcom.sun:auto-snapshot\fR to be set to \fBtrue\fR.
.TP
\fB\-d\fR, \fB\-\-debug\fR
Print debugging messages.
.TP
\fB\-e\fR, \fB\-\-event\fR=\fIEVENT\fR
Set the com.sun:auto\-snapshot\-desc property to EVENT.
.TP
\fB\-\-fast\fR
Use a faster zfs list invocation.
.TP
\fB\-n\fR, \fB\-\-dry\-run\fR
Print actions without actually doing anything.
.TP
\fB\-s\fR, \fB\-\-skip\-scrub\fR
Do not snapshot filesystems in scrubbing pools.
.TP
\fB\-h\fR, \fB\-\-help\fR
Print the usage message.
.TP
\fB\-k\fR, \fB\-\-keep\fR=\fINUM\fR
Keep NUM recent snapshots and destroy older snapshots.
.TP
\fB\-l\fR, \fB\-\-label\fR=\fILAB\fR
LAB is usually 'hourly', 'daily', or 'monthly'.
.TP
\fB\-p\fR, \fB\-\-prefix\fR=\fIPRE\fR
PRE is 'zfs\-auto\-snap' by default.
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Suppress warnings and notices at the console.
.TP
\fB\-\-send\-full\fR=\fIF\fR
Send zfs full backup. Unimplemented.
.TP
\fB\-\-send\-incr\fR=\fIF\fR
Send zfs incremental backup. Unimplemented.
.TP
\fB\-\-sep\fR=\fICHAR\fR
Use CHAR to separate date stamps in snapshot names.
.TP
\fB\-g\fR, \fB\-\-syslog\fR
Write messages into the system log.
.TP
\fB\-r\fR, \fB\-\-recursive\fR
Snapshot named filesystem and all descendants.
.TP
\fB\-v\fR, \fB\-\-verbose\fR
Print info messages.
.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 constructon 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
Filesystem and volume names, or '//' for all ZFS datasets.
.SH SEE ALSO
.BR zfs (8)

View File

@ -1,8 +1,9 @@
#!/bin/sh #!/bin/bash
# zfs-auto-snapshot for Linux # zfs-auto-snapshot for Linux and Macosx
# Automatically create, rotate, and destroy periodic ZFS snapshots. # Automatically create, rotate, and destroy periodic ZFS snapshots.
# Copyright 2011 Darik Horn <dajhorn@vanadac.com> # Copyright 2011 Darik Horn <dajhorn@vanadac.com>
# zfs send options and macosx relevant changes by Matus Kral <matuskral@me.com>
# #
# This program is free software; you can redistribute it and/or modify it under # This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software # the terms of the GNU General Public License as published by the Free Software
@ -29,9 +30,8 @@ opt_backup_incremental=''
opt_default_exclude='' opt_default_exclude=''
opt_dry_run='' opt_dry_run=''
opt_event='-' opt_event='-'
opt_fast_zfs_list=''
opt_keep='' opt_keep=''
opt_label='' opt_label='regular'
opt_prefix='zfs-auto-snap' opt_prefix='zfs-auto-snap'
opt_recursive='' opt_recursive=''
opt_sep='_' opt_sep='_'
@ -39,40 +39,75 @@ opt_setauto=''
opt_syslog='' opt_syslog=''
opt_skip_scrub='' opt_skip_scrub=''
opt_verbose='' opt_verbose=''
opt_pre_snapshot='' opt_remove=''
opt_post_snapshot='' opt_fallback='0'
opt_do_snapshots=1 opt_force=''
opt_sendprefix=''
opt_send='no'
opt_atonce='-I'
opt_create='0'
opt_destroy='0'
# if pipe needs to be used, uncomment opt_pipe="|"
opt_sendtocmd='ssh -1 root@media -i /var/root/.ssh/media.rsa1'
opt_pipe='|'
# Global summary statistics. # Global summary statistics.
DESTRUCTION_COUNT='0' DESTRUCTION_COUNT='0'
SNAPSHOT_COUNT='0' SNAPSHOT_COUNT='0'
WARNING_COUNT='0' WARNING_COUNT='0'
CREATION_COUNT='0'
SENT_COUNT='0'
KEEP=''
PLATFORM_LOC=''
PLATFORM_REM=''
# Other global variables. # Other global variables.
SNAPSHOTS_OLD='' declare -a SNAPSHOTS_OLD_LOC=('')
declare -a SNAPSHOTS_OLD_REM=('')
declare -a CREATED_TARGETS=('')
declare -a ZFS_REMOTE_LIST=('')
declare -a ZFS_LOCAL_LIST=('')
declare -a TARGETS_DRECURSIVE=('')
declare -a TARGETS_DREGULAR=('')
declare -a MOUNTED_LIST_LOC=('')
declare -a MOUNTED_LIST_REM=('')
declare -i RC=99
tmp_file_prefix="/tmp/zfs-auto-snapshot.XXXXXXXX"
set -o pipefail
print_usage () print_usage ()
{ {
echo "Usage: $0 [options] [-l label] <'//' | name [name...]> echo "Usage: $0 [options] [-l label] <'//' | name [name...]>
--default-exclude Exclude datasets if com.sun:auto-snapshot is unset. --default-exclude Exclude datasets if com.sun:auto-snapshot is unset.
--remove-local=n Remove local snapshots after successfully sent via --send-incr or --send-full but still keeps n newest snapshots
(this will destroy snapshots named according to --prefix, but regardless of --label).
-d, --debug Print debugging messages. -d, --debug Print debugging messages.
-e, --event=EVENT Set the com.sun:auto-snapshot-desc property to EVENT. -e, --event=EVENT Set the com.sun:auto-snapshot-desc property to EVENT.
--fast Use a faster zfs list invocation.
-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.
-k, --keep=NUM Keep NUM recent snapshots and destroy older snapshots. -k, --keep=NUM Keep NUM recent snapshots and destroy older snapshots.
-l, --label=LAB LAB is usually 'hourly', 'daily', or 'monthly'. -l, --label=LAB LAB is usually 'hourly', 'daily', or 'monthly' (default is 'regular').
-p, --prefix=PRE PRE is 'zfs-auto-snap' by default. -p, --prefix=PRE PRE is 'zfs-auto-snap' by default.
-q, --quiet Suppress warnings and notices at the console. -q, --quiet Suppress warnings and notices at the console.
--send-full=F Send zfs full backup. Unimplemented. -c, --create Create missing filesystems at destination.
--send-incr=F Send zfs incremental backup. Unimplemented. -i, --send-at-once Send more incremental snapshots at once in one package (-i argument is passed to zfs send instead of -I).
--send-full=F Send zfs full backup. F is target filesystem.
--send-incr=F Send zfs incremental backup. F is target filesystem.
--sep=CHAR Use CHAR to separate date stamps in snapshot names. --sep=CHAR Use CHAR to separate date stamps in snapshot names.
-X, --destroy Destroy remote snapshots to allow --send-full if destination has snapshots (needed for -F in case incremental
snapshots on local and remote do not match).
-F, --fallback Allow fallback from --send-incr to --send-full, if incremental sending is not possible (filesystem on remote just
created or snapshots do not match - see -X).
-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.
-R, --replication Use zfs's replication (zfs send -R) instead of simple send over newly created snapshots (check man zfs for details).
-f is used automatically.
-v, --verbose Print info messages. -v, --verbose Print info messages.
--destroy-only Only destroy older snapshots, do not create new ones. -f, --force Passes -F argument to zfs receive (e.g. makes possible to overwrite remote filesystem during --send-full)
name Filesystem and volume names, or '//' for all ZFS datasets. name Filesystem and volume names, or '//' for all ZFS datasets.
" "
} }
@ -103,6 +138,7 @@ print_log () # level, message, ...
(war*) (war*)
test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.warning $* test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.warning $*
test -z "$opt_quiet" && echo Warning: $* 1>&2 test -z "$opt_quiet" && echo Warning: $* 1>&2
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
;; ;;
(not*) (not*)
test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.notice $* test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.notice $*
@ -126,13 +162,15 @@ print_log () # level, message, ...
do_run () # [argv] do_run () # [argv]
{ {
if [ -n "$opt_dry_run" ] if [ -n "$opt_dry_run" ]
then then
echo $* print_log notice "... running: $*"
RC="$?" RC="$?"
else else
eval $* eval $*
RC="$?" RC="$?"
if [ "$RC" -eq '0' ] if [ "$RC" -eq '0' ]
then then
print_log debug "$*" print_log debug "$*"
@ -140,79 +178,345 @@ do_run () # [argv]
print_log warning "$* returned $RC" print_log warning "$* returned $RC"
fi fi
fi fi
return "$RC" return "$RC"
} }
do_unmount ()
{
local TYPE="$1"
local FLAGS="$3"
local FSNAME="$4"
local SNAPNAME="$5"
local rsort_cmd='0'
local remote_cmd=''
case "$TYPE" in
(remote)
umount_list=( "${MOUNTED_LIST_REM[@]}" )
remote_cmd="$opt_sendtocmd"
;;
(local)
umount_list=( "${MOUNTED_LIST_LOC[@]}" )
;;
esac
if [ -n "$SNAPNAME" ]; then
SNAPNAME="@$SNAPNAME"
else
rsort_cmd='1'
fi
umount_list=( $(printf "%s\t%s\n" "${umount_list[@]}" | grep ^"$FSNAME$SNAPNAME" ) )
test -z "$umount_list" && return 0
# reverse sort the list if unmounting filesystem and not only snapshot
umount_list=($(printf "%s\t%s\n" "${umount_list[@]}" | awk -F'\t' '{print $2}'))
test $rsort_cmd -eq '1' && umount_list=($(printf "%s\n" "${umount_list[@]}"))
for kk in ${umount_list[@]}; do
print_log debug "Trying to unmount '$kk'."
umount_cmd="umount '$kk'"
if ! do_run "$remote_cmd" "$umount_cmd"; then return "$RC"; fi
test "$FLAGS" != "-r" && break
done
return 0
}
do_delete ()
{
local DEL_TYPE="$1"
local FSSNAPNAME="$2"
local FLAGS="$3"
local FSNAME=$(echo $FSSNAPNAME | awk -F'@' '{print $1}')
local SNAPNAME=$(echo $FSSNAPNAME | awk -F'@' '{print $2}')
local remote_cmd=''
if [ "$FSSNAPNAME" = "$FSNAME" -a "$FLAGS" = "-r" ]; then
if [ "$opt_destroy" -ne '1' ]; then
print_log warning "Filesystem $FSNAME destroy requested, but option -X not specified. Aborting."
return 1
else
KEEP='0'
fi
fi
KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le '0' ]
then
if do_unmount "$DEL_TYPE" "" "$FLAGS" "$FSNAME" "$SNAPNAME"; then
if [ "$DEL_TYPE" = "remote" ]; then
remote_cmd="$opt_sendtocmd"
fi
if do_run "$remote_cmd" "zfs destroy $FLAGS '$FSSNAPNAME'"; then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT + 1 ))
fi
fi
fi
return "$RC"
}
is_member ()
{
local ARRAY=(${1})
local MEMBER="$2"
declare -i ISMEMBER=0
result=$(printf "%s\n" "${ARRAY[@]}"| grep -m1 -x "$MEMBER")
if [ -n "$result" -a -z "${result#$MEMBER}" ]; then ISMEMBER=1; fi
return "$ISMEMBER"
}
do_send ()
{
local SENDTYPE="$1"
local SNAPFROM="$2"
local SNAPTO="$3"
local SENDFLAGS="$4"
local REMOTEFS="$5"
local list_child=('')
if [ "$SENDFLAGS" = "-R" -a "$SENDTYPE" = "full" ]; then
# for full send with -R, target filesystem must be with no snapshots (including snapshots on child filesystems)
list_child=( $(printf "%s\n" "${SNAPSHOTS_OLD_REM[@]}" | grep ^"$REMOTEFS/" ) )
fi
if [ "$SENDTYPE" = "full" ]; then
list_child=( ${list_child[@]} $(printf "%s\n" "${SNAPSHOTS_OLD_REM[@]}" | grep ^"$REMOTEFS@" ) )
fi
for ll in ${list_child[@]}; do
if do_delete "remote" "$ll" ""; then
continue
fi
print_log debug "Can't destroy remote filesystem $REMOTEFS ($ll). Can't continue with send-full."
return 1
done
test $SENDTYPE = "incr" && do_run "zfs send " "$SENDFLAGS" "$opt_atonce $SNAPFROM $SNAPTO" "$opt_pipe" "$opt_sendtocmd" "zfs recv $opt_force -u $REMOTEFS"
test $SENDTYPE = "full" && do_run "zfs send " "$SENDFLAGS" "$SNAPTO" "$opt_pipe" "$opt_sendtocmd" "zfs recv $opt_force -u $REMOTEFS"
return "$RC"
}
do_snapshots () # properties, flags, snapname, oldglob, [targets...] do_snapshots () # properties, flags, snapname, oldglob, [targets...]
{ {
local PROPS="$1" local PROPS="$1"
local FLAGS="$2" local sFLAGS="$2"
local NAME="$3" local NAME="$3"
local GLOB="$4" local GLOB="$4"
local TARGETS="$5" local TARGETS=(${5})
local KEEP='' local KEEP=''
local RUNSNAP=1 local LAST_REMOTE=''
# global DESTRUCTION_COUNT if test "$sFLAGS" = '-R'; then
# global SNAPSHOT_COUNT FLAGS='-r'
# global WARNING_COUNT SNexp='.*'
# global SNAPSHOTS_OLD else
FLAGS=''
fi
for ii in $TARGETS for ii in ${TARGETS[@]}
do do
if [ -n "$opt_do_snapshots" ]
FALLBACK='0'
SND_RC='1'
print_log debug "--> Snapshooting $ii"
if ! do_run "zfs snapshot $PROPS $FLAGS '$ii@$NAME'"
then then
if [ "$opt_pre_snapshot" != "" ] continue
then
do_run "$opt_pre_snapshot $ii $NAME" || RUNSNAP=0
fi
if [ $RUNSNAP -eq 1 ] && do_run "zfs snapshot $PROPS $FLAGS '$ii@$NAME'"
then
[ "$opt_post_snapshot" != "" ] && do_run "$opt_post_snapshot $ii $NAME"
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT + 1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
continue
fi
fi fi
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT + 1 ))
if [ "$opt_send" = "incr" ]
then
LAST_REMOTE=$(printf "%s\n" ${SNAPSHOTS_OLD_REM[@]} | grep ^$opt_sendprefix/$ii@ | grep -m1 . | awk -F'@' '{print $2}')
# in case of -R and incremental send, receiving side needs to have $LAST_REMOTE snapshot for each replicated filesystem
if [ "$FLAGS" = "-r" ]; then
snaps_needed=$(( $(printf "%s\n" ${ZFS_LIST_LOC[@]} | grep ^"$ii/") + 1 ))
else
snaps_needed='1'
fi
# remote filesystem just created. if -R run
if ! is_member "${CREATED_TARGETS[*]}" "$opt_sendprefix/$ii"
then
FALLBACK='2'
elif [ -z "$LAST_REMOTE" ]
then
# no snapshot on remote
FALLBACK='1'
elif [ "$snaps_needed" -ne $(printf "%s\n" ${SNAPSHOTS_OLD_REM[@]} | grep -c -e ^"$opt_sendprefix/$ii$SNexp@$LAST_REMOTE" ) -o \
"$snaps_needed" -ne $(printf "%s\n" ${SNAPSHOTS_OLD_LOC[@]} | grep -c -e ^"$ii$SNexp@$LAST_REMOTE" ) ]
then
FALLBACK='3'
else
FALLBACK='0'
fi
case "$FALLBACK" in
(1)
print_log info "Going back to full send, no snapshot exists at destination: $ii"
;;
(2)
print_log info "Going back to full send, remote filesystem was just created: $ii"
;;
(3)
if [ "$FLAGS" = "-r" ]; then
print_log info "Going back to full send, last snapshot on remote is not the last one for whole recursion: $opt_sendprefix/$ii@$LAST_REMOTE"
else
print_log info "Going back to full send, last snapshot on remote is not available on local: $opt_sendprefix/$ii@$LAST_REMOTE"
fi
;;
(0)
do_send "incr" "$ii@$LAST_REMOTE" "$ii@$NAME" "$sFLAGS" "$opt_sendprefix/$ii"
SND_RC="$?"
;;
esac
fi
if [ "$opt_send" = "full" -o "$FALLBACK" -ne '0' -a "$opt_fallback" -eq '1' ]; then
do_send "full" "" "$ii@$NAME" "$sFLAGS" "$opt_sendprefix/$ii"
SND_RC="$?"
fi
test "$SND_RC" -eq '0' && SENT_COUNT=$(( $SENT_COUNT + 1 ))
# 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 if [ -z "$opt_keep" ]
KEEP="$opt_keep" then
print_log debug "Number of snapshots not specified. Keeping all."
continue
elif [ "$opt_send" != "no" ] && [ "$SND_RC" -ne '0' ]
then
print_log debug "Sending of filesystem was requested, but send failed. Ommiting destroy procedures."
continue
elif [ "$opt_send" != "no" -a -n "$opt_remove" ]
then
KEEP="$opt_remove"
else
KEEP="$opt_keep"
fi
print_log debug "Destroying local snapshots, keeping $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 for jj in ${SNAPSHOTS_OLD_LOC[@]}
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}" ] test -z "${jj#$ii@$GLOB}" -o -z "${jj##$ii@$opt_prefix*}" -a -n "$opt_remove" \
then -a "$opt_send" != "no" && do_delete "local" "$jj" "$FLAGS"
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
if [ "$opt_send" = "no" ]
then
print_log debug "No sending option specified, skipping remote snapshot removal."
continue
elif [ "$sFLAGS" = "-R" ]
then
print_log debug "Replication specified, remote snapshots were removed while sending."
continue
elif [ "$opt_destroy" -eq '1' -a "$FALLBACK" -ne '0' -o "$opt_send" = "full" ]
then
print_log debug "Sent full copy, all remote snapshots were already destroyed."
continue
else
KEEP="$opt_keep"
print_log debug "Destroying remote snapshots, keeping only $KEEP."
fi
# ASSERT: The old snapshot list is sorted by increasing age.
for jj in ${SNAPSHOTS_OLD_REM[@]}
do
# Check whether this is an old snapshot of the filesystem.
test -z "${jj#$opt_sendprefix/$ii@$GLOB}" && do_delete "remote" "$jj" "$FLAGS"
done
done done
} }
do_getmountedfs ()
{
local MOUNTED_TYPE="$1"
local MOUNTED_LIST
local remote_cmd=''
case "$MOUNTED_TYPE" in
(remote)
remote_cmd="$opt_sendtocmd"
PLATFORM="$PLATFORM_REM"
;;
(local)
remote_cmd=""
PLATFORM="$PLATFORM_LOC"
;;
esac
case "$PLATFORM" in
(Linux)
MOUNTED_LIST=( $(eval $remote_cmd cat /proc/mounts | grep zfs | awk -F' ' '{OFS="\t"}{print $1,$2}' ) )
;;
(Darwin)
MOUNTED_LIST=( $(eval $remote_cmd zfs mount | awk -F' ' '{OFS="\t"}{print $1,$2}') $(eval $remote_cmd mount -t zfs | grep @ | awk -F' ' '{OFS="\t"}{print $1,$3}') )
;;
esac
printf "%s\t%s\n" "${MOUNTED_LIST[@]}" | sort
}
do_createfs ()
{
local FS=(${1})
for ii in ${FS[@]}; do
print_log debug "checking: $opt_sendprefix/$ii"
if is_member "${ZFS_REMOTE_LIST[*]}" "$opt_sendprefix/$ii" -eq 0
then
print_log debug "creating: $opt_sendprefix/$ii"
if do_run "$opt_sendtocmd" "zfs create $opt_sendprefix/$ii"
then
CREATION_COUNT=$(( $CREATION_COUNT + 1 ))
CREATED_TARGETS=( ${CREATED_TARGETS[@]} "$opt_sendprefix/$ii" )
fi
fi
done
}
# main () # main ()
# { # {
GETOPT=$(getopt \ PLATFORM_LOC=`uname`
--longoptions=default-exclude,dry-run,fast,skip-scrub,recursive \ case "$PLATFORM_LOC" in
--longoptions=event:,keep:,label:,prefix:,sep: \ (Linux)
--longoptions=debug,help,quiet,syslog,verbose \ getopt_cmd='getopt'
--longoptions=pre-snapshot:,post-snapshot:,destroy-only \ ;;
--options=dnshe:l:k:p:rs:qgv \ (Darwin)
getopt_cmd='/opt/local/bin/getopt'
;;
(*)
print_log error "Local system not known ($PLATFORM_LOC) - needs one of Darwin, Linux. Exiting."
exit 300
;;
esac
GETOPT=$("$getopt_cmd" \
--longoptions=default-exclude,dry-run,skip-scrub,recursive,send-atonce \
--longoptions=event:,keep:,label:,prefix:,sep:,create,fallback,rollback \
--longoptions=debug,help,quiet,syslog,verbose,send-full:,send-incr:,remove-local:,destroy \
--options=dnshe:l:k:p:rs:qgvfixcXFRb \
-- "$@" ) \ -- "$@" ) \
|| exit 128 || exit 128
@ -227,7 +531,11 @@ do
opt_verbose='1' opt_verbose='1'
shift 1 shift 1
;; ;;
(--default-exclude) (-c|--create)
opt_create='1'
shift 1
;;
(-x|--default-exclude)
opt_default_exclude='1' opt_default_exclude='1'
shift 1 shift 1
;; ;;
@ -235,17 +543,13 @@ do
if [ "${#2}" -gt '1024' ] if [ "${#2}" -gt '1024' ]
then then
print_log error "The $1 parameter must be less than 1025 characters." print_log error "The $1 parameter must be less than 1025 characters."
exit 139 exit 239
elif [ "${#2}" -gt '0' ] elif [ "${#2}" -gt '0' ]
then then
opt_event="$2" opt_event="$2"
fi fi
shift 2 shift 2
;; ;;
(--fast)
opt_fast_zfs_list='1'
shift 1
;;
(-n|--dry-run) (-n|--dry-run)
opt_dry_run='1' opt_dry_run='1'
shift 1 shift 1
@ -262,7 +566,7 @@ do
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 129 exit 229
fi fi
opt_keep="$2" opt_keep="$2"
shift 2 shift 2
@ -278,7 +582,7 @@ do
case $opt_prefix in case $opt_prefix in
([![:alnum:]_.:\ -]*) ([![:alnum:]_.:\ -]*)
print_log error "The $1 parameter must be alphanumeric." print_log error "The $1 parameter must be alphanumeric."
exit 130 exit 230
;; ;;
esac esac
opt_prefix="${opt_prefix#?}" opt_prefix="${opt_prefix#?}"
@ -293,7 +597,20 @@ do
shift 1 shift 1
;; ;;
(-r|--recursive) (-r|--recursive)
opt_recursive='1' opt_recursive=' '
shift 1
;;
(-R|--replication)
opt_recursive='-R'
opt_force='-F'
shift 1
;;
(-X|--destroy)
opt_destroy='1'
shift 1
;;
(-F|--fallback)
opt_fallback='1'
shift 1 shift 1
;; ;;
(--sep) (--sep)
@ -303,35 +620,67 @@ do
;; ;;
('') ('')
print_log error "The $1 parameter must be non-empty." print_log error "The $1 parameter must be non-empty."
exit 131 exit 231
;; ;;
(*) (*)
print_log error "The $1 parameter must be one alphanumeric character." print_log error "The $1 parameter must be one alphanumeric character."
exit 132 exit 232
;; ;;
esac esac
opt_sep="$2" opt_sep="$2"
shift 2 shift 2
;; ;;
(--send-full)
if [ -n "$opt_sendprefix" ]; then
print_log error "Only one of --send-incr and --send-full must be specified."
exit 239
fi
if [ -z "$2" ]; then
print_log error "Target filesystem needs to be specified with --send-full."
exit 243
fi
opt_sendprefix="$2"
opt_send='full'
shift 2
;;
(--send-incr)
opt_sendincr="$2"
if [ -n "$opt_sendprefix" ]; then
print_log error "Only one of --send-incr and --send-full must be specified."
exit 240
fi
if [ -z "$2" ]; then
print_log error "Target filesystem needs to be specified with --send-incr."
exit 242
fi
opt_sendprefix="$2"
opt_send='incr'
shift 2
;;
(-g|--syslog) (-g|--syslog)
opt_syslog='1' opt_syslog='1'
shift 1 shift 1
;; ;;
(-i|--send-atonce)
opt_atonce='-i'
shift 1
;;
(--remove-local)
if ! test "$2" -gt '0' 2>/dev/null
then
print_log error "The $1 parameter must be a positive integer."
exit 241
fi
opt_remove="$2"
shift 2
;;
(-v|--verbose) (-v|--verbose)
opt_quiet='' opt_quiet=''
opt_verbose='1' opt_verbose='1'
shift 1 shift 1
;; ;;
(--pre-snapshot) (-f|--force|-b|--rollback)
opt_pre_snapshot="$2" opt_force='-F'
shift 2
;;
(--post-snapshot)
opt_post_snapshot="$2"
shift 2
;;
(--destroy-only)
opt_do_snapshots=''
shift 1 shift 1
;; ;;
(--) (--)
@ -347,6 +696,13 @@ then
exit 133 exit 133
fi fi
if [ -f "${tmp_file_prefix%%X*}"* ]; then
print_log error "another copy is running ..."
exit 99
fi
LOCKFILE=$(mktemp $tmp_file_prefix)
trap "rm -f '$LOCKFILE'; exit $?" INT TERM EXIT
# Count the number of times '//' appears on the command line. # Count the number of times '//' appears on the command line.
SLASHIES='0' SLASHIES='0'
for ii in "$@" for ii in "$@"
@ -368,17 +724,10 @@ ZPOOL_STATUS=$(env LC_ALL=C zpool status 2>&1 ) \
|| { print_log error "zpool status $?: $ZPOOL_STATUS"; exit 135; } || { 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",mountpoint,canmount,snapdir) \
|| { print_log error "zfs list $?: $ZFS_LIST"; exit 136; } || { print_log error "zfs list $?: $ZFS_LIST"; exit 136; }
if [ -n "$opt_fast_zfs_list" ] ZFS_LOCAL_LIST=($(echo "$ZFS_LIST" | awk -F'\t' '{ORS="\n"}{print $1}'))
then
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; }
else
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -S creation -o name) \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
fi
# Verify that each argument is a filesystem or volume. # Verify that each argument is a filesystem or volume.
for ii in "$@" for ii in "$@"
@ -406,28 +755,29 @@ ZPOOLS_NOTREADY=$(echo "$ZPOOL_STATUS" | awk -F ': ' \
$1 ~ /^ *state$/ && $2 !~ /ONLINE|DEGRADED/ { print pool } ' \ $1 ~ /^ *state$/ && $2 !~ /ONLINE|DEGRADED/ { print pool } ' \
| sort) | sort)
# Get a list of datasets for which snapshots are explicitly disabled. # Get a list of datasets for which snapshots are not explicitly disabled.
NOAUTO=$(echo "$ZFS_LIST" | awk -F '\t' \ CANDIDATES=$(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 datasets 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 datasets for which snapshots are explicitly enabled. # Get a list of datasets for which snapshots are not explicitly enabled.
CANDIDATES=$(echo "$ZFS_LIST" | awk -F '\t' \ NOAUTO=$(echo "$ZFS_LIST" | awk -F '\t' \
'tolower($2) ~ /true/ || tolower($3) ~ /true/ {print $1}') 'tolower($2) !~ /true/ && tolower($3) !~ /true/ {print $1}')
else else
# Invert the NOAUTO list. # Get a list of datasets for which snapshots are explicitly disabled.
CANDIDATES=$(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}')
fi fi
# Initialize the list of datasets that will get a recursive snapshot. # Initialize the list of datasets that will get a recursive snapshot.
TARGETS_RECURSIVE='' declare -a TARGETS_DRECURSIVE=('')
declare -a TARGETS_TMP_RECURSIVE=('')
# Initialize the list of datasets that will get a non-recursive snapshot. # Initialize the list of datasets that will get a non-recursive snapshot.
TARGETS_REGULAR='' declare -a TARGETS_DREGULAR=('')
for ii in $CANDIDATES for ii in $CANDIDATES
do do
@ -440,7 +790,7 @@ do
IN_ARGS='0' IN_ARGS='0'
for jj in "$@" for jj in "$@"
do do
if [ "$jj" = '//' -o "$jj" = "$ii" ] if [ "$jj" = '//' -o "$jj" = "$ii" -o -n "$opt_recursive" -a -z "${ii##$jj/*}" ]
then then
IN_ARGS=$(( $IN_ARGS + 1 )) IN_ARGS=$(( $IN_ARGS + 1 ))
fi fi
@ -478,29 +828,31 @@ do
fi fi
done done
for jj in $NOAUTO noauto_parent='0'
for jj in $NOAUTO
do do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# The --recursive switch only matters for non-wild arguments. if [ "$jjj" = "$iii" ]
if [ -z "$opt_recursive" -a "$1" != '//' ] then
then
# Snapshot this dataset non-recursively.
print_log debug "Including $ii for regular snapshot."
TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t
continue 2 continue 2
# Check whether the candidate name is a prefix of any excluded dataset 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 dataset non-recursively. noauto_parent='1' && break
print_log debug "Including $ii for regular snapshot." fi
TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t
continue 2
fi
done done
for jj in $TARGETS_RECURSIVE # not scrubbing
if [ -z "$opt_recursive" -a "$1" != '//' -o "$noauto_parent" = '1' ]
then
print_log debug "Including $ii for regular snapshot."
TARGETS_DREGULAR=( ${TARGETS_DREGULAR[@]} $( printf "%s\n" $ii))
continue
fi
for jj in ${TARGETS_TMP_RECURSIVE[@]}
do do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
@ -521,7 +873,8 @@ do
# * Is not the descendant of an already included filesystem. # * Is not the descendant of an already included filesystem.
# #
print_log debug "Including $ii for recursive snapshot." print_log debug "Including $ii for recursive snapshot."
TARGETS_RECURSIVE="${TARGETS_RECURSIVE:+$TARGETS_RECURSIVE }$ii" # nb: \t TARGETS_TMP_RECURSIVE=( ${TARGETS_TMP_RECURSIVE[@]} $( printf "%s\n" $ii) )
done 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
@ -530,46 +883,74 @@ SNAPPROP="-o com.sun:auto-snapshot-desc='$opt_event'"
# 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.
DATE=$(date --utc +%F-%H%M) DATE=$(date +%F-%H%M)
# The snapshot name after the @ symbol. # The snapshot name after the @ symbol.
SNAPNAME="$opt_prefix${opt_label:+$opt_sep$opt_label}-$DATE" SNAPNAME="$opt_prefix${opt_label:+$opt_sep$opt_label-$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_label:+?$opt_label}????????????????" SNAPGLOB="$opt_prefix${opt_label:+?$opt_label}????????????????"
if [ -n "$opt_do_snapshots" ] if test -n "$TARGETS_DREGULAR"; then
then print_log info "Doing regular snapshots of ${TARGETS_DREGULAR[@]}"
test -n "$TARGETS_REGULAR" \ SNAPSHOTS_OLD_LOC=( $(eval zfs list -r -d 1 -H -t snapshot -S creation -o name $(printf "%s " ${TARGETS_DREGULAR[@]}) ))
&& print_log info "Doing regular snapshots of $TARGETS_REGULAR" fi
test -n "$TARGETS_RECURSIVE" \ if test -n "$TARGETS_TMP_RECURSIVE"; then
&& print_log info "Doing recursive snapshots of $TARGETS_RECURSIVE" print_log info "Doing recursive snapshots of ${TARGETS_TMP_RECURSIVE[@]}"
SNAPSHOTS_OLD_LOC=( ${SNAPSHOTS_OLD_LOC[@]} $(eval zfs list -r -H -t snapshot -S creation -o name $(printf "%s " ${TARGETS_TMP_RECURSIVE[@]} ) )) \
if test -n "$opt_keep" && [ "$opt_keep" -ge "1" ] || { print_log error "zfs list $?: $SNAPSHOTS_OLD_LOC"; exit 137; }
then
print_log info "Destroying all but the newest $opt_keep snapshots of each dataset."
fi
elif test -n "$opt_keep" && [ "$opt_keep" -ge "1" ]
then
test -n "$TARGETS_REGULAR" \
&& print_log info "Destroying all but the newest $opt_keep snapshots of $TARGETS_REGULAR"
test -n "$TARGETS_RECURSIVE" \
&& print_log info "Recursively destroying all but the newest $opt_keep snapshots of $TARGETS_RECURSIVE"
else
print_log notice "Only destroying snapshots, but count of snapshots to preserve not given. Nothing to do."
fi 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" # expand FS list if replication is not used
do_snapshots "$SNAPPROP" "-r" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_RECURSIVE" if [ "$opt_recursive" = ' ' -o "$1" = "//" ]
then
for ii in ${TARGETS_TMP_RECURSIVE[@]}; do TARGETS_DRECURSIVE=( ${TARGETS_DRECURSIVE[@]} $(printf "$ii\n") $(printf "%s\n" ${ZFS_LOCAL_LIST[@]} | grep ^"$ii/") ); done
else
TARGETS_DRECURSIVE=( ${TARGETS_TMP_RECURSIVE[@]} )
fi
MOUNTED_LIST_LOC=$(eval do_getmountedfs "local")
# initialize remote system parameters, filesystems, mounts and snapshots
if [ "$opt_send" != "no" ]
then
PLATFORM_REM=$(eval "$opt_sendtocmd" "uname")
case "$PLATFORM_REM" in
(Linux|Darwin)
;;
(*)
print_log error "Remote system not known ($PLATFORM_REM) - needs one of Darwin, Linux. Exiting."
exit 301
;;
esac
MOUNTED_LIST_REM=$(eval do_getmountedfs "remote")
SNAPSHOTS_OLD_REM=($(eval "$opt_sendtocmd" zfs list -r -H -t snapshot -S creation -o name "$opt_sendprefix")) \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD_REM"; exit 140; }
ZFS_REMOTE_LIST=($(eval "$opt_sendtocmd" zfs list -H -t filesystem,volume -s name -o name)) \
|| { print_log error "$opt_sendtocmd zfs list $?: $ZFS_REMOTE_LIST"; exit 139; }
fi
if [ "$opt_create" -eq '1' -a "$opt_send" != "no" ]; then
do_createfs "${TARGETS_DREGULAR[*]}"
do_createfs "${TARGETS_DRECURSIVE[*]}"
fi
do_snapshots "$SNAPPROP" "" "$SNAPNAME" "$SNAPGLOB" "${TARGETS_DREGULAR[*]}"
do_snapshots "$SNAPPROP" "$opt_recursive" "$SNAPNAME" "$SNAPGLOB" "${TARGETS_DRECURSIVE[*]}"
print_log notice "@$SNAPNAME," \ print_log notice "@$SNAPNAME," \
"$SNAPSHOT_COUNT created," \ "$SNAPSHOT_COUNT created snapshots," \
"$SENT_COUNT sent snapshots," \
"$DESTRUCTION_COUNT destroyed," \ "$DESTRUCTION_COUNT destroyed," \
"$CREATION_COUNT created filesystems," \
"$WARNING_COUNT warnings." "$WARNING_COUNT warnings."
exit 0 exit 0