Periodic ZFS snapshots – 2-clause BSD license
Someone found my old ZFS snapshot script rather useful and wanted to improve it. Unfortunately I used the GPLv2 license in the original script. To accommodate my friend, I have re-released the snapshot script under the 2-clause BSD license. Feel free to use whichever license, 2-clause BSD or GPLv2, that suits your needs.
#!/bin/sh
# Shell script for creating periodic ZFS snapshots.
# Copyright © 2012, Trond Endrestøl <Trond.Endrestol@ximalas.info>
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This script creates a monthly snapshot at midnight on the first day of every month.
# Should the snapshot exist, the old snapshot will be removed and recreated.
# Next, this script will create a daily snapshot at midnight,
# and hourly snapshots for the hours 01 to 23,
# and finally minutely snapshots for the minutes 01 to 59.
# Should the snapshot exist, the old snapshot will be removed and recreated.
# This script is supposed to be run by cron every minute.
# Example of invocation of this script from the system /etc/crontab:
# * * * * * root /path/to/create-periodic-zfs-snapshots.sh zpool/filesystem
# Example of invocation of this script from a user's crontab:
# * * * * * /path/to/create-periodic-zfs-snapshots.sh zpool/filesystem
# Tested on FreeBSD/amd64 9.0-STABLE.
# A suitable PATH is nice to have, though most of the commands are given using absolute file names.
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH
# Make sure we have been given one, and only one, file system on the command line.
if [ "${#}" -ne 1 ]; then
echo "Usage: ${0} zpool[/dataset]" >/dev/stderr;
exit 1;
fi
# Make sure the file system exist.
if ! /sbin/zfs list -H -t filesystem ${1} >/dev/null 2>/dev/null; then
echo "${0}: cannot find the file system ${1}" >/dev/stderr;
exit 1;
fi
# I prefer to use English names on months and weekdays.
export LANG=C
export LC_ALL=${LANG}
export LC_COLLATE=${LANG}
export LC_CTYPE=${LANG}
export LC_MESSAGES=${LANG}
export LC_MONETARY=${LANG}
export LC_NUMERIC=${LANG}
export LC_TIME=${LANG}
# Extract the necessary details on the present.
month=`/bin/date +%B | /usr/bin/tr "[:upper:]" "[:lower:]"`
weekday=`/bin/date +%A | /usr/bin/tr "[:upper:]" "[:lower:]"`
day=`/bin/date +%d`
hour=`/bin/date +%H`
minute=`/bin/date +%M`
# The monthly snapshot will ALWAYS be created when appropriate.
if [ ${day} -eq 1 -a ${hour} -eq 0 -a ${minute} -eq 0 ]; then
snapshot="${1}@${month}";
# Destroy the monthly snapshot, if it exist.
if /sbin/zfs list -H -t snapshot ${snapshot} >/dev/null 2>/dev/null; then
/sbin/zfs destroy ${snapshot};
zfs_destroy=${?};
if [ ${zfs_destroy} -ne 0 ]; then
exit ${zfs_destroy};
fi;
fi;
# Create the monthly snapshot.
/sbin/zfs snapshot ${snapshot};
zfs_snapshot=${?};
if [ ${zfs_snapshot} -ne 0 ]; then
exit ${zfs_snapshot};
fi;
fi
# Pick weekday, hour, or minute.
if [ ${hour} -eq 0 -a ${minute} -eq 0 ]; then
snapshot="${1}@${weekday}";
elif [ ${minute} -eq 0 ]; then
snapshot="${1}@hourly${hour}";
else
snapshot="${1}@minute${minute}";
fi
# Destroy the snapshot, if it exist.
if /sbin/zfs list -H -t snapshot ${snapshot} >/dev/null 2>/dev/null; then
/sbin/zfs destroy ${snapshot};
zfs_destroy=${?};
if [ ${zfs_destroy} -ne 0 ]; then
exit ${zfs_destroy};
fi;
fi
# Create the snapshot.
/sbin/zfs snapshot ${snapshot}
exit ${?}
# EOF of create-periodic-zfs-snapshots.sh.