#!/usr/bin/env perl

use lib qw(); # PERL5LIB
use FindBin; use lib "$FindBin::Bin/../lib", "$FindBin::Bin/../thirdparty/lib/perl5"; # LIBDIR

use Getopt::Long qw(:config posix_default no_ignore_case);
use Pod::Usage;

use ZnapZend;
my $VERSION = '0.dev'; #VERSION
my $opts = {};
my %featureMap = map { $_ => 1 } qw(oracleMode recvu pfexec sudo compressed);

sub main {
    GetOptions($opts, qw(help|h man debug|d noaction|n nodestroy features=s),
        qw(sudo pfexec rootExec=s daemonize pidfile=s logto=s loglevel=s),
        qw(runonce:s connectTimeout=s timeWarp=i autoCreation version),
        qw(skipOnPreSnapCmdFail skipOnPreSendCmdFail)) or exit 1;

    $opts->{version} && do {
        print "znapzend version $VERSION\n";
        exit;
    };

    #split all features into individual options
    if ($opts->{features}){
        for my $feature (split /,/, $opts->{features}){
            $featureMap{$feature} or die "ERROR: feature '$feature' not supported\n";
            if ($feature eq "pfexec" or $feature eq "sudo") {
                warn "--features=" . $feature . " is deprecated. Use --rootExec=" . $feature . " instead\n";
                $opts->{rootExec} = $feature;
            } else {
                $opts->{$feature} = 1;
            }
        }
        delete $opts->{features};
    }

    if (defined($opts->{runonce})) {
        $opts->{dataset} = $opts->{runonce};
        $opts->{runonce} = 1;
    }

    $opts->{help} && do {
        pod2usage(-exitval => 'NOEXIT');

### RM_COMM_4_TEST ###  # remove ### RM_COMM_4_TEST ### comments for testing purpose.
### RM_COMM_4_TEST ###  $opts = {};

        return 1;
    };

    $opts->{man} && pod2usage(-exitstatus => 0, -verbose => 2);

    my $znapzend = ZnapZend->new($opts);
    $znapzend->start;

    return 1;
}

main();

1;

__END__

=head1 NAME

znapzend - znapzend daemon

=head1 SYNOPSIS

B<znapzend> [I<options>...]

 --man                  show man-page and exit
 --version              print version and exit
 -h,--help              display this help and exit
 -d,--debug             print debug messages to STDERR
 -n,--noaction          run in simulation mode. does no changes to the filesystem
 --nodestroy            does all changes to the filesystem except destroy
 --logto=x              select where to log to (syslog::<facility> or <filepath>)
 --loglevel=x           define the log level when logging to file
 --pidfile=x            write a pid file when running in daemon mode
 --daemonize            fork into the background
 --runonce=[x]          run one round on the optionally provided dataset
 --features=x           comma separated list of features to be enabled
 --rootExec=x           exec zfs with this command to obtain root privileges (sudo or pfexec)
 --connectTimeout=x     sets the ConnectTimeout for ssh commands
 --autoCreation         automatically create dataset on dest if it not exists
 --timeWarp=x           shift znapzends sens of NOW into the future by x seconds
 --skipOnPreSnapCmdFail skip snapshots if the pre-snap-command fails
 --skipOnPreSendCmdFail skip replication if the pre-send-command fails

=head1 DESCRIPTION

ZnapZend is a snapshot based zfs backup daemon creating snapshots on a
scheduled basis on the source filesystem and on destination filesystems.

ZnapZend reads its configuration from custom properties in the fileset. Use
L<znapzendzetup> to set these properties.

=over

=item B<-d>, B<--debug>

talk a lot while running. Sends debug messages to stderr.

=item B<-n>, B<--noaction>

don't do any actions which have lasting effect. Ideal to try our new new
configurations together with B<--debug>

=item B<--nodestroy>

do all changes to the filesystem except destroy old snapshots

=item B<--logto>={B<syslog::>I<facility>|I<filepath>}

send logs out to either syslog or a logfile. Default is to send logs to
B<syslog::daemon> when runing daemonized. When running in debug mode, the
logs will go to STDERR by default.

