From 3d9b3b04badbce3074c279eb34aab97977df47b1 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Thu, 4 Jan 2018 12:20:37 +0100 Subject: [PATCH 01/31] Added "force-prune" option to skip busy dataset check --- sanoid | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sanoid b/sanoid index d6e58ce..029d846 100755 --- a/sanoid +++ b/sanoid @@ -18,7 +18,7 @@ use Time::Local; # to parse dates in reverse my %args = ("configdir" => "/etc/sanoid"); GetOptions(\%args, "verbose", "debug", "cron", "readonly", "quiet", "monitor-health", "force-update", "configdir=s", - "monitor-snapshots", "take-snapshots", "prune-snapshots" + "monitor-snapshots", "take-snapshots", "prune-snapshots", "force-prune" ) or pod2usage(2); # If only config directory (or nothing) has been specified, default to --cron --verbose @@ -234,7 +234,7 @@ sub prune_snapshots { writelock('sanoid_pruning'); foreach my $snap( @prunesnaps ){ if ($args{'verbose'}) { print "INFO: pruning $snap ... \n"; } - if (iszfsbusy($path)) { + if (iszfsbusy($path) && !$args{'force-prune'}) { print "INFO: deferring pruning of $snap - $path is currently in zfs send or receive.\n"; } else { if (! $args{'readonly'}) { system($zfs, "destroy",$snap) == 0 or warn "could not remove $snap : $?"; } @@ -1082,6 +1082,7 @@ Options: --monitor-snapshots Reports on snapshot "health", in a Nagios compatible format --take-snapshots Creates snapshots as specified in sanoid.conf --prune-snapshots Purges expired snapshots as specified in sanoid.conf + --force-prune Purges expired snapshots even if a send/recv is in progress --help Prints this helptext --version Prints the version number From f670631ca281961c318b0b9fb56ca28017e5de38 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Mon, 30 Apr 2018 21:50:17 +0200 Subject: [PATCH 02/31] Added "no-clone-rollback" option to prevent clone rollback on target host --- syncoid | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/syncoid b/syncoid index 7337f5b..e91c126 100755 --- a/syncoid +++ b/syncoid @@ -19,7 +19,7 @@ use Sys::Hostname; my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => ''); GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@", - "debug", "quiet", "no-stream", "no-sync-snap") or pod2usage(2); + "debug", "quiet", "no-stream", "no-sync-snap", "no-clone-rollback") or pod2usage(2); my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set @@ -306,13 +306,17 @@ sub syncdataset { if (!$quiet) { print "INFO: no snapshots on source newer than $newsyncsnap on target. Nothing to do, not syncing.\n"; } } else { # rollback target to matchingsnap + my $rollbacktype="-R"; + if (defined $args{'no-clone-rollback'}) { + $rollbacktype = "-r"; + } if ($debug) { print "DEBUG: rolling back target to $targetfs\@$matchingsnap...\n"; } if ($targethost ne '') { - if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback -R $targetfs\@$matchingsnap\n"; } - system ("$sshcmd $targethost $targetsudocmd $zfscmd rollback -R $targetfs\@$matchingsnap"); + if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap\n"; } + system ("$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap"); } else { - if ($debug) { print "$targetsudocmd $zfscmd rollback -R $targetfs\@$matchingsnap\n"; } - system ("$targetsudocmd $zfscmd rollback -R $targetfs\@$matchingsnap"); + if ($debug) { print "$targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap\n"; } + system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap"); } my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefs\@$matchingsnap $sourcefs\@$newsyncsnap"; @@ -949,6 +953,7 @@ Options: --target-bwlimit= Bandwidth limit on the target transfer --no-stream Replicates using newest snapshot instead of intermediates --no-sync-snap Does not create new snapshot, only transfers existing + --no-clone-rollback Does not rollback clones on target --sshkey=FILE Specifies a ssh public key to use to connect --sshport=PORT Connects to remote on a particular port From 670e76458c0dff00323aa4ceff67b16cb2fc1841 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Mon, 30 Apr 2018 22:45:05 +0200 Subject: [PATCH 03/31] Reintroduce "no-resume", erroneously removed from previous commit --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index 512e153..c6c85ed 100755 --- a/syncoid +++ b/syncoid @@ -19,7 +19,7 @@ use Sys::Hostname; my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => ''); GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@", - "debug", "quiet", "no-stream", "no-sync-snap", "no-clone-rollback") or pod2usage(2); + "debug", "quiet", "no-stream", "no-sync-snap", "no-clone-rollback", "no-resume") or pod2usage(2); my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set From 793f1c782b703a9050f95a06eb8d6847af58f64e Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Tue, 1 May 2018 09:32:18 +0200 Subject: [PATCH 04/31] Reintroduced escapeshellparam for remote command and updated README file --- README.md | 8 ++++++++ syncoid | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cc75bdf..7a9b76c 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,10 @@ Which would be enough to tell sanoid to take and keep 36 hourly snapshots, 30 da This will process your sanoid.conf file, it will NOT create snapshots, but it will purge expired ones. ++ --force-prune + + Purges expired snapshots even if a send/recv is in progress + + --monitor-snapshots This option is designed to be run by a Nagios monitoring system. It reports on the health of your snapshots. @@ -150,6 +154,10 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup This argument tells syncoid to restrict itself to existing snapshots, instead of creating a semi-ephemeral syncoid snapshot at execution time. Especially useful in multi-target (A->B, A->C) replication schemes, where you might otherwise accumulate a large number of foreign syncoid snapshots. ++ --no-clone-rollback + + Does not rollback clones on target + + --no-resume This argument tells syncoid to not use resumeable zfs send/receive streams. diff --git a/syncoid b/syncoid index c6c85ed..0a2fe4a 100755 --- a/syncoid +++ b/syncoid @@ -366,7 +366,7 @@ sub syncdataset { if ($debug) { print "DEBUG: rolling back target to $targetfs\@$matchingsnap...\n"; } if ($targethost ne '') { if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap\n"; } - system ("$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap"); + system ("$sshcmd $targethost " . escapeshellparam("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped")); } else { if ($debug) { print "$targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap\n"; } system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap"); From 679f1f3bda6feec0392eada8ae65072ac0393627 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Tue, 1 May 2018 09:39:35 +0200 Subject: [PATCH 05/31] Reintroduced targetfsescaped --- syncoid | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/syncoid b/syncoid index 0a2fe4a..49e3aac 100755 --- a/syncoid +++ b/syncoid @@ -359,19 +359,18 @@ sub syncdataset { } else { my $matchingsnapescaped = escapeshellparam($matchingsnap); # rollback target to matchingsnap - my $rollbacktype="-R"; + my $rollbacktype = "-R"; if (defined $args{'no-clone-rollback'}) { $rollbacktype = "-r"; } if ($debug) { print "DEBUG: rolling back target to $targetfs\@$matchingsnap...\n"; } if ($targethost ne '') { - if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap\n"; } + if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped\n"; } system ("$sshcmd $targethost " . escapeshellparam("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped")); } else { - if ($debug) { print "$targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap\n"; } - system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap"); + if ($debug) { print "$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped\n"; } + system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped"); } - my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped"; my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped"; my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot); From f9049085c8b9d7ff4800c44d4cd286bd67337107 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Mon, 11 Jun 2018 09:48:45 +0200 Subject: [PATCH 06/31] Added "no-rollback" option to prevent any rollback on target host --- syncoid | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/syncoid b/syncoid index 49e3aac..9dc83fa 100755 --- a/syncoid +++ b/syncoid @@ -19,7 +19,7 @@ use Sys::Hostname; my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => ''); GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@", - "debug", "quiet", "no-stream", "no-sync-snap", "no-clone-rollback", "no-resume") or pod2usage(2); + "debug", "quiet", "no-stream", "no-sync-snap", "no-clone-rollback", "no-rollback", "no-resume") or pod2usage(2); my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set @@ -151,6 +151,12 @@ sub syncdataset { my $sourcefsescaped = escapeshellparam($sourcefs); my $targetfsescaped = escapeshellparam($targetfs); + # if no rollbacks are allowed, disable forced receive + my $forcedrecv = "-F"; + if (defined $args{'no-rollback'}) { + $forcedrecv = ""; + } + if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } # make sure target is not currently in receive. @@ -244,7 +250,7 @@ sub syncdataset { my $oldestsnapescaped = escapeshellparam($oldestsnap); my $sendcmd = "$sourcesudocmd $zfscmd send $sourcefsescaped\@$oldestsnapescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; my $pvsize = getsendsize($sourcehost,"$sourcefs\@$oldestsnap",0,$sourceisroot); my $disp_pvsize = readablebytes($pvsize); @@ -313,7 +319,7 @@ sub syncdataset { # snapshot, do a normal sync after that if (defined($receivetoken)) { my $sendcmd = "$sourcesudocmd $zfscmd send -t $receivetoken"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; my $pvsize = getsendsize($sourcehost,"","",$sourceisroot,$receivetoken); my $disp_pvsize = readablebytes($pvsize); if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; } @@ -359,20 +365,22 @@ sub syncdataset { } else { my $matchingsnapescaped = escapeshellparam($matchingsnap); # rollback target to matchingsnap - my $rollbacktype = "-R"; - if (defined $args{'no-clone-rollback'}) { - $rollbacktype = "-r"; - } - if ($debug) { print "DEBUG: rolling back target to $targetfs\@$matchingsnap...\n"; } - if ($targethost ne '') { - if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped\n"; } - system ("$sshcmd $targethost " . escapeshellparam("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped")); - } else { - if ($debug) { print "$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped\n"; } - system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped"); + if (!defined $args{'no-rollback'}) { + my $rollbacktype = "-R"; + if (defined $args{'no-clone-rollback'}) { + $rollbacktype = "-r"; + } + if ($debug) { print "DEBUG: rolling back target to $targetfs\@$matchingsnap...\n"; } + if ($targethost ne '') { + if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped\n"; } + system ("$sshcmd $targethost " . escapeshellparam("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped")); + } else { + if ($debug) { print "$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped\n"; } + system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped"); + } } my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot); my $disp_pvsize = readablebytes($pvsize); if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; } @@ -1143,6 +1151,7 @@ Options: --no-stream Replicates using newest snapshot instead of intermediates --no-sync-snap Does not create new snapshot, only transfers existing --no-clone-rollback Does not rollback clones on target + --no-rollback Does not rollback clones or snapshots on target (it probably requires a readonly target) --sshkey=FILE Specifies a ssh public key to use to connect --sshport=PORT Connects to remote on a particular port From 4ed6ff0e447f436fb3b7a77405050839927e61a6 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Mon, 11 Jun 2018 10:06:36 +0200 Subject: [PATCH 07/31] Updated README file --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a9b76c..58ec7dd 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,10 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup + --no-clone-rollback - Does not rollback clones on target + Do not rollback clones on target + ++ --no-rollback + Do not rollback anything (clones or snapshots) on target host + --no-resume From 0aaac4205790e1e179d41140ba38142cf442389c Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Mon, 11 Jun 2018 10:16:18 +0200 Subject: [PATCH 08/31] skip pruning with --no-sync-snap (as by PR #218) --- syncoid | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/syncoid b/syncoid index 9dc83fa..ae31131 100755 --- a/syncoid +++ b/syncoid @@ -398,9 +398,11 @@ sub syncdataset { } } - # prune obsolete sync snaps on source and target. - pruneoldsyncsnaps($sourcehost,$sourcefs,$newsyncsnap,$sourceisroot,keys %{ $snaps{'source'}}); - pruneoldsyncsnaps($targethost,$targetfs,$newsyncsnap,$targetisroot,keys %{ $snaps{'target'}}); + if (!defined $args{'no-sync-snap'}) { + # prune obsolete sync snaps on source and target (only if this run created ones). + pruneoldsyncsnaps($sourcehost,$sourcefs,$newsyncsnap,$sourceisroot,keys %{ $snaps{'source'}}); + pruneoldsyncsnaps($targethost,$targetfs,$newsyncsnap,$targetisroot,keys %{ $snaps{'target'}}); + } } # end syncdataset() From add7bf6de769e32a35f200c87143f4dfcbc126dc Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Tue, 19 Jun 2018 19:08:30 +0200 Subject: [PATCH 09/31] Added a missing newline in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 58ec7dd..4215ee4 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup Do not rollback clones on target + --no-rollback + Do not rollback anything (clones or snapshots) on target host + --no-resume From 108fe5e2fc1d31a1f74e3dfa0448814de3af6ee3 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Wed, 26 Sep 2018 14:02:19 +0200 Subject: [PATCH 10/31] Reversed zfsisbusy and force-prune checks --- sanoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanoid b/sanoid index fcbb630..32651ab 100755 --- a/sanoid +++ b/sanoid @@ -293,7 +293,7 @@ sub prune_snapshots { writelock('sanoid_pruning'); foreach my $snap( @prunesnaps ){ if ($args{'verbose'}) { print "INFO: pruning $snap ... \n"; } - if (iszfsbusy($path) && !$args{'force-prune'}) { + if (!$args{'force-prune'} && iszfsbusy($path)) { if ($args{'verbose'}) { print "INFO: deferring pruning of $snap - $path is currently in zfs send or receive.\n"; } } else { if (! $args{'readonly'}) { From f04be06f392911a2044d893065ca52b3e7cf99ea Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Wed, 26 Sep 2018 14:24:38 +0200 Subject: [PATCH 11/31] Fixed indentation --- sanoid | 2 +- syncoid | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sanoid b/sanoid index 32651ab..7a5fdee 100755 --- a/sanoid +++ b/sanoid @@ -1323,7 +1323,7 @@ Options: --monitor-snapshots Reports on snapshot "health", in a Nagios compatible format --take-snapshots Creates snapshots as specified in sanoid.conf --prune-snapshots Purges expired snapshots as specified in sanoid.conf - --force-prune Purges expired snapshots even if a send/recv is in progress + --force-prune Purges expired snapshots even if a send/recv is in progress --help Prints this helptext --version Prints the version number diff --git a/syncoid b/syncoid index f2335dc..8e67f5e 100755 --- a/syncoid +++ b/syncoid @@ -1246,7 +1246,7 @@ Options: --target-bwlimit= Bandwidth limit on the target transfer --no-stream Replicates using newest snapshot instead of intermediates --no-sync-snap Does not create new snapshot, only transfers existing - --no-clone-rollback Does not rollback clones on target + --no-clone-rollback Does not rollback clones on target --no-rollback Does not rollback clones or snapshots on target (it probably requires a readonly target) --exclude=REGEX Exclude specific datasets which match the given regular expression. Can be specified multiple times --sshkey=FILE Specifies a ssh public key to use to connect From 1ed37e9891400755311429a4a21e5102c87cd393 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Thu, 6 Dec 2018 09:26:22 +0100 Subject: [PATCH 12/31] Resolve a conflict --- syncoid | 9 --------- 1 file changed, 9 deletions(-) diff --git a/syncoid b/syncoid index 3ad1622..1bb4c80 100755 --- a/syncoid +++ b/syncoid @@ -583,14 +583,6 @@ sub syncdataset { system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped"); } } -<<<<<<< HEAD - my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; - my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot); - my $disp_pvsize = readablebytes($pvsize); - if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; } - my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot); -======= my $nextsnapshot = 0; @@ -608,7 +600,6 @@ sub syncdataset { } } } ->>>>>>> e186f3c66e9c757fa62c4eaa8a1c05bc49dbcff1 # bookmark stream size can't be determined my $pvsize = 0; From 210e0aae640481cc846609a9947c66d7a5b2b82f Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Fri, 7 Dec 2018 17:38:45 +0100 Subject: [PATCH 13/31] Introduced (un)forced recv when using bookmarks also --- syncoid | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncoid b/syncoid index 1bb4c80..f599f96 100755 --- a/syncoid +++ b/syncoid @@ -609,7 +609,7 @@ sub syncdataset { my $nextsnapshotescaped = escapeshellparam($nextsnapshot); my $sendcmd = "$sourcesudocmd $zfscmd send -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$nextsnapshotescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot); if (!$quiet) { print "Sending incremental $sourcefs#$bookmarkescaped ... $nextsnapshot (~ $disp_pvsize):\n"; } @@ -624,7 +624,7 @@ sub syncdataset { $matchingsnapescaped = escapeshellparam($matchingsnap); } else { my $sendcmd = "$sourcesudocmd $zfscmd send -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$newsyncsnapescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot); if (!$quiet) { print "Sending incremental $sourcefs#$bookmarkescaped ... $newsyncsnap (~ $disp_pvsize):\n"; } @@ -641,7 +641,7 @@ sub syncdataset { # bookmark replication was only done to the next oldest snapshot if (!$bookmark || $nextsnapshot) { my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot); my $disp_pvsize = readablebytes($pvsize); if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; } From 8568ac3e1030751ce5726bd4950c3e7e20f4b8f2 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Mon, 10 Dec 2018 11:21:43 +0100 Subject: [PATCH 14/31] Fix missing coma --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index f599f96..d3e678f 100755 --- a/syncoid +++ b/syncoid @@ -20,7 +20,7 @@ my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [ GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@", "debug", "quiet", "no-stream", "no-sync-snap", "no-resume", "exclude=s@", "skip-parent", "identifier=s", - "no-clone-handling", "no-privilege-elevation", "force-delete" "no-clone-rollback", "no-rollback") or pod2usage(2); + "no-clone-handling", "no-privilege-elevation", "force-delete", "no-clone-rollback", "no-rollback") or pod2usage(2); my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set From dc4df15e2ee8988449788861b7bd8f0260ea94f3 Mon Sep 17 00:00:00 2001 From: Jim Salter Date: Fri, 14 Dec 2018 10:44:45 -0500 Subject: [PATCH 15/31] fix broken monthly_warn monthly_crit in sanoid.defaults.conf --- sanoid.defaults.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sanoid.defaults.conf b/sanoid.defaults.conf index 4139393..96be95c 100644 --- a/sanoid.defaults.conf +++ b/sanoid.defaults.conf @@ -100,8 +100,8 @@ daily_warn = 28 daily_crit = 32 weekly_warn = 0 weekly_crit = 0 -monthly_warn = 5 -monthly_crit = 6 +monthly_warn = 32 +monthly_crit = 40 yearly_warn = 0 yearly_crit = 0 From 2ece13eccf0bd4f5ce0aaf15c2bfdffed37b9d41 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Sun, 16 Dec 2018 21:54:43 +0100 Subject: [PATCH 16/31] allow time units to be used for monitoring warn/crit values --- sanoid | 48 ++++++++++++++++++++++++++++++++++++++++---- sanoid.defaults.conf | 12 +++++------ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/sanoid b/sanoid index ddd457f..3aa57a3 100755 --- a/sanoid +++ b/sanoid @@ -143,8 +143,8 @@ sub monitor_snapshots { my $typewarn = $type . '_warn'; my $typecrit = $type . '_crit'; - my $warn = $config{$section}{$typewarn} * $smallerperiod; - my $crit = $config{$section}{$typecrit} * $smallerperiod; + my $warn = convertTimePeriod($config{$section}{$typewarn}, $smallerperiod); + my $crit = convertTimePeriod($config{$section}{$typecrit}, $smallerperiod); my $elapsed = -1; if (defined $snapsbytype{$path}{$type}{'newest'}) { $elapsed = $snapsbytype{$path}{$type}{'newest'}; @@ -153,7 +153,7 @@ sub monitor_snapshots { my $dispwarn = displaytime($warn); my $dispcrit = displaytime($crit); if ( $elapsed > $crit || $elapsed == -1) { - if ($config{$section}{$typecrit} > 0) { + if ($crit > 0) { if (! $config{$section}{'monitor_dont_crit'}) { $errorlevel = 2; } if ($elapsed == -1) { push @msgs, "CRIT: $path has no $type snapshots at all!"; @@ -162,7 +162,7 @@ sub monitor_snapshots { } } } elsif ($elapsed > $warn) { - if ($config{$section}{$typewarn} > 0) { + if ($warn > 0) { if (! $config{$section}{'monitor_dont_warn'} && ($errorlevel < 2) ) { $errorlevel = 1; } push @msgs, "WARN: $path\'s newest $type snapshot is $dispelapsed old (should be < $dispwarn)"; } @@ -1511,6 +1511,46 @@ sub runscript { return $ret; } +#######################################################################################################################3 +#######################################################################################################################3 +#######################################################################################################################3 + +sub convertTimePeriod { + my $value=shift; + my $period=shift; + + if ($value =~ /^\d+Y$/) { + $period = 60*60*24*31*365; + chop $value; + } elsif ($value =~ /^\d+M$/) { + $period = 60*60*24*31; + chop $value; + } elsif ($value =~ /^\d+W$/) { + $period = 60*60*24*7; + chop $value; + } elsif ($value =~ /^\d+D$/) { + $period = 60*60*24; + chop $value; + } elsif ($value =~ /^\d+h$/) { + $period = 60*60; + chop $value; + } elsif ($value =~ /^\d+m$/) { + $period = 60; + chop $value; + } elsif ($value =~ /^\d+s$/) { + $period = 1; + chop $value; + } elsif ($value =~ /^\d+$/) { + # no unit, provided fallback period is used + } else { + # invalid value, return smallest valid value as fallback + # (will trigger a warning message for monitoring for sure) + return 1; + } + + return $value * $period; +} + __END__ =head1 NAME diff --git a/sanoid.defaults.conf b/sanoid.defaults.conf index 96be95c..8785e7c 100644 --- a/sanoid.defaults.conf +++ b/sanoid.defaults.conf @@ -94,14 +94,14 @@ monitor_dont_warn = no monitor_dont_crit = no frequently_warn = 0 frequently_crit = 0 -hourly_warn = 90 -hourly_crit = 360 -daily_warn = 28 -daily_crit = 32 +hourly_warn = 90m +hourly_crit = 360m +daily_warn = 28h +daily_crit = 32h weekly_warn = 0 weekly_crit = 0 -monthly_warn = 32 -monthly_crit = 40 +monthly_warn = 32D +monthly_crit = 40D yearly_warn = 0 yearly_crit = 0 From cfab4eafdf2e8512f787db9e854835872493533f Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Sun, 16 Dec 2018 22:02:14 +0100 Subject: [PATCH 17/31] added/fixed documentation --- sanoid.defaults.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sanoid.defaults.conf b/sanoid.defaults.conf index 8785e7c..6649c2e 100644 --- a/sanoid.defaults.conf +++ b/sanoid.defaults.conf @@ -83,7 +83,8 @@ yearly_min = 0 # monitoring plugin - define warn / crit levels for each snapshot type by age, in units of one period down # example hourly_warn = 90 means issue WARNING if most recent hourly snapshot is not less than 90 minutes old, # daily_crit = 36 means issue CRITICAL if most recent daily snapshot is not less than 36 hours old, -# monthly_warn = 36 means issue WARNING if most recent monthly snapshot is not less than 36 days old... etc. +# monthly_warn = 5 means issue WARNING if most recent monthly snapshot is not less than 5 weeks old... etc. +# the following time suffixes can also be used: Y = years, M = months, W = weeks, D = days, h = hours, m = minutes, s = seconds # # monitor_dont_warn = yes will cause the monitoring service to report warnings as text, but with status OK. # monitor_dont_crit = yes will cause the monitoring service to report criticals as text, but with status OK. From ac80a753157dc26005c976675336f1e3fe3e1ad9 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Wed, 19 Dec 2018 00:36:10 +0100 Subject: [PATCH 18/31] made time units case insensitive and removed monthlies --- sanoid | 15 ++++++--------- sanoid.defaults.conf | 7 ++++--- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/sanoid b/sanoid index 3aa57a3..ce8207c 100755 --- a/sanoid +++ b/sanoid @@ -1519,25 +1519,22 @@ sub convertTimePeriod { my $value=shift; my $period=shift; - if ($value =~ /^\d+Y$/) { + if ($value =~ /^\d+[yY]$/) { $period = 60*60*24*31*365; chop $value; - } elsif ($value =~ /^\d+M$/) { - $period = 60*60*24*31; - chop $value; - } elsif ($value =~ /^\d+W$/) { + } elsif ($value =~ /^\d+[wW]$/) { $period = 60*60*24*7; chop $value; - } elsif ($value =~ /^\d+D$/) { + } elsif ($value =~ /^\d+[dD]$/) { $period = 60*60*24; chop $value; - } elsif ($value =~ /^\d+h$/) { + } elsif ($value =~ /^\d+[hH]$/) { $period = 60*60; chop $value; - } elsif ($value =~ /^\d+m$/) { + } elsif ($value =~ /^\d+[mM]$/) { $period = 60; chop $value; - } elsif ($value =~ /^\d+s$/) { + } elsif ($value =~ /^\d+[sS]$/) { $period = 1; chop $value; } elsif ($value =~ /^\d+$/) { diff --git a/sanoid.defaults.conf b/sanoid.defaults.conf index 6649c2e..a9ca382 100644 --- a/sanoid.defaults.conf +++ b/sanoid.defaults.conf @@ -84,7 +84,8 @@ yearly_min = 0 # example hourly_warn = 90 means issue WARNING if most recent hourly snapshot is not less than 90 minutes old, # daily_crit = 36 means issue CRITICAL if most recent daily snapshot is not less than 36 hours old, # monthly_warn = 5 means issue WARNING if most recent monthly snapshot is not less than 5 weeks old... etc. -# the following time suffixes can also be used: Y = years, M = months, W = weeks, D = days, h = hours, m = minutes, s = seconds +# the following time case insensitive suffixes can also be used: +# y = years, w = weeks, d = days, h = hours, m = minutes, s = seconds # # monitor_dont_warn = yes will cause the monitoring service to report warnings as text, but with status OK. # monitor_dont_crit = yes will cause the monitoring service to report criticals as text, but with status OK. @@ -101,8 +102,8 @@ daily_warn = 28h daily_crit = 32h weekly_warn = 0 weekly_crit = 0 -monthly_warn = 32D -monthly_crit = 40D +monthly_warn = 32d +monthly_crit = 40d yearly_warn = 0 yearly_crit = 0 From 8d88c4743e9e0a5bcb66a2530f9c2b1950f3fc65 Mon Sep 17 00:00:00 2001 From: Ben Yanke Date: Wed, 3 Oct 2018 16:20:58 -0500 Subject: [PATCH 19/31] improved documentation on --no-command-checks --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b833dec..99d8db1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ Sanoid is a policy-driven snapshot management tool for ZFS filesystems. When combined with the Linux KVM hypervisor, you can use it to make your systems functionally immortal. -

