2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# UTILITY MODULE
|
|
|
|
####################################################################################################################################
|
2014-06-07 23:13:41 +03:00
|
|
|
package BackRest::Utility;
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-02-21 15:34:17 +03:00
|
|
|
use threads;
|
2014-02-03 03:03:05 +03:00
|
|
|
use strict;
|
2015-03-03 07:57:20 +02:00
|
|
|
use warnings FATAL => qw(all);
|
2015-01-24 01:28:39 +02:00
|
|
|
use Carp qw(confess longmess);
|
2014-07-28 01:13:23 +03:00
|
|
|
|
2014-02-15 03:56:28 +03:00
|
|
|
use Fcntl qw(:DEFAULT :flock);
|
2014-04-28 16:13:25 +03:00
|
|
|
use File::Path qw(remove_tree);
|
2015-01-27 18:44:23 +02:00
|
|
|
use Time::HiRes qw(gettimeofday usleep);
|
|
|
|
use POSIX qw(ceil);
|
2014-06-07 18:51:27 +03:00
|
|
|
use File::Basename;
|
2015-04-01 21:58:33 +02:00
|
|
|
use Cwd qw(abs_path);
|
2014-07-27 21:03:21 +03:00
|
|
|
use JSON;
|
2014-06-07 18:51:27 +03:00
|
|
|
|
2014-09-16 15:21:29 +03:00
|
|
|
use lib dirname($0) . '/../lib';
|
2014-06-07 18:51:27 +03:00
|
|
|
use BackRest::Exception;
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
use Exporter qw(import);
|
|
|
|
|
2014-06-07 23:06:46 +03:00
|
|
|
our @EXPORT = qw(version_get
|
2015-04-01 21:58:33 +02:00
|
|
|
data_hash_build trim common_prefix file_size_format execute
|
2015-01-24 01:28:39 +02:00
|
|
|
log log_file_set log_level_set test_set test_get test_check
|
2015-01-27 18:44:23 +02:00
|
|
|
lock_file_create lock_file_remove hsleep wait_remainder
|
2014-12-16 00:20:42 +02:00
|
|
|
ini_save ini_load timestamp_string_get timestamp_file_string_get
|
2014-07-28 01:13:23 +03:00
|
|
|
TRACE DEBUG ERROR ASSERT WARN INFO OFF true false
|
2015-03-04 04:21:07 +02:00
|
|
|
TEST TEST_ENCLOSE TEST_MANIFEST_BUILD TEST_BACKUP_RESUME TEST_BACKUP_NORESUME FORMAT);
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Global constants
|
|
|
|
use constant
|
|
|
|
{
|
|
|
|
true => 1,
|
|
|
|
false => 0
|
|
|
|
};
|
|
|
|
|
2014-02-14 01:11:53 +03:00
|
|
|
use constant
|
|
|
|
{
|
|
|
|
TRACE => 'TRACE',
|
|
|
|
DEBUG => 'DEBUG',
|
|
|
|
INFO => 'INFO',
|
|
|
|
WARN => 'WARN',
|
|
|
|
ERROR => 'ERROR',
|
|
|
|
ASSERT => 'ASSERT',
|
|
|
|
OFF => 'OFF'
|
|
|
|
};
|
|
|
|
|
2014-02-13 21:54:43 +03:00
|
|
|
my $hLogFile;
|
2014-02-14 01:11:53 +03:00
|
|
|
my $strLogLevelFile = ERROR;
|
|
|
|
my $strLogLevelConsole = ERROR;
|
|
|
|
my %oLogLevelRank;
|
|
|
|
|
2014-04-28 16:13:25 +03:00
|
|
|
my $strLockPath;
|
2014-02-15 03:56:28 +03:00
|
|
|
my $hLockFile;
|
|
|
|
|
2014-02-14 01:11:53 +03:00
|
|
|
$oLogLevelRank{TRACE}{rank} = 6;
|
|
|
|
$oLogLevelRank{DEBUG}{rank} = 5;
|
|
|
|
$oLogLevelRank{INFO}{rank} = 4;
|
|
|
|
$oLogLevelRank{WARN}{rank} = 3;
|
|
|
|
$oLogLevelRank{ERROR}{rank} = 2;
|
|
|
|
$oLogLevelRank{ASSERT}{rank} = 1;
|
|
|
|
$oLogLevelRank{OFF}{rank} = 0;
|
2014-02-13 21:54:43 +03:00
|
|
|
|
2015-03-04 04:21:07 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# FORMAT Constant
|
|
|
|
#
|
|
|
|
# Identified the format of the manifest and file structure. The format is used to determine compatability between versions.
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant FORMAT => 3;
|
|
|
|
|
2014-07-28 01:13:23 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# TEST Constants and Variables
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant
|
|
|
|
{
|
2015-01-22 17:54:02 +02:00
|
|
|
TEST => 'TEST',
|
|
|
|
TEST_ENCLOSE => 'PgBaCkReStTeSt',
|
|
|
|
|
|
|
|
TEST_MANIFEST_BUILD => 'MANIFEST_BUILD',
|
|
|
|
TEST_BACKUP_RESUME => 'BACKUP_RESUME',
|
|
|
|
TEST_BACKUP_NORESUME => 'BACKUP_NORESUME',
|
2014-07-28 01:13:23 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
# Test global variables
|
|
|
|
my $bTest = false;
|
2015-01-25 19:20:12 +02:00
|
|
|
my $fTestDelay;
|
2014-07-28 01:13:23 +03:00
|
|
|
|
2014-06-07 23:06:46 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# VERSION_GET
|
|
|
|
####################################################################################################################################
|
|
|
|
my $strVersion;
|
|
|
|
|
|
|
|
sub version_get
|
|
|
|
{
|
|
|
|
my $hVersion;
|
|
|
|
|
2015-04-01 21:58:33 +02:00
|
|
|
# If version is already stored then return it (should never change during execution)
|
|
|
|
if (defined($strVersion))
|
2014-06-07 23:06:46 +03:00
|
|
|
{
|
2015-04-01 21:58:33 +02:00
|
|
|
return $strVersion;
|
2014-06-07 23:06:46 +03:00
|
|
|
}
|
|
|
|
|
2015-04-01 21:58:33 +02:00
|
|
|
# Construct the version file name
|
|
|
|
my $strVersionFile = abs_path(dirname($0) . '/../VERSION');
|
|
|
|
|
|
|
|
# Open the file
|
|
|
|
if (!open($hVersion, '<', $strVersionFile))
|
|
|
|
{
|
|
|
|
confess &log(ASSERT, "unable to open VERSION file: ${strVersionFile}");
|
|
|
|
}
|
|
|
|
|
|
|
|
# Read version and trim
|
2014-06-07 23:06:46 +03:00
|
|
|
if (!($strVersion = readline($hVersion)))
|
|
|
|
{
|
2015-04-01 21:58:33 +02:00
|
|
|
confess &log(ASSERT, "unable to read VERSION file: ${strVersionFile}");
|
2014-06-07 23:06:46 +03:00
|
|
|
}
|
|
|
|
|
2015-04-01 21:58:33 +02:00
|
|
|
$strVersion = trim($strVersion);
|
|
|
|
|
|
|
|
# Close file
|
2014-06-07 23:06:46 +03:00
|
|
|
close($hVersion);
|
|
|
|
|
2015-04-01 21:58:33 +02:00
|
|
|
return $strVersion;
|
2014-06-07 23:06:46 +03:00
|
|
|
}
|
|
|
|
|
2014-02-15 03:56:28 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# LOCK_FILE_CREATE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub lock_file_create
|
|
|
|
{
|
2014-04-28 16:13:25 +03:00
|
|
|
my $strLockPathParam = shift;
|
|
|
|
|
2014-09-16 15:21:29 +03:00
|
|
|
my $strLockFile = $strLockPathParam . '/process.lock';
|
2014-02-15 03:56:28 +03:00
|
|
|
|
|
|
|
if (defined($hLockFile))
|
|
|
|
{
|
2014-04-28 16:13:25 +03:00
|
|
|
confess &lock(ASSERT, "${strLockFile} lock is already held");
|
|
|
|
}
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-04-28 16:13:25 +03:00
|
|
|
$strLockPath = $strLockPathParam;
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-04-28 16:13:25 +03:00
|
|
|
unless (-e $strLockPath)
|
|
|
|
{
|
|
|
|
if (system("mkdir -p ${strLockPath}") != 0)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "Unable to create lock path ${strLockPath}");
|
|
|
|
}
|
2014-02-15 03:56:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
sysopen($hLockFile, $strLockFile, O_WRONLY | O_CREAT)
|
|
|
|
or confess &log(ERROR, "unable to open lock file ${strLockFile}");
|
|
|
|
|
|
|
|
if (!flock($hLockFile, LOCK_EX | LOCK_NB))
|
|
|
|
{
|
|
|
|
close($hLockFile);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-02-15 03:56:28 +03:00
|
|
|
return $hLockFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# LOCK_FILE_REMOVE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub lock_file_remove
|
|
|
|
{
|
|
|
|
if (defined($hLockFile))
|
|
|
|
{
|
|
|
|
close($hLockFile);
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-04-28 16:13:25 +03:00
|
|
|
remove_tree($strLockPath) or confess &log(ERROR, "unable to delete lock path ${strLockPath}");
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-02-15 03:56:28 +03:00
|
|
|
$hLockFile = undef;
|
2014-04-28 16:13:25 +03:00
|
|
|
$strLockPath = undef;
|
2014-02-15 03:56:28 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-09-16 15:21:29 +03:00
|
|
|
confess &log(ASSERT, 'there is no lock to free');
|
2014-02-15 03:56:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-27 18:44:23 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# WAIT_REMAINDER - Wait the remainder of the current second
|
|
|
|
####################################################################################################################################
|
|
|
|
sub wait_remainder
|
|
|
|
{
|
|
|
|
my $lTimeBegin = gettimeofday();
|
2015-04-19 23:27:01 +02:00
|
|
|
my $lSleepMs = ceil(((int($lTimeBegin) + 1.05) - $lTimeBegin) * 1000);
|
2015-01-27 18:44:23 +02:00
|
|
|
|
|
|
|
usleep($lSleepMs * 1000);
|
|
|
|
|
|
|
|
&log(TRACE, "WAIT_REMAINDER: slept ${lSleepMs}ms: begin ${lTimeBegin}, end " . gettimeofday());
|
|
|
|
|
|
|
|
return int($lTimeBegin);
|
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# DATA_HASH_BUILD - Hash a delimited file with header
|
|
|
|
####################################################################################################################################
|
|
|
|
sub data_hash_build
|
|
|
|
{
|
2014-06-02 23:26:37 +03:00
|
|
|
my $oHashRef = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strData = shift;
|
|
|
|
my $strDelimiter = shift;
|
|
|
|
my $strUndefinedKey = shift;
|
|
|
|
|
|
|
|
my @stryFile = split("\n", $strData);
|
|
|
|
my @stryHeader = split($strDelimiter, $stryFile[0]);
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
for (my $iLineIdx = 1; $iLineIdx < scalar @stryFile; $iLineIdx++)
|
|
|
|
{
|
|
|
|
my @stryLine = split($strDelimiter, $stryFile[$iLineIdx]);
|
|
|
|
|
2014-09-16 15:21:29 +03:00
|
|
|
if (!defined($stryLine[0]) || $stryLine[0] eq '')
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
|
|
|
$stryLine[0] = $strUndefinedKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (my $iColumnIdx = 1; $iColumnIdx < scalar @stryHeader; $iColumnIdx++)
|
|
|
|
{
|
2014-06-02 23:26:37 +03:00
|
|
|
if (defined(${$oHashRef}{"$stryHeader[0]"}{"$stryLine[0]"}{"$stryHeader[$iColumnIdx]"}))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-09-16 15:21:29 +03:00
|
|
|
confess 'the first column must be unique to build the hash';
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-09-16 15:21:29 +03:00
|
|
|
if (defined($stryLine[$iColumnIdx]) && $stryLine[$iColumnIdx] ne '')
|
2014-06-02 23:26:37 +03:00
|
|
|
{
|
|
|
|
${$oHashRef}{"$stryHeader[0]"}{"$stryLine[0]"}{"$stryHeader[$iColumnIdx]"} = $stryLine[$iColumnIdx];
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# TRIM - trim whitespace off strings
|
|
|
|
####################################################################################################################################
|
|
|
|
sub trim
|
|
|
|
{
|
|
|
|
my $strBuffer = shift;
|
|
|
|
|
2014-05-27 16:00:24 +03:00
|
|
|
if (!defined($strBuffer))
|
|
|
|
{
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
$strBuffer =~ s/^\s+|\s+$//g;
|
|
|
|
|
|
|
|
return $strBuffer;
|
|
|
|
}
|
|
|
|
|
2015-01-25 19:20:12 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# hsleep - wrapper for usleep that takes seconds in fractions and returns time slept in ms
|
|
|
|
####################################################################################################################################
|
|
|
|
sub hsleep
|
|
|
|
{
|
|
|
|
my $fSecond = shift;
|
|
|
|
|
|
|
|
return usleep($fSecond * 1000000);
|
|
|
|
}
|
|
|
|
|
2014-02-14 04:29:42 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# WAIT_FOR_FILE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub wait_for_file
|
|
|
|
{
|
|
|
|
my $strDir = shift;
|
|
|
|
my $strRegEx = shift;
|
|
|
|
my $iSeconds = shift;
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-02-14 04:29:42 +03:00
|
|
|
my $lTime = time();
|
|
|
|
my $hDir;
|
|
|
|
|
|
|
|
while ($lTime > time() - $iSeconds)
|
|
|
|
{
|
2015-01-28 07:26:20 +02:00
|
|
|
if (opendir($hDir, $strDir))
|
2014-02-14 04:29:42 +03:00
|
|
|
{
|
2015-01-28 07:26:20 +02:00
|
|
|
my @stryFile = grep(/$strRegEx/i, readdir $hDir);
|
|
|
|
close $hDir;
|
|
|
|
|
|
|
|
if (scalar @stryFile == 1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2014-02-14 04:29:42 +03:00
|
|
|
}
|
|
|
|
|
2015-01-25 19:20:12 +02:00
|
|
|
hsleep(.1);
|
2014-02-14 04:29:42 +03:00
|
|
|
}
|
|
|
|
|
2014-09-16 15:21:29 +03:00
|
|
|
confess &log(ERROR, "could not find $strDir/$strRegEx after ${iSeconds} second(s)");
|
2014-02-14 04:29:42 +03:00
|
|
|
}
|
|
|
|
|
2014-02-05 18:40:49 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# COMMON_PREFIX
|
|
|
|
####################################################################################################################################
|
|
|
|
sub common_prefix
|
|
|
|
{
|
|
|
|
my $strString1 = shift;
|
|
|
|
my $strString2 = shift;
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-02-05 18:40:49 +03:00
|
|
|
my $iCommonLen = 0;
|
|
|
|
my $iCompareLen = length($strString1) < length($strString2) ? length($strString1) : length($strString2);
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-02-05 18:40:49 +03:00
|
|
|
for (my $iIndex = 0; $iIndex < $iCompareLen; $iIndex++)
|
|
|
|
{
|
|
|
|
if (substr($strString1, $iIndex, 1) ne substr($strString2, $iIndex, 1))
|
|
|
|
{
|
|
|
|
last;
|
|
|
|
}
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-08-13 03:00:52 +03:00
|
|
|
$iCommonLen++;
|
2014-02-05 18:40:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return $iCommonLen;
|
|
|
|
}
|
|
|
|
|
2014-02-13 21:54:43 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# FILE_SIZE_FORMAT - Format file sizes in human-readable form
|
|
|
|
####################################################################################################################################
|
|
|
|
sub file_size_format
|
|
|
|
{
|
|
|
|
my $lFileSize = shift;
|
|
|
|
|
|
|
|
if ($lFileSize < 1024)
|
|
|
|
{
|
2014-09-16 15:21:29 +03:00
|
|
|
return $lFileSize . 'B';
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($lFileSize < (1024 * 1024))
|
|
|
|
{
|
2015-04-11 18:31:39 +02:00
|
|
|
return (int($lFileSize / 102.4) / 10) . 'KB';
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($lFileSize < (1024 * 1024 * 1024))
|
|
|
|
{
|
2015-04-11 18:31:39 +02:00
|
|
|
return (int($lFileSize / 1024 / 102.4) / 10) . 'MB';
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
|
|
|
|
2015-04-11 18:31:39 +02:00
|
|
|
return (int($lFileSize / 1024 / 1024 / 102.4) / 10) . 'GB';
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-08-13 03:00:52 +03:00
|
|
|
# TIMESTAMP_STRING_GET - Get backrest standard timestamp (or formatted as specified
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-08-13 03:00:52 +03:00
|
|
|
sub timestamp_string_get
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
my $strFormat = shift;
|
2015-01-03 23:49:26 +02:00
|
|
|
my $lTime = shift;
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-02-13 21:54:43 +03:00
|
|
|
if (!defined($strFormat))
|
|
|
|
{
|
2014-09-16 15:21:29 +03:00
|
|
|
$strFormat = '%4d-%02d-%02d %02d:%02d:%02d';
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2015-01-03 23:49:26 +02:00
|
|
|
if (!defined($lTime))
|
|
|
|
{
|
|
|
|
$lTime = time();
|
|
|
|
}
|
|
|
|
|
|
|
|
my ($iSecond, $iMinute, $iHour, $iMonthDay, $iMonth, $iYear, $iWeekDay, $iYearDay, $bIsDst) = localtime($lTime);
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-08-13 03:00:52 +03:00
|
|
|
return sprintf($strFormat, $iYear + 1900, $iMonth + 1, $iMonthDay, $iHour, $iMinute, $iSecond);
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# TIMESTAMP_FILE_STRING_GET - Get the date and time string formatted for filenames
|
|
|
|
####################################################################################################################################
|
|
|
|
sub timestamp_file_string_get
|
|
|
|
{
|
2014-09-16 15:21:29 +03:00
|
|
|
return timestamp_string_get('%4d%02d%02d-%02d%02d%02d');
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# LOG_FILE_SET - set the file messages will be logged to
|
|
|
|
####################################################################################################################################
|
|
|
|
sub log_file_set
|
|
|
|
{
|
|
|
|
my $strFile = shift;
|
2014-06-24 01:54:00 +03:00
|
|
|
|
2014-06-22 21:51:28 +03:00
|
|
|
unless (-e dirname($strFile))
|
|
|
|
{
|
|
|
|
mkdir(dirname($strFile)) or die "unable to create directory for log file ${strFile}";
|
|
|
|
}
|
2014-02-14 01:11:53 +03:00
|
|
|
|
2014-09-16 15:21:29 +03:00
|
|
|
$strFile .= '-' . timestamp_string_get('%4d%02d%02d') . '.log';
|
2014-02-13 21:54:43 +03:00
|
|
|
my $bExists = false;
|
2014-02-14 01:11:53 +03:00
|
|
|
|
2014-02-13 21:54:43 +03:00
|
|
|
if (-e $strFile)
|
|
|
|
{
|
|
|
|
$bExists = true;
|
|
|
|
}
|
2014-02-14 01:11:53 +03:00
|
|
|
|
2014-02-13 21:54:43 +03:00
|
|
|
open($hLogFile, '>>', $strFile) or confess "unable to open log file ${strFile}";
|
2014-02-14 01:11:53 +03:00
|
|
|
|
2014-02-13 21:54:43 +03:00
|
|
|
if ($bExists)
|
|
|
|
{
|
|
|
|
print $hLogFile "\n";
|
|
|
|
}
|
|
|
|
|
2014-09-18 22:18:52 +03:00
|
|
|
print $hLogFile "-------------------PROCESS START-------------------\n";
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2014-07-28 01:13:23 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# TEST_SET - set test parameters
|
|
|
|
####################################################################################################################################
|
|
|
|
sub test_set
|
|
|
|
{
|
|
|
|
my $bTestParam = shift;
|
2015-01-25 19:20:12 +02:00
|
|
|
my $fTestDelayParam = shift;
|
2014-07-28 01:13:23 +03:00
|
|
|
|
2014-09-18 22:18:52 +03:00
|
|
|
# Set defaults
|
2014-07-28 01:13:23 +03:00
|
|
|
$bTest = defined($bTestParam) ? $bTestParam : false;
|
2015-01-25 19:20:12 +02:00
|
|
|
$fTestDelay = defined($bTestParam) ? $fTestDelayParam : $fTestDelay;
|
2014-07-28 01:13:23 +03:00
|
|
|
|
|
|
|
# Make sure that a delay is specified in test mode
|
2015-01-25 19:20:12 +02:00
|
|
|
if ($bTest && !defined($fTestDelay))
|
2014-07-28 01:13:23 +03:00
|
|
|
{
|
2014-09-16 15:21:29 +03:00
|
|
|
confess &log(ASSERT, 'iTestDelay must be provided when bTest is true');
|
2014-07-28 01:13:23 +03:00
|
|
|
}
|
2014-09-18 22:18:52 +03:00
|
|
|
|
|
|
|
# Test delay should be between 1 and 600 seconds
|
2015-01-25 19:20:12 +02:00
|
|
|
if (!($fTestDelay >= 0 && $fTestDelay <= 600))
|
2014-09-18 22:18:52 +03:00
|
|
|
{
|
|
|
|
confess &log(ERROR, 'test-delay must be between 1 and 600 seconds');
|
|
|
|
}
|
2014-07-28 01:13:23 +03:00
|
|
|
}
|
|
|
|
|
2015-01-24 01:28:39 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# TEST_GET - are we in test mode?
|
|
|
|
####################################################################################################################################
|
|
|
|
sub test_get
|
|
|
|
{
|
|
|
|
return $bTest;
|
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-02-14 01:11:53 +03:00
|
|
|
# LOG_LEVEL_SET - set the log level for file and console
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-02-14 01:11:53 +03:00
|
|
|
sub log_level_set
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-14 01:11:53 +03:00
|
|
|
my $strLevelFileParam = shift;
|
|
|
|
my $strLevelConsoleParam = shift;
|
|
|
|
|
2014-06-04 04:22:07 +03:00
|
|
|
if (defined($strLevelFileParam))
|
2014-02-14 01:11:53 +03:00
|
|
|
{
|
2015-03-12 18:15:19 +02:00
|
|
|
if (!defined($oLogLevelRank{uc($strLevelFileParam)}{rank}))
|
2014-06-04 04:22:07 +03:00
|
|
|
{
|
|
|
|
confess &log(ERROR, "file log level ${strLevelFileParam} does not exist");
|
|
|
|
}
|
|
|
|
|
2015-03-12 18:15:19 +02:00
|
|
|
$strLogLevelFile = uc($strLevelFileParam);
|
2014-02-14 01:11:53 +03:00
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-06-04 04:22:07 +03:00
|
|
|
if (defined($strLevelConsoleParam))
|
2014-02-14 01:11:53 +03:00
|
|
|
{
|
2015-03-12 18:15:19 +02:00
|
|
|
if (!defined($oLogLevelRank{uc($strLevelConsoleParam)}{rank}))
|
2014-06-04 04:22:07 +03:00
|
|
|
{
|
|
|
|
confess &log(ERROR, "console log level ${strLevelConsoleParam} does not exist");
|
|
|
|
}
|
2014-02-14 01:11:53 +03:00
|
|
|
|
2015-03-12 18:15:19 +02:00
|
|
|
$strLogLevelConsole = uc($strLevelConsoleParam);
|
2014-06-04 04:22:07 +03:00
|
|
|
}
|
2014-02-14 01:11:53 +03:00
|
|
|
}
|
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# TEST_CHECK - Check for a test message
|
|
|
|
####################################################################################################################################
|
|
|
|
sub test_check
|
|
|
|
{
|
|
|
|
my $strLog = shift;
|
|
|
|
my $strTest = shift;
|
|
|
|
|
|
|
|
return index($strLog, TEST_ENCLOSE . '-' . $strTest . '-' . TEST_ENCLOSE) != -1;
|
|
|
|
}
|
|
|
|
|
2014-02-14 01:11:53 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# LOG - log messages
|
|
|
|
####################################################################################################################################
|
2014-02-03 03:03:05 +03:00
|
|
|
sub log
|
|
|
|
{
|
|
|
|
my $strLevel = shift;
|
|
|
|
my $strMessage = shift;
|
2014-06-07 18:51:27 +03:00
|
|
|
my $iCode = shift;
|
2014-09-30 22:51:08 +03:00
|
|
|
my $bSuppressLog = shift;
|
2014-06-07 18:51:27 +03:00
|
|
|
|
2014-09-30 22:51:08 +03:00
|
|
|
# Set defaults
|
|
|
|
$bSuppressLog = defined($bSuppressLog) ? $bSuppressLog : false;
|
|
|
|
|
|
|
|
# Set operational variables
|
2014-06-07 18:51:27 +03:00
|
|
|
my $strMessageFormat = $strMessage;
|
2014-08-10 01:35:55 +03:00
|
|
|
my $iLogLevelRank = $oLogLevelRank{"${strLevel}"}{rank};
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
# If test message
|
2014-08-10 01:35:55 +03:00
|
|
|
if ($strLevel eq TEST)
|
2014-07-28 01:13:23 +03:00
|
|
|
{
|
2014-08-10 01:35:55 +03:00
|
|
|
$iLogLevelRank = $oLogLevelRank{TRACE}{rank} + 1;
|
2014-07-28 01:13:23 +03:00
|
|
|
$strMessageFormat = TEST_ENCLOSE . '-' . $strMessageFormat . '-' . TEST_ENCLOSE;
|
|
|
|
}
|
2014-08-10 22:02:14 +03:00
|
|
|
# Else level rank must be valid
|
2014-08-10 01:35:55 +03:00
|
|
|
elsif (!defined($iLogLevelRank))
|
2014-02-14 01:11:53 +03:00
|
|
|
{
|
|
|
|
confess &log(ASSERT, "log level ${strLevel} does not exist");
|
|
|
|
}
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
# If message was undefined then set default message
|
2014-06-07 18:51:27 +03:00
|
|
|
if (!defined($strMessageFormat))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-09-16 15:21:29 +03:00
|
|
|
$strMessageFormat = '(undefined)';
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2015-01-24 01:28:39 +02:00
|
|
|
$strMessageFormat = (defined($iCode) ? "[${iCode}] " : '') . $strMessageFormat;
|
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
# Indent subsequent lines of the message if it has more than one line - makes the log more readable
|
2014-08-10 01:35:55 +03:00
|
|
|
if ($strLevel eq TRACE || $strLevel eq TEST)
|
2014-02-13 21:54:43 +03:00
|
|
|
{
|
2014-06-07 22:30:13 +03:00
|
|
|
$strMessageFormat =~ s/\n/\n /g;
|
2014-09-16 15:21:29 +03:00
|
|
|
$strMessageFormat = ' ' . $strMessageFormat;
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
2014-08-10 01:35:55 +03:00
|
|
|
elsif ($strLevel eq DEBUG)
|
2014-02-13 21:54:43 +03:00
|
|
|
{
|
2014-06-07 22:30:13 +03:00
|
|
|
$strMessageFormat =~ s/\n/\n /g;
|
2014-09-16 15:21:29 +03:00
|
|
|
$strMessageFormat = ' ' . $strMessageFormat;
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
2014-06-07 22:30:13 +03:00
|
|
|
else
|
|
|
|
{
|
|
|
|
$strMessageFormat =~ s/\n/\n /g;
|
|
|
|
}
|
2014-02-13 21:54:43 +03:00
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
# Format the message text
|
|
|
|
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
|
|
|
|
|
2015-04-07 13:34:37 +02:00
|
|
|
$strMessageFormat = timestamp_string_get() . sprintf('.%03d T%02d', (gettimeofday() - int(gettimeofday())) * 1000,
|
|
|
|
threads->tid()) .
|
2015-01-24 01:28:39 +02:00
|
|
|
(' ' x (7 - length($strLevel))) . "${strLevel}: ${strMessageFormat}\n";
|
2014-02-13 21:54:43 +03:00
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
# Output to console depending on log level and test flag
|
2014-08-10 01:35:55 +03:00
|
|
|
if ($iLogLevelRank <= $oLogLevelRank{"${strLogLevelConsole}"}{rank} ||
|
2014-07-28 01:13:23 +03:00
|
|
|
$bTest && $strLevel eq TEST)
|
2014-02-13 21:54:43 +03:00
|
|
|
{
|
2014-09-30 22:51:08 +03:00
|
|
|
if (!$bSuppressLog)
|
|
|
|
{
|
|
|
|
print $strMessageFormat;
|
|
|
|
}
|
2014-08-10 22:02:14 +03:00
|
|
|
|
|
|
|
if ($bTest && $strLevel eq TEST)
|
|
|
|
{
|
|
|
|
*STDOUT->flush();
|
2015-01-22 17:54:02 +02:00
|
|
|
|
2015-01-25 19:20:12 +02:00
|
|
|
if ($fTestDelay > 0)
|
2015-01-22 17:54:02 +02:00
|
|
|
{
|
2015-01-25 19:20:12 +02:00
|
|
|
hsleep($fTestDelay);
|
2015-01-22 17:54:02 +02:00
|
|
|
}
|
2014-08-10 22:02:14 +03:00
|
|
|
}
|
2014-02-14 01:11:53 +03:00
|
|
|
}
|
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
# Output to file depending on log level and test flag
|
|
|
|
if ($iLogLevelRank <= $oLogLevelRank{"${strLogLevelFile}"}{rank})
|
2014-02-14 01:11:53 +03:00
|
|
|
{
|
|
|
|
if (defined($hLogFile))
|
|
|
|
{
|
2014-09-30 22:51:08 +03:00
|
|
|
if (!$bSuppressLog)
|
|
|
|
{
|
|
|
|
print $hLogFile $strMessageFormat;
|
2015-01-24 01:28:39 +02:00
|
|
|
|
|
|
|
if ($strLevel eq ERROR || $strLevel eq ASSERT)
|
|
|
|
{
|
|
|
|
my $strStackTrace = longmess() . "\n";
|
|
|
|
$strStackTrace =~ s/\n/\n /g;
|
|
|
|
|
|
|
|
print $hLogFile $strStackTrace;
|
|
|
|
}
|
2014-09-30 22:51:08 +03:00
|
|
|
}
|
2014-02-14 01:11:53 +03:00
|
|
|
}
|
2014-02-13 21:54:43 +03:00
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
# Throw a typed exception if code is defined
|
2014-06-07 18:51:27 +03:00
|
|
|
if (defined($iCode))
|
|
|
|
{
|
2014-10-09 23:01:06 +03:00
|
|
|
return new BackRest::Exception($iCode, $strMessage);
|
2014-06-07 18:51:27 +03:00
|
|
|
}
|
|
|
|
|
2014-08-10 22:02:14 +03:00
|
|
|
# Return the message test so it can be used in a confess
|
2014-02-03 03:03:05 +03:00
|
|
|
return $strMessage;
|
|
|
|
}
|
|
|
|
|
2014-07-27 21:03:21 +03:00
|
|
|
####################################################################################################################################
|
2014-12-16 00:20:42 +02:00
|
|
|
# INI_LOAD
|
2014-09-18 23:41:36 +03:00
|
|
|
#
|
2014-12-16 00:20:42 +02:00
|
|
|
# Load file from standard INI format to a hash.
|
2014-07-27 21:03:21 +03:00
|
|
|
####################################################################################################################################
|
2014-12-16 00:20:42 +02:00
|
|
|
sub ini_load
|
2014-07-27 21:03:21 +03:00
|
|
|
{
|
2014-12-16 00:20:42 +02:00
|
|
|
my $strFile = shift; # Full path to ini file to load from
|
|
|
|
my $oConfig = shift; # Reference to the hash where ini data will be stored
|
2014-07-27 21:03:21 +03:00
|
|
|
|
2014-12-16 00:20:42 +02:00
|
|
|
# Open the ini file for reading
|
2014-07-27 21:03:21 +03:00
|
|
|
my $hFile;
|
|
|
|
my $strSection;
|
|
|
|
|
|
|
|
open($hFile, '<', $strFile)
|
|
|
|
or confess &log(ERROR, "unable to open ${strFile}");
|
|
|
|
|
|
|
|
while (my $strLine = readline($hFile))
|
|
|
|
{
|
|
|
|
$strLine = trim($strLine);
|
|
|
|
|
|
|
|
if ($strLine ne '')
|
|
|
|
{
|
|
|
|
# Get the section
|
|
|
|
if (index($strLine, '[') == 0)
|
|
|
|
{
|
|
|
|
$strSection = substr($strLine, 1, length($strLine) - 2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Get key and value
|
|
|
|
my $iIndex = index($strLine, '=');
|
|
|
|
|
|
|
|
if ($iIndex == -1)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable to read from ${strFile}: ${strLine}");
|
|
|
|
}
|
|
|
|
|
|
|
|
my $strKey = substr($strLine, 0, $iIndex);
|
|
|
|
my $strValue = substr($strLine, $iIndex + 1);
|
|
|
|
|
|
|
|
# Try to store value as JSON
|
|
|
|
eval
|
|
|
|
{
|
|
|
|
${$oConfig}{"${strSection}"}{"${strKey}"} = decode_json($strValue);
|
|
|
|
};
|
|
|
|
|
|
|
|
# On error store value as a scalar
|
|
|
|
if ($@)
|
|
|
|
{
|
|
|
|
${$oConfig}{"${strSection}"}{"${strKey}"} = $strValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
close($hFile);
|
2015-03-12 18:15:19 +02:00
|
|
|
|
|
|
|
return($oConfig);
|
2014-07-27 21:03:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2014-12-16 00:20:42 +02:00
|
|
|
# INI_SAVE
|
2014-09-18 23:41:36 +03:00
|
|
|
#
|
2014-12-16 00:20:42 +02:00
|
|
|
# Save from a hash to standard INI format.
|
2014-07-27 21:03:21 +03:00
|
|
|
####################################################################################################################################
|
2014-12-16 00:20:42 +02:00
|
|
|
sub ini_save
|
2014-07-27 21:03:21 +03:00
|
|
|
{
|
2014-12-16 00:20:42 +02:00
|
|
|
my $strFile = shift; # Full path to ini file to save to
|
|
|
|
my $oConfig = shift; # Reference to the hash where ini data is stored
|
2014-07-27 21:03:21 +03:00
|
|
|
|
2014-12-16 00:20:42 +02:00
|
|
|
# Open the ini file for writing
|
2014-07-27 21:03:21 +03:00
|
|
|
my $hFile;
|
|
|
|
my $bFirst = true;
|
|
|
|
|
2014-09-18 23:41:36 +03:00
|
|
|
open($hFile, '>', $strFile)
|
|
|
|
or confess &log(ERROR, "unable to open ${strFile}");
|
2014-07-27 21:03:21 +03:00
|
|
|
|
|
|
|
foreach my $strSection (sort(keys $oConfig))
|
|
|
|
{
|
|
|
|
if (!$bFirst)
|
|
|
|
{
|
|
|
|
syswrite($hFile, "\n")
|
|
|
|
or confess "unable to write lf: $!";
|
|
|
|
}
|
|
|
|
|
|
|
|
syswrite($hFile, "[${strSection}]\n")
|
|
|
|
or confess "unable to write section ${strSection}: $!";
|
|
|
|
|
|
|
|
foreach my $strKey (sort(keys ${$oConfig}{"${strSection}"}))
|
|
|
|
{
|
|
|
|
my $strValue = ${$oConfig}{"${strSection}"}{"${strKey}"};
|
|
|
|
|
|
|
|
if (defined($strValue))
|
|
|
|
{
|
|
|
|
if (ref($strValue) eq "HASH")
|
|
|
|
{
|
2014-10-18 20:25:20 +03:00
|
|
|
syswrite($hFile, "${strKey}=" . to_json($strValue, {canonical => true}) . "\n")
|
2014-07-27 21:03:21 +03:00
|
|
|
or confess "unable to write key ${strKey}: $!";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
syswrite($hFile, "${strKey}=${strValue}\n")
|
|
|
|
or confess "unable to write key ${strKey}: $!";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$bFirst = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
close($hFile);
|
|
|
|
}
|
|
|
|
|
2014-06-04 18:58:30 +03:00
|
|
|
1;
|