Periodic ZFS snapshots

As I continue exploring the nearly endless possibilities within ZFS, I figured it would be neat to have periodic snapshots of my mail files. The “Interweb” is filled with various shell scripts for this purpose, and I guess another one won’t make much difference.

#!/bin/sh
# Shell script for creating periodic ZFS snapshots.

# Copyright © 2012 Trond Endrestøl <Trond.Endrestol@ximalas.info>

# 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 Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.

# 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.

Here’s how the snapshots will appear by the use of the command zfs list -t all:

NAME                                  USED  AVAIL  REFER  MOUNTPOINT
enterprise_zroot/var/mail            1,91M   419G   184K  /var/mail
enterprise_zroot/var/mail@february    124K      -   172K  -
enterprise_zroot/var/mail@march          0      -   176K  -
enterprise_zroot/var/mail@thursday       0      -   176K  -
enterprise_zroot/var/mail@friday      132K      -   180K  -
enterprise_zroot/var/mail@saturday    124K      -   172K  -
enterprise_zroot/var/mail@sunday      136K      -   184K  -
enterprise_zroot/var/mail@monday      132K      -   184K  -
enterprise_zroot/var/mail@tuesday     124K      -   184K  -
enterprise_zroot/var/mail@hourly19       0      -   184K  -
enterprise_zroot/var/mail@hourly20       0      -   184K  -
enterprise_zroot/var/mail@hourly21       0      -   184K  -
enterprise_zroot/var/mail@hourly22       0      -   184K  -
enterprise_zroot/var/mail@hourly23     72K      -   184K  -
enterprise_zroot/var/mail@wednesday      0      -   184K  -
enterprise_zroot/var/mail@hourly01       0      -   184K  -
enterprise_zroot/var/mail@hourly02       0      -   184K  -
enterprise_zroot/var/mail@hourly03       0      -   184K  -
enterprise_zroot/var/mail@hourly04       0      -   192K  -
enterprise_zroot/var/mail@hourly05       0      -   192K  -
enterprise_zroot/var/mail@hourly06       0      -   192K  -
enterprise_zroot/var/mail@hourly07       0      -   184K  -
enterprise_zroot/var/mail@hourly08       0      -   184K  -
enterprise_zroot/var/mail@hourly09       0      -   184K  -
enterprise_zroot/var/mail@hourly10       0      -   184K  -
enterprise_zroot/var/mail@hourly11       0      -   184K  -
enterprise_zroot/var/mail@hourly12       0      -   184K  -
enterprise_zroot/var/mail@hourly13       0      -   220K  -
enterprise_zroot/var/mail@hourly14       0      -   220K  -
enterprise_zroot/var/mail@hourly15       0      -   220K  -
enterprise_zroot/var/mail@hourly16       0      -   220K  -
enterprise_zroot/var/mail@hourly17       0      -   220K  -
enterprise_zroot/var/mail@minute44       0      -   184K  -
enterprise_zroot/var/mail@minute45       0      -   184K  -
enterprise_zroot/var/mail@minute46       0      -   184K  -
enterprise_zroot/var/mail@minute47       0      -   184K  -
enterprise_zroot/var/mail@minute48       0      -   184K  -
enterprise_zroot/var/mail@minute49       0      -   184K  -
enterprise_zroot/var/mail@minute50       0      -   184K  -
enterprise_zroot/var/mail@minute51       0      -   184K  -
enterprise_zroot/var/mail@minute52       0      -   184K  -
enterprise_zroot/var/mail@minute53       0      -   184K  -
enterprise_zroot/var/mail@minute54       0      -   184K  -
enterprise_zroot/var/mail@minute55       0      -   184K  -
enterprise_zroot/var/mail@minute56       0      -   184K  -
enterprise_zroot/var/mail@minute57       0      -   184K  -
enterprise_zroot/var/mail@minute58       0      -   184K  -
enterprise_zroot/var/mail@minute59       0      -   184K  -
enterprise_zroot/var/mail@hourly18       0      -   184K  -
enterprise_zroot/var/mail@minute01       0      -   184K  -
enterprise_zroot/var/mail@minute02       0      -   184K  -
enterprise_zroot/var/mail@minute03       0      -   184K  -
enterprise_zroot/var/mail@minute04       0      -   184K  -
enterprise_zroot/var/mail@minute05       0      -   184K  -
enterprise_zroot/var/mail@minute06       0      -   184K  -
enterprise_zroot/var/mail@minute07       0      -   184K  -
enterprise_zroot/var/mail@minute08       0      -   184K  -
enterprise_zroot/var/mail@minute09       0      -   184K  -
enterprise_zroot/var/mail@minute10       0      -   184K  -
enterprise_zroot/var/mail@minute11       0      -   184K  -
enterprise_zroot/var/mail@minute12       0      -   184K  -
enterprise_zroot/var/mail@minute13       0      -   184K  -
enterprise_zroot/var/mail@minute14       0      -   184K  -
enterprise_zroot/var/mail@minute15       0      -   184K  -
enterprise_zroot/var/mail@minute16       0      -   184K  -
enterprise_zroot/var/mail@minute17       0      -   184K  -
enterprise_zroot/var/mail@minute18       0      -   184K  -
enterprise_zroot/var/mail@minute19       0      -   184K  -
enterprise_zroot/var/mail@minute20       0      -   184K  -
enterprise_zroot/var/mail@minute21       0      -   184K  -
enterprise_zroot/var/mail@minute22       0      -   184K  -
enterprise_zroot/var/mail@minute23       0      -   184K  -
enterprise_zroot/var/mail@minute24       0      -   184K  -
enterprise_zroot/var/mail@minute25       0      -   184K  -
enterprise_zroot/var/mail@minute26       0      -   184K  -
enterprise_zroot/var/mail@minute27       0      -   184K  -
enterprise_zroot/var/mail@minute28       0      -   184K  -
enterprise_zroot/var/mail@minute29       0      -   184K  -
enterprise_zroot/var/mail@minute30       0      -   184K  -
enterprise_zroot/var/mail@minute31       0      -   184K  -
enterprise_zroot/var/mail@minute32       0      -   184K  -
enterprise_zroot/var/mail@minute33       0      -   184K  -
enterprise_zroot/var/mail@minute34       0      -   184K  -
enterprise_zroot/var/mail@minute35       0      -   184K  -
enterprise_zroot/var/mail@minute36       0      -   184K  -
enterprise_zroot/var/mail@minute37       0      -   184K  -
enterprise_zroot/var/mail@minute38       0      -   184K  -
enterprise_zroot/var/mail@minute39       0      -   184K  -
enterprise_zroot/var/mail@minute40       0      -   184K  -
enterprise_zroot/var/mail@minute41       0      -   184K  -
enterprise_zroot/var/mail@minute42       0      -   184K  -
enterprise_zroot/var/mail@minute43       0      -   184K  -