sanoid rollback demo
(Real time demo: rolling back a full-scale cryptomalware infection in seconds!)

+

sanoid rollback demo
(Real time demo: rolling back a full-scale cryptomalware infection in seconds!)

More prosaically, you can use Sanoid to create, automatically thin, and monitor snapshots and pool health from a single eminently human-readable TOML config file at /etc/sanoid/sanoid.conf. (Sanoid also requires a "defaults" file located at /etc/sanoid/sanoid.defaults.conf, which is not user-editable.) A typical Sanoid system would have a single cron job: ``` @@ -180,7 +181,7 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup + --no-command-checks - Do not check the existance of commands before attempting the transfer. It assumes all programs are available. This should never be used. + Does not check the existence of commands before attempting the transfer, providing administrators a way to run the tool with minimal overhead and maximum speed, at risk of potentially failed replication, or other possible edge cases. It assumes all programs are available, and should not be used in most situations. This is an not an officially supported run mode. + --no-stream From 1605a60c624b586243afa6a5d803b746a83b3b17 Mon Sep 17 00:00:00 2001 From: Sam Allred Date: Tue, 19 Dec 2017 15:47:40 -0700 Subject: [PATCH 20/31] CLIMATE-1151: added config option to use zfs recursion --- sanoid | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sanoid b/sanoid index ce8207c..30363c5 100755 --- a/sanoid +++ b/sanoid @@ -502,7 +502,14 @@ sub take_snapshots { # update to most current possible datestamp %datestamp = get_date(); # print "we should have had a $type snapshot of $path $maxage seconds ago; most recent is $newestage seconds old.\n"; - push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}${dateSuffix}_$type"); + + # use zfs recursion if specified in config + if ($config{$section}{'zfs_recursion'}) { + push(@newsnaps, "-r $path\@autosnap_$datestamp{'sortable'}_$type"); + + }else{ + push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}_$type"); + } } } } @@ -530,7 +537,7 @@ sub take_snapshots { } if ($args{'verbose'}) { print "taking snapshot $snap\n"; } if (!$args{'readonly'}) { - system($zfs, "snapshot", "$snap") == 0 + system("$zfs snapshot $snap") == 0 or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?"; } if ($config{$dataset}{'post_snapshot_script'} and !$args{'readonly'}) { @@ -846,7 +853,10 @@ sub init { my $recursive = $ini{$section}{'recursive'} && grep( /^$ini{$section}{'recursive'}$/, @istrue ); my $skipChildren = $ini{$section}{'skip_children'} && grep( /^$ini{$section}{'skip_children'}$/, @istrue ); my @datasets; - if ($recursive || $skipChildren) { + + if($ini{$section}{'recursive'} =~ /zfs/i) { + $config{$section}{'zfs_recursion'} = 1; + }elsif ($ini{$section}{'recursive'}) { @datasets = getchilddatasets($config{$section}{'path'}); DATASETS: foreach my $dataset(@datasets) { chomp $dataset; From 4daafca9cf5386ac33bb0cec6e29c2c0e9ab2c38 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Thu, 20 Dec 2018 01:33:35 +0100 Subject: [PATCH 21/31] prevent problems with shell expansion and codestyle --- sanoid | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/sanoid b/sanoid index 30363c5..9fcbb88 100755 --- a/sanoid +++ b/sanoid @@ -503,12 +503,11 @@ sub take_snapshots { %datestamp = get_date(); # print "we should have had a $type snapshot of $path $maxage seconds ago; most recent is $newestage seconds old.\n"; - # use zfs recursion if specified in config + # use zfs (atomic) recursion if specified in config if ($config{$section}{'zfs_recursion'}) { - push(@newsnaps, "-r $path\@autosnap_$datestamp{'sortable'}_$type"); - - }else{ - push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}_$type"); + push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}${dateSuffix}_$type\@"); + } else { + push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}${dateSuffix}_$type"); } } } @@ -517,8 +516,14 @@ sub take_snapshots { if ( (scalar(@newsnaps)) > 0) { foreach my $snap ( @newsnaps ) { - my $dataset = (split '@', $snap)[0]; - my $snapname = (split '@', $snap)[1]; + my @split = split '@', $snap, -1; + my $recursiveFlag = 0; + if (scalar(@split) == 3) { + $recursiveFlag = 1; + chop $snap; + } + my $dataset = $split[0]; + my $snapname = $split[1]; my $presnapshotfailure = 0; if ($config{$dataset}{'pre_snapshot_script'} and !$args{'readonly'}) { $ENV{'SANOID_TARGET'} = $dataset; @@ -537,8 +542,13 @@ sub take_snapshots { } if ($args{'verbose'}) { print "taking snapshot $snap\n"; } if (!$args{'readonly'}) { - system("$zfs snapshot $snap") == 0 - or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?"; + if ($recursiveFlag) { + system($zfs, "snapshot", "-r", "$snap") == 0 + or warn "CRITICAL ERROR: $zfs snapshot -r $snap failed, $?"; + } else { + system($zfs, "snapshot", "$snap") == 0 + or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?"; + } } if ($config{$dataset}{'post_snapshot_script'} and !$args{'readonly'}) { if (!$presnapshotfailure or $config{$dataset}{'force_post_snapshot_script'}) { @@ -853,10 +863,9 @@ sub init { my $recursive = $ini{$section}{'recursive'} && grep( /^$ini{$section}{'recursive'}$/, @istrue ); my $skipChildren = $ini{$section}{'skip_children'} && grep( /^$ini{$section}{'skip_children'}$/, @istrue ); my @datasets; - - if($ini{$section}{'recursive'} =~ /zfs/i) { + if ($ini{$section}{'recursive'} =~ /zfs/i) { $config{$section}{'zfs_recursion'} = 1; - }elsif ($ini{$section}{'recursive'}) { + } elsif ($recursive || $skipChildren) { @datasets = getchilddatasets($config{$section}{'path'}); DATASETS: foreach my $dataset(@datasets) { chomp $dataset; From 7cf64be7029a985bd8dbde04688bb947861a15ef Mon Sep 17 00:00:00 2001 From: matveevandrey Date: Thu, 20 Dec 2018 22:53:57 +0300 Subject: [PATCH 22/31] pre/post snapshot script calls should be also dumped in --readonly mode --- sanoid | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sanoid b/sanoid index ce8207c..03aefc5 100755 --- a/sanoid +++ b/sanoid @@ -513,11 +513,15 @@ sub take_snapshots { my $dataset = (split '@', $snap)[0]; my $snapname = (split '@', $snap)[1]; my $presnapshotfailure = 0; - if ($config{$dataset}{'pre_snapshot_script'} and !$args{'readonly'}) { + my $ret = 0; + if ($config{$dataset}{'pre_snapshot_script'}) { $ENV{'SANOID_TARGET'} = $dataset; $ENV{'SANOID_SNAPNAME'} = $snapname; if ($args{'verbose'}) { print "executing pre_snapshot_script '".$config{$dataset}{'pre_snapshot_script'}."' on dataset '$dataset'\n"; } - my $ret = runscript('pre_snapshot_script',$dataset); + + if (!$args{'readonly'}) { + $ret = runscript('pre_snapshot_script',$dataset); + } delete $ENV{'SANOID_TARGET'}; delete $ENV{'SANOID_SNAPNAME'}; @@ -533,12 +537,15 @@ sub take_snapshots { system($zfs, "snapshot", "$snap") == 0 or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?"; } - if ($config{$dataset}{'post_snapshot_script'} and !$args{'readonly'}) { + if ($config{$dataset}{'post_snapshot_script'}) { if (!$presnapshotfailure or $config{$dataset}{'force_post_snapshot_script'}) { $ENV{'SANOID_TARGET'} = $dataset; $ENV{'SANOID_SNAPNAME'} = $snapname; if ($args{'verbose'}) { print "executing post_snapshot_script '".$config{$dataset}{'post_snapshot_script'}."' on dataset '$dataset'\n"; } - runscript('post_snapshot_script',$dataset); + + if (!$args{'readonly'}) { + runscript('post_snapshot_script',$dataset); + } delete $ENV{'SANOID_TARGET'}; delete $ENV{'SANOID_SNAPNAME'}; From f8e0e006ab92344b62a640988c668631c243107b Mon Sep 17 00:00:00 2001 From: matveevandrey Date: Thu, 20 Dec 2018 23:52:40 +0300 Subject: [PATCH 23/31] preserve taking snapshots order always from yearly to frequently very simple patch (not best way in terms of programming but minimal change requirements achieved) --- sanoid | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sanoid b/sanoid index 03aefc5..d30dcba 100755 --- a/sanoid +++ b/sanoid @@ -377,9 +377,12 @@ sub take_snapshots { if ($config{$section}{'process_children_only'}) { next; } my $path = $config{$section}{'path'}; + my @types = ('yearly','monthly','weekly','daily','hourly','frequently'); - foreach my $type (keys %{ $config{$section} }){ - unless ($type =~ /ly$/) { next; } + foreach my $type (@types) { + + foreach my $_type (keys %{ $config{$section} }){ + unless ($type eq $_type) { next; } if ($config{$section}{$type} > 0) { my $newestage; # in seconds @@ -506,6 +509,7 @@ sub take_snapshots { } } } + } } if ( (scalar(@newsnaps)) > 0) { From c35c953e548a922e57edfaa046313a5fafe271a6 Mon Sep 17 00:00:00 2001 From: matveevandrey Date: Fri, 21 Dec 2018 12:56:10 +0300 Subject: [PATCH 24/31] remove redundant loop --- sanoid | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sanoid b/sanoid index d30dcba..82cfce9 100755 --- a/sanoid +++ b/sanoid @@ -380,9 +380,6 @@ sub take_snapshots { my @types = ('yearly','monthly','weekly','daily','hourly','frequently'); foreach my $type (@types) { - - foreach my $_type (keys %{ $config{$section} }){ - unless ($type eq $_type) { next; } if ($config{$section}{$type} > 0) { my $newestage; # in seconds @@ -509,7 +506,6 @@ sub take_snapshots { } } } - } } if ( (scalar(@newsnaps)) > 0) { From ca76f4268b27f8330f91b1e1421853765c326047 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 23 Dec 2018 09:18:49 -0500 Subject: [PATCH 25/31] syncoid: add '--mbuffer-size' option --- syncoid | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/syncoid b/syncoid index d3e678f..1085e36 100755 --- a/syncoid +++ b/syncoid @@ -14,13 +14,16 @@ use Pod::Usage; use Time::Local; use Sys::Hostname; +my $mbuffer_size = "16M"; + # Blank defaults to use ssh client's default # TODO: Merge into a single "sshflags" option? my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => ''); GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@", "debug", "quiet", "no-stream", "no-sync-snap", "no-resume", "exclude=s@", "skip-parent", "identifier=s", - "no-clone-handling", "no-privilege-elevation", "force-delete", "no-clone-rollback", "no-rollback") or pod2usage(2); + "no-clone-handling", "no-privilege-elevation", "force-delete", "no-clone-rollback", "no-rollback", + "mbuffer-size=s" => \$mbuffer_size) or pod2usage(2); my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set @@ -56,7 +59,7 @@ my $pscmd = '/bin/ps'; my $pvcmd = '/usr/bin/pv'; my $mbuffercmd = '/usr/bin/mbuffer'; my $sudocmd = '/usr/bin/sudo'; -my $mbufferoptions = '-q -s 128k -m 16M 2>/dev/null'; +my $mbufferoptions = "-q -s 128k -m $mbuffer_size 2>/dev/null"; # currently using ls to check for file existence because we aren't depending on perl # being present on remote machines. my $lscmd = '/bin/ls'; @@ -1489,6 +1492,7 @@ Options: --skip-parent Skips syncing of the parent dataset. Does nothing without '--recursive' option. --source-bwlimit= Bandwidth limit on the source transfer --target-bwlimit= Bandwidth limit on the target transfer + --mbuffer-size=VALUE Specify the mbuffer size (default: 16M), please refer to mbuffer(1) manual page. --no-stream Replicates using newest snapshot instead of intermediates --no-sync-snap Does not create new snapshot, only transfers existing --no-clone-rollback Does not rollback clones on target From bd4eb491d8d101333381340382d1f1ab3a88d4d5 Mon Sep 17 00:00:00 2001 From: Matthew Swabey Date: Wed, 12 Sep 2018 22:22:03 -0400 Subject: [PATCH 26/31] Added in --sendoptions=OPTIONS and --recvoptions=OPTIONS to inject OPTIONS enabling things like syncoid --sendoptions=-Lcep and syncoid --recvoptions="-x property" Co-authored-by: Clint Armstrong --- syncoid | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/syncoid b/syncoid index d3e678f..009f10e 100755 --- a/syncoid +++ b/syncoid @@ -17,13 +17,24 @@ use Sys::Hostname; # Blank defaults to use ssh client's default # TODO: Merge into a single "sshflags" option? my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => ''); -GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", +GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "sendoptions=s", "recvoptions=s", "source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@", "debug", "quiet", "no-stream", "no-sync-snap", "no-resume", "exclude=s@", "skip-parent", "identifier=s", "no-clone-handling", "no-privilege-elevation", "force-delete", "no-clone-rollback", "no-rollback") or pod2usage(2); my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set +my $sendoptions = ''; +if (length $args{'sendoptions'}) { + $sendoptions = $args{'sendoptions'} +} + +my $recvoptions = ''; +if (length $args{'recvoptions'}) { + $recvoptions = $args{'recvoptions'} +} + + # TODO Expand to accept multiple sources? if (scalar(@ARGV) != 2) { print("Source or target not found!\n"); @@ -365,13 +376,13 @@ sub syncdataset { if (defined $args{'no-stream'}) { $oldestsnap = getnewestsnapshot(\%snaps); } my $oldestsnapescaped = escapeshellparam($oldestsnap); - my $sendcmd = "$sourcesudocmd $zfscmd send $sourcefsescaped\@$oldestsnapescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; + my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions $sourcefsescaped\@$oldestsnapescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped"; my $pvsize; if (defined $origin) { my $originescaped = escapeshellparam($origin); - $sendcmd = "$sourcesudocmd $zfscmd send -i $originescaped $sourcefsescaped\@$oldestsnapescaped"; + $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -i $originescaped $sourcefsescaped\@$oldestsnapescaped"; my $streamargBackup = $args{'streamarg'}; $args{'streamarg'} = "-i"; $pvsize = getsendsize($sourcehost,$origin,"$sourcefs\@$oldestsnap",$sourceisroot); @@ -419,7 +430,7 @@ sub syncdataset { # $originaltargetreadonly = getzfsvalue($targethost,$targetfs,$targetisroot,'readonly'); # setzfsvalue($targethost,$targetfs,$targetisroot,'readonly','on'); - $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$oldestsnapescaped $sourcefsescaped\@$newsyncsnapescaped"; + $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions $args{'streamarg'} $sourcefsescaped\@$oldestsnapescaped $sourcefsescaped\@$newsyncsnapescaped"; $pvsize = getsendsize($sourcehost,"$sourcefs\@$oldestsnap","$sourcefs\@$newsyncsnap",$sourceisroot); $disp_pvsize = readablebytes($pvsize); if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; } @@ -456,8 +467,8 @@ sub syncdataset { # and because this will ony resume the receive to the next # snapshot, do a normal sync after that if (defined($receivetoken)) { - my $sendcmd = "$sourcesudocmd $zfscmd send -t $receivetoken"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; + my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -t $receivetoken"; + my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped"; my $pvsize = getsendsize($sourcehost,"","",$sourceisroot,$receivetoken); my $disp_pvsize = readablebytes($pvsize); if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; } @@ -608,8 +619,8 @@ sub syncdataset { if ($nextsnapshot) { my $nextsnapshotescaped = escapeshellparam($nextsnapshot); - my $sendcmd = "$sourcesudocmd $zfscmd send -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$nextsnapshotescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; + my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$nextsnapshotescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped"; my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot); if (!$quiet) { print "Sending incremental $sourcefs#$bookmarkescaped ... $nextsnapshot (~ $disp_pvsize):\n"; } @@ -623,8 +634,8 @@ sub syncdataset { $matchingsnap = $nextsnapshot; $matchingsnapescaped = escapeshellparam($matchingsnap); } else { - my $sendcmd = "$sourcesudocmd $zfscmd send -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$newsyncsnapescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; + my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$newsyncsnapescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped"; my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot); if (!$quiet) { print "Sending incremental $sourcefs#$bookmarkescaped ... $newsyncsnap (~ $disp_pvsize):\n"; } @@ -640,8 +651,8 @@ sub syncdataset { # do a normal replication if bookmarks aren't used or if previous # bookmark replication was only done to the next oldest snapshot if (!$bookmark || $nextsnapshot) { - my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped"; - my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs $forcedrecv $targetfsescaped"; + my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped"; + my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped"; my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot); my $disp_pvsize = readablebytes($pvsize); if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; } @@ -1384,7 +1395,7 @@ sub getsendsize { $snaps = "-t $receivetoken"; } - my $getsendsizecmd = "$sourcessh $mysudocmd $zfscmd send -nP $snaps"; + my $getsendsizecmd = "$sourcessh $mysudocmd $zfscmd send $sendoptions -nP $snaps"; if ($debug) { print "DEBUG: getting estimated transfer size from source $sourcehost using \"$getsendsizecmd 2>&1 |\"...\n"; } open FH, "$getsendsizecmd 2>&1 |"; @@ -1494,6 +1505,8 @@ Options: --no-clone-rollback Does not rollback clones on target --no-rollback Does not rollback clones or snapshots on target (it probably requires a readonly target) --exclude=REGEX Exclude specific datasets which match the given regular expression. Can be specified multiple times + --sendoptions=OPTIONS DANGER: Inject OPTIONS into zfs send, e.g. syncoid --sendoptions="-Lce" sets zfs send -Lce ... + --recvoptions=OPTIONS DANGER: Inject OPTIONS into zfs received, e.g. syncoid --recvoptions="-x property" sets zfs receive -x property ... --sshkey=FILE Specifies a ssh public key to use to connect --sshport=PORT Connects to remote on a particular port --sshcipher|c=CIPHER Passes CIPHER to ssh to use a particular cipher set From f941d7f78244f2be1f9b3f7a3d826feccae993b4 Mon Sep 17 00:00:00 2001 From: WG Dev Date: Sun, 6 Jan 2019 10:46:24 +0100 Subject: [PATCH 27/31] fix for #316 - CRITICAL ERROR: bookmarks couldn't be listed for --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index d3e678f..b0a8ec8 100755 --- a/syncoid +++ b/syncoid @@ -1316,7 +1316,7 @@ sub getbookmarks() { close FH or $error = 1; if ($error == 1) { - if ($rawbookmarks[0] =~ /invalid type/) { + if ($rawbookmarks[0] =~ /invalid type/ or $rawbookmarks[0] =~ /operation not applicable to datasets of this type/) { # no support for zfs bookmarks, return empty hash return %bookmarks; } From 165faee70600bd866ea037c6c4fd123a6fd227d2 Mon Sep 17 00:00:00 2001 From: Jim Salter Date: Sun, 6 Jan 2019 15:29:29 -0500 Subject: [PATCH 28/31] remove spurious line break from HTML --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 99d8db1..4a57c32 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ Sanoid is a policy-driven snapshot management tool for ZFS filesystems. When combined with the Linux KVM hypervisor, you can use it to make your systems functionally immortal. -

sanoid rollback demo
(Real time demo: rolling back a full-scale cryptomalware infection in seconds!)

+

sanoid rollback demo
(Real time demo: rolling back a full-scale cryptomalware infection in seconds!)

More prosaically, you can use Sanoid to create, automatically thin, and monitor snapshots and pool health from a single eminently human-readable TOML config file at /etc/sanoid/sanoid.conf. (Sanoid also requires a "defaults" file located at /etc/sanoid/sanoid.defaults.conf, which is not user-editable.) A typical Sanoid system would have a single cron job: ``` From 50a237402156f816b7dbfba50f29a4e44e66dfd1 Mon Sep 17 00:00:00 2001 From: Jim Salter Date: Sun, 6 Jan 2019 15:41:18 -0500 Subject: [PATCH 29/31] Revert "Zfs Recursion" --- sanoid | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/sanoid b/sanoid index 9fcbb88..ce8207c 100755 --- a/sanoid +++ b/sanoid @@ -502,13 +502,7 @@ sub take_snapshots { # update to most current possible datestamp %datestamp = get_date(); # print "we should have had a $type snapshot of $path $maxage seconds ago; most recent is $newestage seconds old.\n"; - - # use zfs (atomic) recursion if specified in config - if ($config{$section}{'zfs_recursion'}) { - push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}${dateSuffix}_$type\@"); - } else { - push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}${dateSuffix}_$type"); - } + push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}${dateSuffix}_$type"); } } } @@ -516,14 +510,8 @@ sub take_snapshots { if ( (scalar(@newsnaps)) > 0) { foreach my $snap ( @newsnaps ) { - my @split = split '@', $snap, -1; - my $recursiveFlag = 0; - if (scalar(@split) == 3) { - $recursiveFlag = 1; - chop $snap; - } - my $dataset = $split[0]; - my $snapname = $split[1]; + my $dataset = (split '@', $snap)[0]; + my $snapname = (split '@', $snap)[1]; my $presnapshotfailure = 0; if ($config{$dataset}{'pre_snapshot_script'} and !$args{'readonly'}) { $ENV{'SANOID_TARGET'} = $dataset; @@ -542,13 +530,8 @@ sub take_snapshots { } if ($args{'verbose'}) { print "taking snapshot $snap\n"; } if (!$args{'readonly'}) { - if ($recursiveFlag) { - system($zfs, "snapshot", "-r", "$snap") == 0 - or warn "CRITICAL ERROR: $zfs snapshot -r $snap failed, $?"; - } else { - system($zfs, "snapshot", "$snap") == 0 - or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?"; - } + system($zfs, "snapshot", "$snap") == 0 + or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?"; } if ($config{$dataset}{'post_snapshot_script'} and !$args{'readonly'}) { if (!$presnapshotfailure or $config{$dataset}{'force_post_snapshot_script'}) { @@ -863,9 +846,7 @@ sub init { my $recursive = $ini{$section}{'recursive'} && grep( /^$ini{$section}{'recursive'}$/, @istrue ); my $skipChildren = $ini{$section}{'skip_children'} && grep( /^$ini{$section}{'skip_children'}$/, @istrue ); my @datasets; - if ($ini{$section}{'recursive'} =~ /zfs/i) { - $config{$section}{'zfs_recursion'} = 1; - } elsif ($recursive || $skipChildren) { + if ($recursive || $skipChildren) { @datasets = getchilddatasets($config{$section}{'path'}); DATASETS: foreach my $dataset(@datasets) { chomp $dataset; From ea482ce7b6d7f662701874c2ef889054831f36d5 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Tue, 8 Jan 2019 20:10:38 +0100 Subject: [PATCH 30/31] make tests work on FreeBSD --- tests/1_one_year/run.sh | 2 +- tests/2_dst_handling/run.sh | 2 +- tests/common/lib.sh | 14 +++++++++++++- tests/run-tests.sh | 2 +- .../1_bookmark_replication_intermediate/run.sh | 4 ++-- .../2_bookmark_replication_no_intermediate/run.sh | 4 ++-- tests/syncoid/3_force_delete/run.sh | 4 ++-- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/tests/1_one_year/run.sh b/tests/1_one_year/run.sh index 1cae7b4..88100da 100755 --- a/tests/1_one_year/run.sh +++ b/tests/1_one_year/run.sh @@ -39,7 +39,7 @@ function cleanUp { trap cleanUp EXIT while [ $timestamp -le $END ]; do - date --utc --set @$timestamp; date; "${SANOID}" --cron --verbose + setdate $timestamp; date; "${SANOID}" --cron --verbose timestamp=$((timestamp+3600)) done diff --git a/tests/2_dst_handling/run.sh b/tests/2_dst_handling/run.sh index eba21ed..7d7774e 100755 --- a/tests/2_dst_handling/run.sh +++ b/tests/2_dst_handling/run.sh @@ -42,7 +42,7 @@ function cleanUp { trap cleanUp EXIT while [ $timestamp -le $END ]; do - date --utc --set @$timestamp; date; "${SANOID}" --cron --verbose + setdate $timestamp; date; "${SANOID}" --cron --verbose timestamp=$((timestamp+900)) done diff --git a/tests/common/lib.sh b/tests/common/lib.sh index 78f128b..3aee40d 100644 --- a/tests/common/lib.sh +++ b/tests/common/lib.sh @@ -1,5 +1,7 @@ #!/bin/bash +unamestr="$(uname)" + function setup { export LANG=C export LANGUAGE=C @@ -90,7 +92,7 @@ function verifySnapshotList { message="${message}monthly snapshot count is wrong: ${monthly_count}\n" fi - checksum=$(sha256sum "${RESULT}" | cut -d' ' -f1) + checksum=$(shasum -a 256 "${RESULT}" | cut -d' ' -f1) if [ "${checksum}" != "${CHECKSUM}" ]; then failed=1 message="${message}result checksum mismatch\n" @@ -105,3 +107,13 @@ function verifySnapshotList { exit 1 } + +function setdate { + TIMESTAMP="$1" + + if [ "$unamestr" == 'FreeBSD' ]; then + date -u -f '%s' "${TIMESTAMP}" + else + date --utc --set "@${TIMESTAMP}" + fi +} diff --git a/tests/run-tests.sh b/tests/run-tests.sh index a8469e9..38054b0 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -15,7 +15,7 @@ for test in */; do echo -n "Running test ${testName} ... " cd "${test}" - echo | bash run.sh > "${LOGFILE}" 2>&1 + echo -n y | bash run.sh > "${LOGFILE}" 2>&1 if [ $? -eq 0 ]; then echo "[PASS]" diff --git a/tests/syncoid/1_bookmark_replication_intermediate/run.sh b/tests/syncoid/1_bookmark_replication_intermediate/run.sh index 11edb04..66af442 100755 --- a/tests/syncoid/1_bookmark_replication_intermediate/run.sh +++ b/tests/syncoid/1_bookmark_replication_intermediate/run.sh @@ -46,8 +46,8 @@ zfs snapshot "${POOL_NAME}"/src@snap5 ../../../syncoid --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1 # verify -output=$(zfs list -t snapshot -r "${POOL_NAME}" -H -o name) -checksum=$(echo "${output}" | grep -v syncoid_ | sha256sum) +output=$(zfs list -t snapshot -r -H -o name "${POOL_NAME}") +checksum=$(echo "${output}" | grep -v syncoid_ | shasum -a 256) if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then exit 1 diff --git a/tests/syncoid/2_bookmark_replication_no_intermediate/run.sh b/tests/syncoid/2_bookmark_replication_no_intermediate/run.sh index 94ac690..f6c1755 100755 --- a/tests/syncoid/2_bookmark_replication_no_intermediate/run.sh +++ b/tests/syncoid/2_bookmark_replication_no_intermediate/run.sh @@ -46,8 +46,8 @@ zfs snapshot "${POOL_NAME}"/src@snap5 ../../../syncoid --no-stream --no-sync-snap --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1 # verify -output=$(zfs list -t snapshot -r "${POOL_NAME}" -H -o name) -checksum=$(echo "${output}" | sha256sum) +output=$(zfs list -t snapshot -r -H -o name "${POOL_NAME}") +checksum=$(echo "${output}" | shasum -a 256) if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then exit 1 diff --git a/tests/syncoid/3_force_delete/run.sh b/tests/syncoid/3_force_delete/run.sh index 03ad9fa..25044cb 100755 --- a/tests/syncoid/3_force_delete/run.sh +++ b/tests/syncoid/3_force_delete/run.sh @@ -37,8 +37,8 @@ sleep 1 ../../../syncoid -r --force-delete --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1 # verify -output=$(zfs list -t snapshot -r "${POOL_NAME}" -H -o name | sed 's/@syncoid_.*$'/@syncoid_/) -checksum=$(echo "${output}" | sha256sum) +output=$(zfs list -t snapshot -r -H -o name "${POOL_NAME}" | sed 's/@syncoid_.*$'/@syncoid_/) +checksum=$(echo "${output}" | shasum -a 256) if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then exit 1 From c3e20fdefcd0a61976155831c6b2b23ac7aa035f Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Wed, 9 Jan 2019 17:41:16 +0100 Subject: [PATCH 31/31] FreeBSD sed needs a different syntax --- tests/common/lib.sh | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/common/lib.sh b/tests/common/lib.sh index 3aee40d..b070da2 100644 --- a/tests/common/lib.sh +++ b/tests/common/lib.sh @@ -60,7 +60,11 @@ function saveSnapshotList { zfs list -t snapshot -o name -Hr "${POOL_NAME}" | sort > "${RESULT}" # clear the seconds for comparing - sed -i 's/\(autosnap_[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:\)[0-9][0-9]_/\100_/g' "${RESULT}" + if [ "$unamestr" == 'FreeBSD' ]; then + sed -i '' 's/\(autosnap_[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:\)[0-9][0-9]_/\100_/g' "${RESULT}" + else + sed -i 's/\(autosnap_[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:\)[0-9][0-9]_/\100_/g' "${RESULT}" + fi } function verifySnapshotList { @@ -109,11 +113,11 @@ function verifySnapshotList { } function setdate { - TIMESTAMP="$1" + TIMESTAMP="$1" - if [ "$unamestr" == 'FreeBSD' ]; then - date -u -f '%s' "${TIMESTAMP}" - else - date --utc --set "@${TIMESTAMP}" - fi + if [ "$unamestr" == 'FreeBSD' ]; then + date -u -f '%s' "${TIMESTAMP}" + else + date --utc --set "@${TIMESTAMP}" + fi }