diff --git a/.gitignore b/.gitignore
index 3d56d93e..f63154c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,5 @@ init/znapzend.service
init/znapzend.sysv
init/znapzend.upstart
init/znapzend.xml
+init/org.znapzend.plist
+.DS_Store
diff --git a/README.md b/README.md
index a7e8fe6b..b75b0493 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,11 @@ since the installed perl version is probably very old.
On OmniOS/SmartOS you will need perl and gnu-make.
+On macOS, if you have not already installed the Xcode command line tools, you can
+get them from the command line (Terminal app) with:
+
+ xcode-select --install (or just install the full Xcode app from the Apple app store).
+
With that in place you can now utter:
```sh
diff --git a/configure.ac b/configure.ac
index ef1736a6..ea0509e6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,7 @@ AC_CONFIG_FILES([
init/znapzend.sysv
init/znapzend.upstart
init/znapzend.xml
+ init/org.znapzend.plist
])
AC_SUBST(VERSION)
diff --git a/init/README.md b/init/README.md
index 652119a2..c1eeb2d8 100644
--- a/init/README.md
+++ b/init/README.md
@@ -13,6 +13,19 @@ replacing the tags in the corresponding ```.in``` template file
to match your existing system layout (replace ```@BINDIR@``` usually
with ```/usr/local/bin``` to match other setup documentation).
+## macOS/launchd
+
+For macOS launchd, you can copy the generated ```org.znapzend.plist```
+file to ```/Library/LaunchDaemons``` and then start the daemon with:
+
+```sh
+launchctl load /Library/LaunchDaemons/org.znapzend.plist
+```
+
+```Note:``` It is recommended to ```not``` set the ```--daemonize``` flag of ```znapzend```
+as launchd will lose control of the process. Check out ```init/org.znapzend.plist.in```
+for an example plist.
+
## Solaris/Illumos
For solaris/illumos OSes you can tell configure to install a znapzend
diff --git a/init/org.znapzend.plist.in b/init/org.znapzend.plist.in
new file mode 100644
index 00000000..241300f9
--- /dev/null
+++ b/init/org.znapzend.plist.in
@@ -0,0 +1,30 @@
+
+
+
+
+ EnvironmentVariables
+
+ PATH
+ /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
+
+ KeepAlive
+
+ Crashed
+
+
+ Label
+ org.znapzend
+ ProgramArguments
+
+ @BINDIR@/org.znapzend
+
+ RunAtLoad
+
+ StandardErrorPath
+ /var/log/org.znapzend.stderr
+ StandardOutPath
+ /var/log/org.znapzend.stdout
+ ThrottleInterval
+ 30
+
+
diff --git a/lib/ZnapZend.pm b/lib/ZnapZend.pm
index d3917215..ca3312cc 100644
--- a/lib/ZnapZend.pm
+++ b/lib/ZnapZend.pm
@@ -129,7 +129,7 @@ my $refreshBackupPlans = sub {
$self->backupSets($self->zConfig->getBackupSetEnabled($dataSet));
@{$self->backupSets}
- or $self->zLog->warn("No backup set defined or enabled, yet. run 'znapzendzetup' to setup znapzend");
+ or die "No backup set defined or enabled, yet. run 'znapzendzetup' to setup znapzend\n";
for my $backupSet (@{$self->backupSets}){
$backupSet->{srcPlanHash} = $self->zTime->backupPlanToHash($backupSet->{src_plan});
@@ -157,7 +157,7 @@ my $refreshBackupPlans = sub {
for (keys %$backupSet){
my ($key) = /^dst_([^_]+)_plan$/ or next;
- #check if destination exists (i.e. is valid) otherwise recheck as dst might be online, now
+ #check if destination exists (i.e. is valid) otherwise recheck as dst might be online, now
if (!$backupSet->{"dst_$key" . '_valid'}){
$backupSet->{"dst_$key" . '_valid'} =
@@ -175,7 +175,7 @@ my $refreshBackupPlans = sub {
};
}
$backupSet->{"dst_$key" . '_valid'} or
- $self->zLog->warn("destination '" . $backupSet->{"dst_$key"}
+ $self->zLog->warn("destination '" . $backupSet->{"dst_$key"}
. "' does not exist or is offline. will be rechecked every run...");
};
}
@@ -227,7 +227,7 @@ my $sendRecvCleanup = sub {
my $sendFailed = 0;
my $startTime = time;
$self->zLog->info('starting work on backupSet ' . $backupSet->{src});
-
+
#get all sub datasets of source filesystem; need to send them all individually if recursive
my $srcSubDataSets = $backupSet->{recursive} eq 'on'
? $self->zZfs->listSubDataSets($backupSet->{src}) : [ $backupSet->{src} ];
@@ -254,7 +254,7 @@ my $sendRecvCleanup = sub {
}
}
- #recheck non valid dst as it might be online, now
+ #recheck non valid dst as it might be online, now
if (!$backupSet->{"dst_$key" . '_valid'}) {
$backupSet->{"dst_$key" . '_valid'} =
@@ -397,7 +397,7 @@ my $createSnapshot = sub {
local $ENV{ZNAP_TIME} = $timeStamp;
my $skip = 0;
-
+
if ($backupSet->{pre_znap_cmd} && $backupSet->{pre_znap_cmd} ne 'off'){
$self->zLog->info("running pre snapshot command on $backupSet->{src}");
@@ -468,7 +468,7 @@ my $sendWorker = sub {
$fc->on(
spawn => sub {
my ($fc, $pid) = @_;
-
+
$self->zLog->debug('send/receive worker for ' . $backupSet->{src}
. " spawned ($pid)");
$backupSet->{send_pid} = $pid;
@@ -505,7 +505,7 @@ my $snapWorker = sub {
#snapshot worker callback
sub {
my ($fc, $err) = @_;
-
+
$self->zLog->warn('taking snapshot on ' . $backupSet->{src}
. ' failed: ' . $err) if $err;
@@ -528,7 +528,7 @@ my $snapWorker = sub {
$fc->on(
spawn => sub {
my ($fc, $pid) = @_;
-
+
$self->zLog->debug('snapshot worker for ' . $backupSet->{src}
. " spawned ($pid)");
$backupSet->{snap_pid} = $pid;
@@ -662,7 +662,7 @@ sub start {
$self->$refreshBackupPlans($self->dataset);
$self->$createWorkers;
};
-
+
$self->$refreshBackupPlans($self->dataset);
$self->$createWorkers;
@@ -670,12 +670,12 @@ sub start {
$self->zLog->info("znapzend (PID=$$) initialized -- resuming normal operations.");
# if Mojo is running with EV, signals will not be received if the IO loop
- # is sleeping so lets activate it periodically
+ # is sleeping so lets activate it periodically
### RM_COMM_4_TEST ### # remove ### RM_COMM_4_TEST ### comments for testing purpose.
### RM_COMM_4_TEST ### if (0) {
Mojo::IOLoop->recurring(1 => sub { }) if not $self->runonce;
### RM_COMM_4_TEST ### }
-
+
#start eventloop
Mojo::IOLoop->start;
@@ -755,4 +755,3 @@ Shadfl@cpan.orgE>
2014-05-30 had Initial Version
=cut
-
diff --git a/t/znapzend.t b/t/znapzend.t
index 755d994a..a25d2ba2 100755
--- a/t/znapzend.t
+++ b/t/znapzend.t
@@ -54,6 +54,7 @@ sub runCommand {
}
use Test::More;
+use Test::Exception;
use_ok 'ZnapZend';
@@ -65,12 +66,15 @@ is (runCommand('--help'), 1, 'znapzend help');
is (runCommand(), 1, 'znapzend');
-is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend --runonce');
+throws_ok { runCommand(qw(--runonce=nosets) ) } qr/No backup set defined or enabled/,
+ 'znapzend dies with no backup sets defined or enabled at startup';
+
+# seems to allow tests to continue so why not?
+is (runCommand('--help'), 1, 'znapzend help');
is (runCommand(qw(--daemonize --debug),'--features=oracleMode,recvu',
qw( --pidfile=znapzend.pid)), 1, 'znapzend --daemonize');
done_testing;
-
-1;
+1;