####################################################################################################################################
# COMMON EXIT MODULE
####################################################################################################################################
package pgBackRest::Common::Exit;

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

use Exporter qw(import);
    our @EXPORT = qw();
use File::Basename qw(dirname);

use pgBackRest::Common::Exception;
use pgBackRest::Common::Lock;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::Protocol::Helper;

####################################################################################################################################
# Signal constants
####################################################################################################################################
use constant SIGNAL_HUP                                             => 'HUP';
use constant SIGNAL_INT                                             => 'INT';
use constant SIGNAL_TERM                                            => 'TERM';

####################################################################################################################################
# Hook important signals into exitSafe function
####################################################################################################################################
$SIG{&SIGNAL_HUP} = sub {exitSafe(ERROR_TERM, undef, SIGNAL_HUP)};
$SIG{&SIGNAL_INT} = sub {exitSafe(ERROR_TERM, undef, SIGNAL_INT)};
$SIG{&SIGNAL_TERM} = sub {exitSafe(ERROR_TERM, undef, SIGNAL_TERM)};

####################################################################################################################################
# exitSafe
#
# Terminate all remotes and release locks.
####################################################################################################################################
sub exitSafe
{
    # Assign function parameters, defaults, and log debug info
    my
    (
        $strOperation,
        $iExitCode,
        $oException,
        $strSignal,
    ) =
        logDebugParam
        (
            __PACKAGE__ . '::exitSafe', \@_,
            {name => 'iExitCode', required => false},
            {name => 'oException', required => false},
            {name => 'strSignal', required => false},
        );

    # Reset logging in case it was disabled when the exception/signal occurred
    configLogging();

    # Close the remote
    protocolDestroy(undef, undef, defined($iExitCode) && ($iExitCode == 0 || $iExitCode == 1));

    # Don't fail if the lock can't be released
    eval
    {
        lockRelease(false);
    }
    # uncoverable branch false - this eval exists only to suppress lock errors so original error will not be lost
    or do {};

    # If exit code is not defined then try to get it from the exception
    if (!defined($iExitCode))
    {
        # If a backrest exception
        if (isException(\$oException))
        {
            $iExitCode = $oException->code();
            logException($oException);
        }
        else
        {
            $iExitCode = ERROR_UNHANDLED;

            &log(
                ERROR,
                'process terminated due to an unhandled exception' .
                    (defined($oException) ? ":\n${oException}" : ': [exception not defined]'),
                $iExitCode);
        }
    }
    elsif ($iExitCode == ERROR_TERM)
    {
        &log(ERROR, "terminated on signal [SIG${strSignal}]", ERROR_TERM);
    }

    # Log command end
    commandEnd(defined($oException) || $iExitCode == ERROR_TERM ? $iExitCode : undef, $strSignal);

    # Log return values if any
    logDebugReturn
    (
        $strOperation,
        {name => 'iExitCode', value => $iExitCode}
    );

    exit $iExitCode;
}

push @EXPORT, qw(exitSafe);

1;