Examples:

 --logto=/var/log/znapzend.log
 --logto=syslog::daemon

=item B<--loglevel>={B<debug>|B<info>|B<warning>|B<err>|B<alert>}

Define the log level when logging to file. Default is debug.

=item B<--pidfile>=I<path>

write a pid file when running in daemon mode
B<pidfile> defaults to I</var/run/znapzend.pid> if no pidfile is given

=item B<--daemonize>

Fork into the background.

=item B<--runonce>=[I<dataset>]

run one round on source I<dataset> or on all datasets if empty.
This is very useful for testing. Use it in connection with B<--noaction> and
B<--debug> while testing your new configuration

=item B<--features>=I<feature1>,I<feature2>,...

enables enhanced zfs features not supported by all zfs implementations.
Do not enable features unless you are sure your zfs supports it

Available features:

=over

=item oracleMode

working around the following zfs issues we have seen on oracle:

=over

=item *

The multi snapshot destroy syntax is not available. So stick to destroying
them individually.

=item *

Sometimes a snapshot can not be destroyed because of some oracle zfs bug.
Only a reeboot seems to be able to fix this. So we just destroy the ones we
can destroy. Logging an error about the problem

=back

=item recvu

use the -u option on the receive end, to keep the destination zfs
filesystems unmounted.

=item compressed

use 'compressed' to add options -Lce to the zfs send command
If a source and destination volume are both using compression, zfs send will, by
default, decompress the data before sending. Zfs recv will then compress it again before
writing it to disk. Using -c will skip the unnecessary decompress-compress stages.
This decreases CPU load on both source and destination as well as reduce network
bandwidth. The -L option is for large block support and -e is for embedded data support.

=back

=item B<--rootExec>={sudo|pfexec}

Execute zfs with this command, 'sudo' or 'pfexec', to
obtain root privileges. This is often necessary when running znapzend as a
non-privileged user with a zfs install that doesn't support finer permission
controls. This also applies to the zfs commands ran on remote servers over ssh.

For sudo, the /etc/sudoers file will need to be modified to allow for
passwordless access to zfs commands if znapzend is to be ran as a daemon or
the system will be used as a remote. Many ZFS installations include an
/etc/sudoers.d/zfs file as an example.

=item B<--connectTimeout>=I<timeout>

sets the ssh connection timeout (in seconds)

=item B<--autoCreation>

Automatically create a dataset on a destination host if it's not there yet.

=item B<--timeWarp>=x

Shift ZnapZends sense of time into the future by x seconds.

The practical application if this function is to determine what will happen at some future point in time.
This can be useful for testing but also when running in noaction and debug mode to determine which
snapshots would be created and removed at some future point in time.

=item B<--skipOnPreSnapCmdFail>

Prevent snapshots of a dataset from being taken when it has a pre-snap-command
defined and the command returns a non-zero exit code or is killed by a signal.

=item B<--skipOnPreSendCmdFail>

Prevent snapshots of a dataset from being replicated to a destination when it has
a pre-snap-command defined and the command returns a non-zero exit code or is killed
by a signal.

=back

=head1 EXAMPLE

To test a new config

 znapzend --debug --noaction --runonce=tank/test

To see what is going to happen in one hour

 znapzend --debug --noaction --timeWarp=3600 --runonce=tank/test

To run as a daemon

 znapzend --daemonize --pidfile=/var/run/znapzend.pid --logto=syslog::daemon

=head1 COPYRIGHT

Copyright (c) 2014 by OETIKER+PARTNER AG. All rights reserved.

=head1 LICENSE

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.

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

You should have received a copy of the GNU General Public License along with
this program. If not, see L<http://www.gnu.org/licenses/>.

=head1 AUTHOR

S<Tobias Oetiker E<lt>tobi@oetiker.chE<gt>>,
S<Dominik Hassler E<lt>hadfl@cpan.orgE<gt>>

=head1 HISTORY

 2014-06-01 had Multi destination backup
 2014-05-30 had Initial Version

=cut
