From 8a2a673c58ddbc1b222ec65a75c4db77b9e8b4e4 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sat, 6 Jan 2018 11:01:44 +0000 Subject: [PATCH 01/14] fixed loud 'NEWEST SNAPSHOT' message --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index 7337f5b..d039d15 100755 --- a/syncoid +++ b/syncoid @@ -597,7 +597,7 @@ sub getnewestsnapshot { my $snaps = shift; foreach my $snap ( sort { $snaps{'source'}{$b}{'creation'}<=>$snaps{'source'}{$a}{'creation'} } keys %{ $snaps{'source'} }) { # return on first snap found - it's the newest - print "NEWEST SNAPSHOT: $snap\n"; + if (!$quiet) { print "NEWEST SNAPSHOT: $snap\n"; } return $snap; } # must not have had any snapshots on source - looks like we'd better create one! From e902df1ef2347aa08aeb20478a2b988695dd5d20 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sat, 6 Jan 2018 11:03:02 +0000 Subject: [PATCH 02/14] also made warnings quiet --- syncoid | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/syncoid b/syncoid index d039d15..aec99cd 100755 --- a/syncoid +++ b/syncoid @@ -452,13 +452,13 @@ sub checkcommands { if ($avail{'sourcecompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - print "WARN: $compressargs{'rawcmd'} not available on source $s- sync will continue without compression.\n"; + if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on source $s- sync will continue without compression.\n"; } } $avail{'compress'} = 0; } if ($avail{'targetcompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - print "WARN: $compressargs{'rawcmd'} not available on target $t - sync will continue without compression.\n"; + if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on target $t - sync will continue without compression.\n"; } } $avail{'compress'} = 0; } @@ -471,7 +471,7 @@ sub checkcommands { # corner case - if source AND target are BOTH remote, we have to check for local compress too if ($sourcehost ne '' && $targethost ne '' && $avail{'localcompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - print "WARN: $compressargs{'rawcmd'} not available on local machine - sync will continue without compression.\n"; + if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on local machine - sync will continue without compression.\n"; } } $avail{'compress'} = 0; } @@ -479,7 +479,7 @@ sub checkcommands { if ($debug) { print "DEBUG: checking availability of $mbuffercmd on source...\n"; } $avail{'sourcembuffer'} = `$sourcessh $lscmd $mbuffercmd 2>/dev/null`; if ($avail{'sourcembuffer'} eq '') { - print "WARN: $mbuffercmd not available on source $s - sync will continue without source buffering.\n"; + if (!$quiet) { print "WARN: $mbuffercmd not available on source $s - sync will continue without source buffering.\n"; } $avail{'sourcembuffer'} = 0; } else { $avail{'sourcembuffer'} = 1; @@ -488,7 +488,7 @@ sub checkcommands { if ($debug) { print "DEBUG: checking availability of $mbuffercmd on target...\n"; } $avail{'targetmbuffer'} = `$targetssh $lscmd $mbuffercmd 2>/dev/null`; if ($avail{'targetmbuffer'} eq '') { - print "WARN: $mbuffercmd not available on target $t - sync will continue without target buffering.\n"; + if (!$quiet) { print "WARN: $mbuffercmd not available on target $t - sync will continue without target buffering.\n"; } $avail{'targetmbuffer'} = 0; } else { $avail{'targetmbuffer'} = 1; @@ -500,14 +500,14 @@ sub checkcommands { $avail{'localmbuffer'} = `$lscmd $mbuffercmd 2>/dev/null`; if ($avail{'localmbuffer'} eq '') { $avail{'localmbuffer'} = 0; - print "WARN: $mbuffercmd not available on local machine - sync will continue without local buffering.\n"; + if (!$quiet) { print "WARN: $mbuffercmd not available on local machine - sync will continue without local buffering.\n"; } } } if ($debug) { print "DEBUG: checking availability of $pvcmd on local machine...\n"; } $avail{'localpv'} = `$lscmd $pvcmd 2>/dev/null`; if ($avail{'localpv'} eq '') { - print "WARN: $pvcmd not available on local machine - sync will continue without progress bar.\n"; + if (!$quiet) { print "WARN: $pvcmd not available on local machine - sync will continue without progress bar.\n"; } $avail{'localpv'} = 0; } else { $avail{'localpv'} = 1; From 7c68ef5e8f2f491fe7de3b23a53f8b64a026a883 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Sun, 29 Jul 2018 13:16:53 +0200 Subject: [PATCH 03/14] return a non zero exit code if there was a problem replicating datasets --- syncoid | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/syncoid b/syncoid index 6c569b2..6453d0f 100755 --- a/syncoid +++ b/syncoid @@ -97,6 +97,7 @@ my $targetsudocmd = $targetisroot ? '' : $sudocmd; my %avail = checkcommands(); my %snaps; +my $exitcode = 0; ## break here to call replication individually so that we ## ## can loop across children separately, for recursive ## @@ -127,7 +128,7 @@ if ($targethost ne '') { close FH; } -exit 0; +exit $exitcode; ############################################################################## ############################################################################## @@ -186,6 +187,7 @@ sub syncdataset { # make sure target is not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } @@ -236,6 +238,7 @@ sub syncdataset { $newsyncsnap = getnewestsnapshot($sourcehost,$sourcefs,$sourceisroot); if ($newsyncsnap eq 0) { warn "CRITICAL: no snapshots exist on source $sourcefs, and you asked for --no-sync-snap.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } } @@ -292,6 +295,7 @@ sub syncdataset { # make sure target is (still) not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } system($synccmd) == 0 @@ -318,6 +322,7 @@ sub syncdataset { # make sure target is (still) not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } @@ -325,9 +330,12 @@ sub syncdataset { if ($debug) { print "DEBUG: $synccmd\n"; } if ($oldestsnap ne $newsyncsnap) { - system($synccmd) == 0 - or warn "CRITICAL ERROR: $synccmd failed: $?"; + my $ret = system($synccmd); + if ($ret != 0) { + warn "CRITICAL ERROR: $synccmd failed: $?"; + if ($exitcode < 1) { $exitcode = 1; } return 0; + } } else { if (!$quiet) { print "INFO: no incremental sync needed; $oldestsnap is already the newest available snapshot.\n"; } } @@ -380,6 +388,7 @@ sub syncdataset { # make sure target is (still) not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } @@ -910,6 +919,7 @@ sub getmatchingsnapshot { } # if we got this far, we failed to find a matching snapshot. + if ($exitcode < 2) { $exitcode = 2; } print "\n"; print "CRITICAL ERROR: Target $targetfs exists but has no snapshots matching with $sourcefs!\n"; From 63eec4994c20d7eb6207f6c6927badf138442f22 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Mon, 30 Jul 2018 22:21:14 +0200 Subject: [PATCH 04/14] don't die on some critical sync errors, but continue to replicate all the other datasets. after all is done exit with an error code --- syncoid | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/syncoid b/syncoid index 6453d0f..1ef1506 100755 --- a/syncoid +++ b/syncoid @@ -298,8 +298,11 @@ sub syncdataset { if ($exitcode < 1) { $exitcode = 1; } return 0; } - system($synccmd) == 0 - or die "CRITICAL ERROR: $synccmd failed: $?"; + system($synccmd) == 0 or do { + warn "CRITICAL ERROR: $synccmd failed: $?"; + if ($exitcode < 2) { $exitcode = 2; } + return 0; + }; # now do an -I to the new sync snapshot, assuming there were any snapshots # other than the new sync snapshot to begin with, of course - and that we @@ -359,8 +362,11 @@ sub syncdataset { if (!$quiet) { print "Resuming interrupted zfs send/receive from $sourcefs to $targetfs (~ $disp_pvsize remaining):\n"; } if ($debug) { print "DEBUG: $synccmd\n"; } - system("$synccmd") == 0 - or die "CRITICAL ERROR: $synccmd failed: $?"; + system("$synccmd") == 0 or do { + warn "CRITICAL ERROR: $synccmd failed: $?"; + if ($exitcode < 2) { $exitcode = 2; } + return 0; + }; # a resumed transfer will only be done to the next snapshot, # so do an normal sync cycle @@ -416,8 +422,11 @@ sub syncdataset { if (!$quiet) { print "Sending incremental $sourcefs\@$matchingsnap ... $newsyncsnap (~ $disp_pvsize):\n"; } if ($debug) { print "DEBUG: $synccmd\n"; } - system("$synccmd") == 0 - or die "CRITICAL ERROR: $synccmd failed: $?"; + system("$synccmd") == 0 or do { + warn "CRITICAL ERROR: $synccmd failed: $?"; + if ($exitcode < 2) { $exitcode = 2; } + return 0; + }; # restore original readonly value to target after sync complete # dyking this functionality out for the time being due to buggy mount/unmount behavior From 9668567a870def5418032fb922d3a27a643059fa Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Mon, 30 Jul 2018 22:53:48 +0200 Subject: [PATCH 05/14] continue replication on more critical errors --- syncoid | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/syncoid b/syncoid index 1ef1506..73205ce 100755 --- a/syncoid +++ b/syncoid @@ -233,6 +233,10 @@ sub syncdataset { if (!defined $args{'no-sync-snap'}) { # create a new syncoid snapshot on the source filesystem. $newsyncsnap = newsyncsnap($sourcehost,$sourcefs,$sourceisroot); + if (!$newsyncsnap) { + # we already whined about the error + return 0; + } } else { # we don't want sync snapshots created, so use the newest snapshot we can find. $newsyncsnap = getnewestsnapshot($sourcehost,$sourcefs,$sourceisroot); @@ -267,6 +271,11 @@ sub syncdataset { } my $oldestsnap = getoldestsnapshot(\%snaps); if (! $oldestsnap) { + if (defined ($args{'no-sync-snap'}) ) { + # we already whined about the missing snapshots + return 0; + } + # getoldestsnapshot() returned false, so use new sync snapshot if ($debug) { print "DEBUG: getoldestsnapshot() returned false, so using $newsyncsnap.\n"; } $oldestsnap = $newsyncsnap; @@ -752,7 +761,7 @@ sub getoldestsnapshot { # must not have had any snapshots on source - luckily, we already made one, amirite? if (defined ($args{'no-sync-snap'}) ) { # well, actually we set --no-sync-snap, so no we *didn't* already make one. Whoops. - die "CRIT: --no-sync-snap is set, and getoldestsnapshot() could not find any snapshots on source!\n"; + warn "CRIT: --no-sync-snap is set, and getoldestsnapshot() could not find any snapshots on source!\n"; } return 0; } @@ -774,6 +783,7 @@ sub getnewestsnapshot { # we also probably need an argument to mute this WARN, for people who deliberately exclude # datasets from recursive replication this way. warn "WARN: --no-sync-snap is set, and getnewestsnapshot() could not find any snapshots on source for current dataset. Continuing.\n"; + if ($exitcode < 2) { $exitcode = 2; } } return 0; } @@ -961,8 +971,12 @@ sub newsyncsnap { my %date = getdate(); my $snapname = "syncoid\_$identifier$hostid\_$date{'stamp'}"; my $snapcmd = "$rhost $mysudocmd $zfscmd snapshot $fsescaped\@$snapname\n"; - system($snapcmd) == 0 - or die "CRITICAL ERROR: $snapcmd failed: $?"; + system($snapcmd) == 0 or do { + warn "CRITICAL ERROR: $snapcmd failed: $?"; + if ($exitcode < 2) { $exitcode = 2; } + return 0; + }; + return $snapname; } From fb8edad885522040369c6ab95edfbb922fb6901d Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 3 Aug 2018 09:13:35 +0100 Subject: [PATCH 06/14] compression warnings are no longer hidden by --quiet --- syncoid | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncoid b/syncoid index aec99cd..85d5e16 100755 --- a/syncoid +++ b/syncoid @@ -452,13 +452,13 @@ sub checkcommands { if ($avail{'sourcecompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on source $s- sync will continue without compression.\n"; } + print "WARN: $compressargs{'rawcmd'} not available on source $s- sync will continue without compression.\n"; } $avail{'compress'} = 0; } if ($avail{'targetcompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on target $t - sync will continue without compression.\n"; } + print "WARN: $compressargs{'rawcmd'} not available on target $t - sync will continue without compression.\n"; } $avail{'compress'} = 0; } @@ -471,7 +471,7 @@ sub checkcommands { # corner case - if source AND target are BOTH remote, we have to check for local compress too if ($sourcehost ne '' && $targethost ne '' && $avail{'localcompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on local machine - sync will continue without compression.\n"; } + print "WARN: $compressargs{'rawcmd'} not available on local machine - sync will continue without compression.\n"; } $avail{'compress'} = 0; } From 39d1fd38c1edd52f45b44707beabe32e3a20900d Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 5 Jan 2018 21:54:06 +0000 Subject: [PATCH 07/14] added ability to skip datasets... simply set syncoid:no-sync=true --- syncoid | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/syncoid b/syncoid index 6c569b2..8adaa87 100755 --- a/syncoid +++ b/syncoid @@ -183,6 +183,11 @@ sub syncdataset { if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } + if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:no-sync') eq 'true') { + print "Skipping dataset (syncoid:nosync=true): $sourcefs...\n"; + return 0; + } + # make sure target is not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; From 22c137627a9a923807565dff01794d9d3775e767 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sat, 6 Jan 2018 10:15:30 +0000 Subject: [PATCH 08/14] updated doc for syncoid:no-sync --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index e359886..ba325e6 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,14 @@ If ZFS supports resumeable send/receive streams on both the source and target th As of 1.4.18, syncoid also automatically supports and enables resume of interrupted replication when both source and target support this feature. +##### Syncoid Dataset Properties + ++ syncoid:no-sync + + Setting this to `true` will prevent the dataset from being handled by syncoid in _any_ way - it will be skipped. This can be useful for preventing certain datasets from being transferred when recursively handling a tree. + + Note that this will also prevent syncoid from handling the dataset if given explicitly on the command line. + ##### Syncoid Command Line Options + [source] From b02b9a582bd3f92c01f16c954add64a78e647896 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sat, 6 Jan 2018 10:43:30 +0000 Subject: [PATCH 09/14] now obeying --quiet, and added 'INFO: ' prefix to skip message --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index 8adaa87..2201f41 100755 --- a/syncoid +++ b/syncoid @@ -184,7 +184,7 @@ sub syncdataset { if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:no-sync') eq 'true') { - print "Skipping dataset (syncoid:nosync=true): $sourcefs...\n"; + if (!$quiet) { print "INFO: Skipping dataset (syncoid:nosync=true): $sourcefs...\n"; } return 0; } From c188e47a6da2d8ee2857a8eb1c0400bc78fc7a53 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 9 Mar 2018 15:15:06 +0000 Subject: [PATCH 10/14] inverted sync/no-sync logic --- syncoid | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncoid b/syncoid index 2201f41..20c0853 100755 --- a/syncoid +++ b/syncoid @@ -183,8 +183,8 @@ sub syncdataset { if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } - if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:no-sync') eq 'true') { - if (!$quiet) { print "INFO: Skipping dataset (syncoid:nosync=true): $sourcefs...\n"; } + if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync') eq 'false') { + if (!$quiet) { print "INFO: Skipping dataset (syncoid:sync=false): $sourcefs...\n"; } return 0; } From f0a37310a54b5e421264d05a02ad226a1409bb98 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 9 Mar 2018 15:18:03 +0000 Subject: [PATCH 11/14] implemented sync true/false/${HOSTS} filtering --- syncoid | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/syncoid b/syncoid index 20c0853..7877723 100755 --- a/syncoid +++ b/syncoid @@ -183,9 +183,20 @@ sub syncdataset { if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } - if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync') eq 'false') { + my $sync = getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync'); + + if ($sync eq 'true' || $sync eq '-') { + # definitely sync this dataset - if a host is called 'true' or '-', then you're special + } elsif ($sync eq 'false') { if (!$quiet) { print "INFO: Skipping dataset (syncoid:sync=false): $sourcefs...\n"; } return 0; + } else { + my $hostid = hostname(); + my @hosts = split(/,/,$sync); + if (!(grep $hostid eq $_, @hosts)) { + if (!$quiet) { print "INFO: Skipping dataset (syncoid:sync doesn't include $hostid): $sourcefs...\n"; } + return 0; + } } # make sure target is not currently in receive. From a4e490f430b73454422c28254012a3328f9bbe98 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 9 Mar 2018 15:28:39 +0000 Subject: [PATCH 12/14] updated documentation for the syncoid:sync property's new behaviour --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ba325e6..bb6ec89 100644 --- a/README.md +++ b/README.md @@ -120,11 +120,27 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup ##### Syncoid Dataset Properties -+ syncoid:no-sync ++ syncoid:sync - Setting this to `true` will prevent the dataset from being handled by syncoid in _any_ way - it will be skipped. This can be useful for preventing certain datasets from being transferred when recursively handling a tree. + Available values: - Note that this will also prevent syncoid from handling the dataset if given explicitly on the command line. + + `true` (default if unset) + + This dataset will be synchronised to all hosts. + + + `false` + + This dataset will not be synchronised to any hosts - it will be skipped. This can be useful for preventing certain datasets from being transferred when recursively handling a tree. + + + `host1,host2,...` + + A comma seperated list of hosts. This dataset will only be synchronised by hosts listed in the property. + + _Note_: this check is performed by the host running `syncoid`, thus the local hostname must be present for inclusion during a push operation // the remote hostname must be present for a pull. + + _Note_: this will also prevent syncoid from handling the dataset if given explicitly on the command line. + + _Note_: syncing a child of a no-sync dataset will currently result in a critical error ##### Syncoid Command Line Options From 60b2dedc456a8464f86cf75dacfe62c0b6410dbc Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Mon, 6 Aug 2018 14:07:58 +0100 Subject: [PATCH 13/14] fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb6ec89..8c05fb3 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup + `host1,host2,...` - A comma seperated list of hosts. This dataset will only be synchronised by hosts listed in the property. + A comma separated list of hosts. This dataset will only be synchronised by hosts listed in the property. _Note_: this check is performed by the host running `syncoid`, thus the local hostname must be present for inclusion during a push operation // the remote hostname must be present for a pull. From d0ba0bc284bb92a8f0e63cfbd773e8c61e175973 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Mon, 6 Aug 2018 14:16:25 +0100 Subject: [PATCH 14/14] handled empty syncoid:sync property - behaves like unset --- README.md | 4 +++- syncoid | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8c05fb3..b833dec 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,9 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup _Note_: this will also prevent syncoid from handling the dataset if given explicitly on the command line. - _Note_: syncing a child of a no-sync dataset will currently result in a critical error + _Note_: syncing a child of a no-sync dataset will currently result in a critical error. + + _Note_: empty properties will be handled as if they were unset. ##### Syncoid Command Line Options diff --git a/syncoid b/syncoid index 7877723..fa00751 100755 --- a/syncoid +++ b/syncoid @@ -185,7 +185,8 @@ sub syncdataset { my $sync = getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync'); - if ($sync eq 'true' || $sync eq '-') { + if ($sync eq 'true' || $sync eq '-' || $sync eq '') { + # empty is handled the same as unset (aka: '-') # definitely sync this dataset - if a host is called 'true' or '-', then you're special } elsif ($sync eq 'false') { if (!$quiet) { print "INFO: Skipping dataset (syncoid:sync=false): $sourcefs...\n"; }