#!/usr/bin/perl
#Automatic ZFS snapshot tool version 1.0
#Created by Dick Tump (http://www.cybje.nl/)
#
#Tool provided as-is, I cannot be held responsible for any data loss
#
#Commercial use allowed, sale of this tool (or receiving money in any way for
#distributing this tool) is not allowed.

### CONFIG
#Total number of snapshots to keep
my $keepsnapshots=3;

#Mounts you want to create snapshots for
my @mounts=(
	'magic/data',
	'magic/kvm',
	'magic/bin',
);

#Overwrite snapshots if they already exist? (0 = no, 1 = yes)
my $overwrite=1;

#ZFS binary
my $zfsbin="/sbin/zfs";
### /CONFIG

use strict;

if (!-e $zfsbin) {
	die "ZFS binary not found";
}

my @timearr=localtime();
my $curdate=1900+$timearr[5].sprintf("%02d",$timearr[4]+1).sprintf("%02d",$timearr[3]);

sub getsnaplist {
	chomp(my @zfssnapshotlist=`$zfsbin list -H -t snapshot`);
	if ($? != 0) {
		die "Error retrieving snapshot list, error code: $?";
	}
	my @snapshots;
	foreach(@zfssnapshotlist) {
		(my $snapshot,my $otherstuff)=split(/\t/, $_);
		push (@snapshots, $snapshot);
	}
	return @snapshots;
}

my @snapshots=&getsnaplist;

foreach my $m (@mounts) {
	my $newsnapshotname="$m\@auto_$curdate";
	my $createsnapshot=1;
	foreach my $s (@snapshots) {
		if ($s eq $newsnapshotname) {
			if ($overwrite==1) {
				system "$zfsbin destroy $newsnapshotname";
				if ($? != 0) {
					print "Cannot remove snapshot $newsnapshotname, to create a new one.\n";
					$createsnapshot=0;
				} else {
					print "Snapshot $newsnapshotname removed to create a new snapshot.\n";
				}
			} else {
				print "Snapshot $newsnapshotname already exists and overwrite is disabled. Not creating snapshot.\n";
				$createsnapshot=0;
			}
		}
	}

	if ($createsnapshot==1) {
		system "$zfsbin snapshot $newsnapshotname";
		if ($? != 0) {
			print "Error creating snapshot $newsnapshotname. Error code from ZFS: $?\n";
		} else {
			print "Snapshot $newsnapshotname created.\n";
		}
	}
}

#Get new snapshot list, sort descending to easily remove old snapshots
@snapshots=sort {$b cmp $a} &getsnaplist;
foreach my $m (@mounts) {
	my $snapcount=0;
	foreach my $s (@snapshots) {
		if ($s =~ /^$m\@auto\_\d{8}$/) {
			$snapcount++;

			if ($snapcount>$keepsnapshots) {
				system "$zfsbin destroy $s";

				if ($?!=0) {
					print "Error: cannot remove ${snapcount}th snapshot $m. Error code from ZFS: $?\n";
				} else {
					print "Removed ${snapcount}th snapshot $s\n";
				}
			}
		}
	}
}

print "Done.\n";

