#!/usr/bin/perl
####################################################################################################################################
# pgBackRest - Simple PostgreSQL Backup and Restore
####################################################################################################################################

####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';

# Convert die to confess to capture the stack trace
$SIG{__DIE__} = sub { Carp::confess @_ };

use File::Basename qw(dirname);

use lib dirname($0) . '/../lib';

use pgBackRest::Archive;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Exit;
use pgBackRest::Common::Lock;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::File;
use pgBackRest::Protocol::Common;
use pgBackRest::Protocol::Protocol;

####################################################################################################################################
# Run in eval block to catch errors
####################################################################################################################################
local $EVAL_ERROR = undef; eval
{
    ################################################################################################################################
    # Load command line parameters and config
    ################################################################################################################################
    my $bConfigResult = configLoad();

    # Display help and version
    if (commandTest(CMD_HELP) || commandTest(CMD_VERSION))
    {
        # Load module dynamically
        require pgBackRest::Config::ConfigHelp;
        pgBackRest::Config::ConfigHelp->import();

        # Generate help and exit
        configHelp($ARGV[1], $ARGV[2], commandTest(CMD_VERSION), $bConfigResult);
        exitSafe(0);
    }

    # Set test options
    if (optionTest(OPTION_TEST) && optionGet(OPTION_TEST))
    {
        testSet(optionGet(OPTION_TEST), optionGet(OPTION_TEST_DELAY), optionGet(OPTION_TEST_POINT, false));
    }

    ################################################################################################################################
    # Process remote commands
    ################################################################################################################################
    if (commandTest(CMD_REMOTE))
    {
        # Set log levels
        optionSet(OPTION_LOG_LEVEL_STDERR, PROTOCOL, true);
        logLevelSet(OFF, OFF, optionGet(OPTION_LOG_LEVEL_STDERR));

        # Check that the repo path exists if this is a backup remote
        if (optionTest(OPTION_TYPE, BACKUP) && !-e optionGet(OPTION_REPO_PATH))
        {
            confess &log(ERROR, 'repo-path \'' . optionGet(OPTION_REPO_PATH) . '\' does not exist', ERROR_PATH_MISSING);
        }

        # Load module dynamically
        require pgBackRest::Protocol::RemoteMinion;
        pgBackRest::Protocol::RemoteMinion->import();

        # Create the remote object
        my $oRemote = new pgBackRest::Protocol::RemoteMinion
        (
            optionGet(OPTION_COMMAND),
            optionGet(OPTION_BUFFER_SIZE),
            optionGet(OPTION_COMPRESS_LEVEL),
            optionGet(OPTION_COMPRESS_LEVEL_NETWORK),
            optionGet(OPTION_PROTOCOL_TIMEOUT)
        );

        # Acquire a remote lock
        lockAcquire(optionGet(OPTION_COMMAND), undef, true, optionGet(OPTION_PROCESS, false));

        # Process remote requests
        exitSafe($oRemote->process());
    }

    ################################################################################################################################
    # Process local commands
    ################################################################################################################################
    if (commandTest(CMD_LOCAL))
    {
        # Set log levels
        optionSet(OPTION_LOG_LEVEL_STDERR, PROTOCOL, true);
        logLevelSet(OFF, OFF, optionGet(OPTION_LOG_LEVEL_STDERR));

        # Load module dynamically
        require pgBackRest::Protocol::LocalMinion;
        pgBackRest::Protocol::LocalMinion->import();

        # Create the local object
        my $oLocal = new pgBackRest::Protocol::LocalMinion(optionGet(OPTION_COMMAND));

        # Acquire a local lock
        lockAcquire(optionGet(OPTION_COMMAND), undef, true, optionGet(OPTION_PROCESS, false));

        # Process local requests
        exitSafe($oLocal->process());
    }

    # Check that the repo path exists
    if (isRepoLocal() && !-e optionGet(OPTION_REPO_PATH))
    {
        confess &log(ERROR, 'repo-path \'' . optionGet(OPTION_REPO_PATH) . '\' does not exist', ERROR_PATH_MISSING);
    }

    ################################################################################################################################
    # Process archive commands
    ################################################################################################################################
    if (commandTest(CMD_ARCHIVE_PUSH) || commandTest(CMD_ARCHIVE_GET))
    {
        if (!isDbLocal())
        {
            confess &log(ERROR, commandGet() . ' command must be run on the db host', ERROR_HOST_INVALID);
        }

        exitSafe(new pgBackRest::Archive()->process());
    }

    ################################################################################################################################
    # Process check command
    ################################################################################################################################
    if (commandTest(CMD_CHECK))
    {
        exitSafe(new pgBackRest::Archive()->check());
    }

    ################################################################################################################################
    # Process start/stop commands
    ################################################################################################################################
    if (commandTest(CMD_START))
    {
        lockStart();
        exitSafe(0);
    }
    elsif (commandTest(CMD_STOP))
    {
        lockStop();
        exitSafe(0);
    }

    ################################################################################################################################
    # Process info command
    ################################################################################################################################
    if (commandTest(CMD_INFO))
    {
        # Load module dynamically
        require pgBackRest::Info;
        pgBackRest::Info->import();

        exitSafe(new pgBackRest::Info()->process());
    }

    ################################################################################################################################
    # Acquire the command lock
    ################################################################################################################################
    lockAcquire(commandGet());

    ################################################################################################################################
    # Open the log file
    ################################################################################################################################
    logFileSet(optionGet(OPTION_LOG_PATH) . '/' . optionGet(OPTION_STANZA) . '-' . lc(commandGet()));

    ################################################################################################################################
    # Process stanza-create command
    ################################################################################################################################
    if (commandTest(CMD_STANZA_CREATE))
    {
        if (!isRepoLocal())
        {
            confess &log(ERROR, commandGet() . ' command must be run on the backup host', ERROR_HOST_INVALID);
        }

        # Load module dynamically
        require pgBackRest::Stanza;
        pgBackRest::Stanza->import();

        exitSafe(new pgBackRest::Stanza()->process());
    }

    ################################################################################################################################
    # RESTORE
    ################################################################################################################################
    if (commandTest(CMD_RESTORE))
    {
        if (!isDbLocal())
        {
            confess &log(ERROR, 'restore command must be run on the db host', ERROR_HOST_INVALID);
        }

        # Load module dynamically
        require pgBackRest::Restore;
        pgBackRest::Restore->import();

        # Do the restore
        new pgBackRest::Restore()->process();

        exitSafe(0);
    }
    else
    {
        ############################################################################################################################
        # Make sure backup and expire commands happen on the backup side
        ############################################################################################################################
        if (!isRepoLocal())
        {
            confess &log(ERROR, 'backup and expire commands must be run on the backup host', ERROR_HOST_INVALID);
        }

        ############################################################################################################################
        # BACKUP
        ############################################################################################################################
        if (commandTest(CMD_BACKUP))
        {
            # Load module dynamically
            require pgBackRest::Backup;
            pgBackRest::Backup->import();

            new pgBackRest::Backup()->process();

            commandSet(CMD_EXPIRE);
        }

        ############################################################################################################################
        # EXPIRE
        ############################################################################################################################
        if (commandTest(CMD_EXPIRE))
        {
            # Load module dynamically
            require pgBackRest::Expire;
            pgBackRest::Expire->import();

            new pgBackRest::Expire()->process();
        }
    }

    lockRelease();
    exitSafe(0);

    # It shouldn't be possible to get here
    &log(ASSERT, 'execution reached invalid location in ' . __FILE__ . ', line ' . __LINE__);
    exit ERROR_ASSERT;
}

####################################################################################################################################
# Check for errors
####################################################################################################################################
or do
{
    exitSafe(undef, $EVAL_ERROR);
};

# It shouldn't be possible to get here
&log(ASSERT, 'execution reached invalid location in ' . __FILE__ . ', line ' . __LINE__);
exit ERROR_ASSERT;
