2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# FILE MODULE
|
|
|
|
####################################################################################################################################
|
|
|
|
package pg_backrest_file;
|
|
|
|
|
2014-02-21 15:34:17 +03:00
|
|
|
use threads;
|
2014-02-03 03:03:05 +03:00
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use Carp;
|
2014-06-02 23:26:37 +03:00
|
|
|
|
|
|
|
use Moose;
|
2014-02-03 03:03:05 +03:00
|
|
|
use Net::OpenSSH;
|
|
|
|
use IPC::Open3;
|
|
|
|
use File::Basename;
|
|
|
|
use IPC::System::Simple qw(capture);
|
2014-06-02 00:23:33 +03:00
|
|
|
use Digest::SHA;
|
2014-06-02 23:26:37 +03:00
|
|
|
use File::stat;
|
|
|
|
use Fcntl ':mode';
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
use lib dirname($0);
|
|
|
|
use pg_backrest_utility;
|
|
|
|
|
|
|
|
use Exporter qw(import);
|
2014-06-02 00:23:33 +03:00
|
|
|
our @EXPORT = qw(PATH_ABSOLUTE PATH_DB PATH_DB_ABSOLUTE PATH_BACKUP PATH_BACKUP_ABSOLUTE PATH_BACKUP_CLUSTER PATH_BACKUP_TMP PATH_BACKUP_ARCHIVE);
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Extension and permissions
|
2014-02-06 05:39:08 +03:00
|
|
|
has strCompressExtension => (is => 'ro', default => 'gz');
|
|
|
|
has strDefaultPathPermission => (is => 'bare', default => '0750');
|
|
|
|
has strDefaultFilePermission => (is => 'ro', default => '0640');
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Command strings
|
2014-06-02 00:23:33 +03:00
|
|
|
has strCommand => (is => 'bare');
|
2014-02-06 05:39:08 +03:00
|
|
|
has strCommandChecksum => (is => 'bare');
|
|
|
|
has strCommandCompress => (is => 'bare');
|
|
|
|
has strCommandDecompress => (is => 'bare');
|
|
|
|
has strCommandCat => (is => 'bare', default => 'cat %file%');
|
|
|
|
has strCommandManifest => (is => 'bare');
|
2014-02-04 04:48:02 +03:00
|
|
|
|
2014-04-28 16:13:25 +03:00
|
|
|
# Lock path
|
|
|
|
has strLockPath => (is => 'bare');
|
|
|
|
|
|
|
|
# Files to hold stderr
|
|
|
|
#has strBackupStdErrFile => (is => 'bare');
|
|
|
|
#has strDbStdErrFile => (is => 'bare');
|
|
|
|
|
2014-02-04 04:48:02 +03:00
|
|
|
# Module variables
|
2014-02-06 05:39:08 +03:00
|
|
|
has strDbUser => (is => 'bare'); # Database user
|
|
|
|
has strDbHost => (is => 'bare'); # Database host
|
|
|
|
has oDbSSH => (is => 'bare'); # Database SSH object
|
2014-02-04 04:48:02 +03:00
|
|
|
|
2014-02-06 05:39:08 +03:00
|
|
|
has strBackupUser => (is => 'bare'); # Backup user
|
|
|
|
has strBackupHost => (is => 'bare'); # Backup host
|
|
|
|
has oBackupSSH => (is => 'bare'); # Backup SSH object
|
|
|
|
has strBackupPath => (is => 'bare'); # Backup base path
|
|
|
|
has strBackupClusterPath => (is => 'bare'); # Backup cluster path
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Process flags
|
2014-02-06 05:39:08 +03:00
|
|
|
has bNoCompression => (is => 'bare');
|
2014-02-06 20:49:54 +03:00
|
|
|
has strStanza => (is => 'bare');
|
2014-02-11 23:31:16 +03:00
|
|
|
has iThreadIdx => (is => 'bare');
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# COMMAND Error Constants
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant
|
|
|
|
{
|
2014-06-02 23:26:37 +03:00
|
|
|
COMMAND_ERR_FILE_MISSING => 1,
|
|
|
|
COMMAND_ERR_FILE_READ => 2,
|
|
|
|
COMMAND_ERR_FILE_TYPE => 3,
|
|
|
|
COMMAND_ERR_LINK_READ => 4
|
2014-06-02 00:23:33 +03:00
|
|
|
};
|
|
|
|
|
2014-04-28 16:13:25 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# PATH_GET Constants
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant
|
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
PATH_ABSOLUTE => 'absolute',
|
2014-04-28 16:13:25 +03:00
|
|
|
PATH_DB => 'db',
|
|
|
|
PATH_DB_ABSOLUTE => 'db:absolute',
|
|
|
|
PATH_BACKUP => 'backup',
|
|
|
|
PATH_BACKUP_ABSOLUTE => 'backup:absolute',
|
|
|
|
PATH_BACKUP_CLUSTER => 'backup:cluster',
|
|
|
|
PATH_BACKUP_TMP => 'backup:tmp',
|
|
|
|
PATH_BACKUP_ARCHIVE => 'backup:archive',
|
|
|
|
PATH_LOCK_ERR => 'lock:err'
|
|
|
|
};
|
|
|
|
|
2014-02-07 00:37:37 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# CONSTRUCTOR
|
|
|
|
####################################################################################################################################
|
2014-02-06 06:26:10 +03:00
|
|
|
sub BUILD
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 22:50:23 +03:00
|
|
|
|
|
|
|
# Make sure the backup path is defined
|
2014-06-02 00:23:33 +03:00
|
|
|
if (defined($self->{strBackupPath}))
|
2014-02-03 22:50:23 +03:00
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
# Create the backup cluster path
|
|
|
|
$self->{strBackupClusterPath} = $self->{strBackupPath} . "/" . $self->{strStanza};
|
2014-02-03 22:50:23 +03:00
|
|
|
}
|
2014-02-06 05:39:08 +03:00
|
|
|
|
2014-02-17 03:01:06 +03:00
|
|
|
# Create the ssh options string
|
|
|
|
if (defined($self->{strBackupHost}) || defined($self->{strDbHost}))
|
2014-02-03 22:50:23 +03:00
|
|
|
{
|
2014-05-27 16:00:24 +03:00
|
|
|
# if (defined($self->{strBackupHost}) && defined($self->{strDbHost}))
|
|
|
|
# {
|
|
|
|
# confess &log(ASSERT, "backup and db hosts cannot both be remote");
|
|
|
|
# }
|
|
|
|
|
2014-04-01 15:59:09 +03:00
|
|
|
my $strOptionSSHRequestTTY = "RequestTTY=yes";
|
|
|
|
my $strOptionSSHCompression = "Compression=no";
|
2014-02-17 03:01:06 +03:00
|
|
|
|
|
|
|
if ($self->{bNoCompression})
|
|
|
|
{
|
2014-04-01 15:59:09 +03:00
|
|
|
$strOptionSSHCompression = "Compression=yes";
|
2014-02-17 03:01:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
# Connect SSH object if backup host is defined
|
2014-02-19 21:03:52 +03:00
|
|
|
if (!defined($self->{oBackupSSH}) && defined($self->{strBackupHost}))
|
2014-02-17 03:01:06 +03:00
|
|
|
{
|
|
|
|
&log(TRACE, "connecting to backup ssh host " . $self->{strBackupHost});
|
2014-04-28 16:13:25 +03:00
|
|
|
|
2014-04-01 15:59:09 +03:00
|
|
|
$self->{oBackupSSH} = Net::OpenSSH->new($self->{strBackupHost}, timeout => 300, user => $self->{strBackupUser},
|
2014-05-27 16:00:24 +03:00
|
|
|
# default_stderr_file => $self->path_get(PATH_LOCK_ERR, "file"),
|
2014-04-01 15:59:09 +03:00
|
|
|
master_opts => [-o => $strOptionSSHCompression, -o => $strOptionSSHRequestTTY]);
|
2014-02-17 03:01:06 +03:00
|
|
|
$self->{oBackupSSH}->error and confess &log(ERROR, "unable to connect to $self->{strBackupHost}: " . $self->{oBackupSSH}->error);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Connect SSH object if db host is defined
|
2014-02-19 21:03:52 +03:00
|
|
|
if (!defined($self->{oDbSSH}) && defined($self->{strDbHost}))
|
2014-02-17 03:01:06 +03:00
|
|
|
{
|
|
|
|
&log(TRACE, "connecting to database ssh host $self->{strDbHost}");
|
|
|
|
|
2014-04-01 15:59:09 +03:00
|
|
|
$self->{oDbSSH} = Net::OpenSSH->new($self->{strDbHost}, timeout => 300, user => $self->{strDbUser},
|
2014-05-27 16:00:24 +03:00
|
|
|
# default_stderr_file => $self->path_get(PATH_LOCK_ERR, "file"),
|
2014-04-01 15:59:09 +03:00
|
|
|
master_opts => [-o => $strOptionSSHCompression, -o => $strOptionSSHRequestTTY]);
|
2014-02-17 03:01:06 +03:00
|
|
|
$self->{oDbSSH}->error and confess &log(ERROR, "unable to connect to $self->{strDbHost}: " . $self->{oDbSSH}->error);
|
|
|
|
}
|
2014-02-03 22:50:23 +03:00
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2014-02-11 23:31:16 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# CLONE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub clone
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $iThreadIdx = shift;
|
|
|
|
|
|
|
|
return pg_backrest_file->new
|
|
|
|
(
|
|
|
|
strCompressExtension => $self->{strCompressExtension},
|
|
|
|
strDefaultPathPermission => $self->{strDefaultPathPermission},
|
|
|
|
strDefaultFilePermission => $self->{strDefaultFilePermission},
|
2014-06-02 00:23:33 +03:00
|
|
|
strCommand => $self->{strCommand},
|
2014-02-11 23:31:16 +03:00
|
|
|
strCommandChecksum => $self->{strCommandChecksum},
|
|
|
|
strCommandCompress => $self->{strCommandCompress},
|
|
|
|
strCommandDecompress => $self->{strCommandDecompress},
|
|
|
|
strCommandCat => $self->{strCommandCat},
|
|
|
|
strCommandManifest => $self->{strCommandManifest},
|
|
|
|
strDbUser => $self->{strDbUser},
|
|
|
|
strDbHost => $self->{strDbHost},
|
|
|
|
strBackupUser => $self->{strBackupUser},
|
|
|
|
strBackupHost => $self->{strBackupHost},
|
|
|
|
strBackupPath => $self->{strBackupPath},
|
|
|
|
strBackupClusterPath => $self->{strBackupClusterPath},
|
|
|
|
bNoCompression => $self->{bNoCompression},
|
|
|
|
strStanza => $self->{strStanza},
|
2014-04-28 16:13:25 +03:00
|
|
|
iThreadIdx => $iThreadIdx,
|
|
|
|
strLockPath => $self->{strLockPath}
|
2014-02-11 23:31:16 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-04-28 16:13:25 +03:00
|
|
|
# ERROR_GET
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-04-28 16:13:25 +03:00
|
|
|
sub error_get
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-04-28 16:13:25 +03:00
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
my $strErrorFile = $self->path_get(PATH_LOCK_ERR, "file");
|
|
|
|
|
|
|
|
open my $hFile, '<', $strErrorFile or return "error opening ${strErrorFile} to read STDERR output";
|
|
|
|
|
|
|
|
my $strError = do {local $/; <$hFile>};
|
|
|
|
close $hFile;
|
|
|
|
|
|
|
|
return trim($strError);
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-05-27 16:00:24 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# ERROR_CLEAR
|
|
|
|
####################################################################################################################################
|
|
|
|
sub error_clear
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
unlink($self->path_get(PATH_LOCK_ERR, "file"));
|
|
|
|
}
|
|
|
|
|
2014-04-28 16:13:25 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# PATH_GET
|
|
|
|
####################################################################################################################################
|
2014-02-03 03:03:05 +03:00
|
|
|
sub path_type_get
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strType = shift;
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
# If db type
|
|
|
|
if ($strType =~ /^db(\:.*){0,1}/)
|
|
|
|
{
|
|
|
|
return PATH_DB;
|
|
|
|
}
|
|
|
|
# Else if backup type
|
|
|
|
elsif ($strType =~ /^backup(\:.*){0,1}/)
|
|
|
|
{
|
|
|
|
return PATH_BACKUP;
|
|
|
|
}
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
# Error when path type not recognized
|
|
|
|
confess &log(ASSERT, "no known path types in '${strType}'");
|
|
|
|
}
|
|
|
|
|
|
|
|
sub path_get
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-05 23:56:05 +03:00
|
|
|
my $strType = shift; # Base type of the path to get (PATH_DB_ABSOLUTE, PATH_BACKUP_TMP, etc)
|
|
|
|
my $strFile = shift; # File to append to the base path (can include a path as well)
|
|
|
|
my $bTemp = shift; # Return the temp file for this path type - only some types have temp files
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
# Only allow temp files for PATH_BACKUP_ARCHIVE and PATH_BACKUP_TMP
|
2014-05-14 22:07:37 +03:00
|
|
|
if (defined($bTemp) && $bTemp && !($strType eq PATH_BACKUP_ARCHIVE || $strType eq PATH_BACKUP_TMP ||
|
|
|
|
$strType eq PATH_DB_ABSOLUTE || $strType eq PATH_BACKUP_ABSOLUTE))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
|
|
|
confess &log(ASSERT, "temp file not supported on path " . $strType);
|
|
|
|
}
|
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
# Get absolute path
|
|
|
|
if ($strType eq PATH_ABSOLUTE)
|
|
|
|
{
|
|
|
|
return $strFile;
|
|
|
|
}
|
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
# Get absolute db path
|
2014-02-03 03:03:05 +03:00
|
|
|
if ($strType eq PATH_DB_ABSOLUTE)
|
|
|
|
{
|
2014-03-28 22:20:36 +03:00
|
|
|
if (defined($bTemp) && $bTemp)
|
|
|
|
{
|
|
|
|
return $strFile . ".backrest.tmp";
|
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
return $strFile;
|
|
|
|
}
|
2014-02-05 23:56:05 +03:00
|
|
|
|
|
|
|
# Make sure the base backup path is defined
|
2014-02-06 05:39:08 +03:00
|
|
|
if (!defined($self->{strBackupPath}))
|
2014-02-05 21:10:36 +03:00
|
|
|
{
|
2014-02-05 23:56:05 +03:00
|
|
|
confess &log(ASSERT, "\$strBackupPath not yet defined");
|
2014-02-05 21:10:36 +03:00
|
|
|
}
|
2014-02-05 23:56:05 +03:00
|
|
|
|
|
|
|
# Get absolute backup path
|
|
|
|
if ($strType eq PATH_BACKUP_ABSOLUTE)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-05-14 22:07:37 +03:00
|
|
|
if (defined($bTemp) && $bTemp)
|
|
|
|
{
|
|
|
|
return "${strFile}.backrest.tmp";
|
|
|
|
}
|
2014-02-04 03:03:17 +03:00
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
return $strFile;
|
|
|
|
}
|
2014-02-05 21:10:36 +03:00
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
# Get base backup path
|
|
|
|
if ($strType eq PATH_BACKUP)
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
return $self->{strBackupPath} . (defined($strFile) ? "/${strFile}" : "");
|
2014-02-05 23:56:05 +03:00
|
|
|
}
|
2014-02-05 21:10:36 +03:00
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
# Make sure the cluster is defined
|
2014-02-06 20:49:54 +03:00
|
|
|
if (!defined($self->{strStanza}))
|
2014-02-05 23:56:05 +03:00
|
|
|
{
|
2014-02-06 20:49:54 +03:00
|
|
|
confess &log(ASSERT, "\$strStanza not yet defined");
|
2014-02-05 23:56:05 +03:00
|
|
|
}
|
2014-02-05 21:10:36 +03:00
|
|
|
|
2014-05-13 18:23:15 +03:00
|
|
|
# Get the lock error path
|
2014-04-28 16:13:25 +03:00
|
|
|
if ($strType eq PATH_LOCK_ERR)
|
|
|
|
{
|
2014-05-27 16:00:24 +03:00
|
|
|
my $strTempPath = $self->{strLockPath};
|
|
|
|
|
|
|
|
if (!defined($strTempPath))
|
|
|
|
{
|
|
|
|
return undef;
|
|
|
|
}
|
2014-04-28 16:13:25 +03:00
|
|
|
|
|
|
|
return ${strTempPath} . (defined($strFile) ? "/${strFile}" .
|
|
|
|
(defined($self->{iThreadIdx}) ? ".$self->{iThreadIdx}" : "") . ".err" : "");
|
|
|
|
}
|
|
|
|
|
2014-05-13 18:23:15 +03:00
|
|
|
# Get the backup tmp path
|
2014-02-05 23:56:05 +03:00
|
|
|
if ($strType eq PATH_BACKUP_TMP)
|
|
|
|
{
|
2014-02-11 23:31:16 +03:00
|
|
|
my $strTempPath = "$self->{strBackupPath}/temp/$self->{strStanza}.tmp";
|
2014-02-05 21:10:36 +03:00
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
if (defined($bTemp) && $bTemp)
|
|
|
|
{
|
2014-02-11 23:31:16 +03:00
|
|
|
return "${strTempPath}/file.tmp" . (defined($self->{iThreadIdx}) ? ".$self->{iThreadIdx}" : "");
|
2014-02-05 21:10:36 +03:00
|
|
|
}
|
2014-02-05 23:56:05 +03:00
|
|
|
|
|
|
|
return "${strTempPath}" . (defined($strFile) ? "/${strFile}" : "");
|
2014-02-04 03:03:17 +03:00
|
|
|
}
|
2014-02-05 23:56:05 +03:00
|
|
|
|
|
|
|
# Get the backup archive path
|
|
|
|
if ($strType eq PATH_BACKUP_ARCHIVE)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 20:49:54 +03:00
|
|
|
my $strArchivePath = "$self->{strBackupPath}/archive/$self->{strStanza}";
|
2014-02-05 23:56:05 +03:00
|
|
|
my $strArchive;
|
2014-02-04 03:03:17 +03:00
|
|
|
|
2014-02-13 23:26:07 +03:00
|
|
|
if (defined($bTemp) && $bTemp)
|
|
|
|
{
|
|
|
|
return "${strArchivePath}/file.tmp" . (defined($self->{iThreadIdx}) ? ".$self->{iThreadIdx}" : "");
|
|
|
|
}
|
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
if (defined($strFile))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-05 23:56:05 +03:00
|
|
|
$strArchive = substr(basename($strFile), 0, 24);
|
|
|
|
|
|
|
|
if ($strArchive !~ /^([0-F]){24}$/)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-05 23:56:05 +03:00
|
|
|
return "${strArchivePath}/${strFile}";
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
2014-02-05 23:56:05 +03:00
|
|
|
}
|
2014-02-04 03:03:17 +03:00
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
return $strArchivePath . (defined($strArchive) ? "/" . substr($strArchive, 0, 16) : "") .
|
|
|
|
(defined($strFile) ? "/" . $strFile : "");
|
|
|
|
}
|
2014-02-04 03:03:17 +03:00
|
|
|
|
2014-02-05 23:56:05 +03:00
|
|
|
if ($strType eq PATH_BACKUP_CLUSTER)
|
|
|
|
{
|
2014-02-06 20:49:54 +03:00
|
|
|
return $self->{strBackupPath} . "/backup/$self->{strStanza}" . (defined($strFile) ? "/${strFile}" : "");
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
# Error when path type not recognized
|
|
|
|
confess &log(ASSERT, "no known path types in '${strType}'");
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# LINK_CREATE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub link_create
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strSourcePathType = shift;
|
|
|
|
my $strSourceFile = shift;
|
|
|
|
my $strDestinationPathType = shift;
|
|
|
|
my $strDestinationFile = shift;
|
|
|
|
my $bHard = shift;
|
|
|
|
my $bRelative = shift;
|
2014-02-14 17:05:14 +03:00
|
|
|
my $bPathCreate = shift;
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-05 18:40:49 +03:00
|
|
|
# if bHard is not defined default to false
|
|
|
|
$bHard = defined($bHard) ? $bHard : false;
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-05 18:40:49 +03:00
|
|
|
# if bRelative is not defined or bHard is true, default to false
|
|
|
|
$bRelative = !defined($bRelative) || $bHard ? false : $bRelative;
|
2014-02-14 17:05:14 +03:00
|
|
|
|
|
|
|
# if bPathCreate is not defined, default to true
|
|
|
|
$bPathCreate = defined($bPathCreate) ? $bPathCreate : true;
|
2014-02-05 18:40:49 +03:00
|
|
|
|
|
|
|
# Source and destination path types must be the same (both PATH_DB or both PATH_BACKUP)
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->path_type_get($strSourcePathType) ne $self->path_type_get($strDestinationPathType))
|
2014-02-04 03:03:17 +03:00
|
|
|
{
|
|
|
|
confess &log(ASSERT, "path types must be equal in link create");
|
|
|
|
}
|
|
|
|
|
2014-02-05 18:40:49 +03:00
|
|
|
# Generate source and destination files
|
2014-02-06 05:39:08 +03:00
|
|
|
my $strSource = $self->path_get($strSourcePathType, $strSourceFile);
|
|
|
|
my $strDestination = $self->path_get($strDestinationPathType, $strDestinationFile);
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-02-04 03:03:17 +03:00
|
|
|
# If the destination path is backup and does not exist, create it
|
2014-02-14 17:05:14 +03:00
|
|
|
if ($bPathCreate && $self->path_type_get($strDestinationPathType) eq PATH_BACKUP)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$self->path_create(PATH_BACKUP_ABSOLUTE, dirname($strDestination));
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
unless (-e $strSource)
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
if (-e $strSource . ".$self->{strCompressExtension}")
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$strSource .= ".$self->{strCompressExtension}";
|
|
|
|
$strDestination .= ".$self->{strCompressExtension}";
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-02-05 18:40:49 +03:00
|
|
|
# Error when a hardlink will be created on a missing file
|
|
|
|
if ($bHard)
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
confess &log(ASSERT, "unable to find ${strSource}(.$self->{strCompressExtension}) for link");
|
2014-02-05 18:40:49 +03:00
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
}
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-05 18:40:49 +03:00
|
|
|
# Generate relative path if requested
|
|
|
|
if ($bRelative)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-05 18:40:49 +03:00
|
|
|
my $iCommonLen = common_prefix($strSource, $strDestination);
|
|
|
|
|
|
|
|
if ($iCommonLen != 0)
|
|
|
|
{
|
|
|
|
$strSource = ("../" x substr($strDestination, $iCommonLen) =~ tr/\///) . substr($strSource, $iCommonLen);
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
2014-02-05 18:40:49 +03:00
|
|
|
|
|
|
|
# Create the command
|
|
|
|
my $strCommand = "ln" . (!$bHard ? " -s" : "") . " ${strSource} ${strDestination}";
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-04 03:03:17 +03:00
|
|
|
# Run remotely
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->is_remote($strSourcePathType))
|
2014-02-04 03:03:17 +03:00
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "link_create: remote ${strSourcePathType} '${strCommand}'");
|
2014-02-04 03:03:17 +03:00
|
|
|
|
2014-02-06 05:39:08 +03:00
|
|
|
my $oSSH = $self->remote_get($strSourcePathType);
|
2014-02-04 03:03:17 +03:00
|
|
|
$oSSH->system($strCommand) or confess &log("unable to create link from ${strSource} to ${strDestination}");
|
|
|
|
}
|
|
|
|
# Run locally
|
|
|
|
else
|
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "link_create: local '${strCommand}'");
|
2014-02-04 03:03:17 +03:00
|
|
|
system($strCommand) == 0 or confess &log("unable to create link from ${strSource} to ${strDestination}");
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# PATH_CREATE
|
2014-02-04 03:03:17 +03:00
|
|
|
#
|
|
|
|
# Creates a path locally or remotely. Currently does not error if the path already exists. Also does not set permissions if the
|
|
|
|
# path aleady exists.
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
sub path_create
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strPathType = shift;
|
|
|
|
my $strPath = shift;
|
|
|
|
my $strPermission = shift;
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-04 03:03:17 +03:00
|
|
|
# If no permissions are given then use the default
|
2014-02-03 03:03:05 +03:00
|
|
|
if (!defined($strPermission))
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$strPermission = $self->{strDefaultPathPermission};
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
# Get the path to create
|
|
|
|
my $strPathCreate = $strPath;
|
|
|
|
|
|
|
|
if (defined($strPathType))
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$strPathCreate = $self->path_get($strPathType, $strPath);
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2014-02-04 03:03:17 +03:00
|
|
|
my $strCommand = "mkdir -p -m ${strPermission} ${strPathCreate}";
|
|
|
|
|
|
|
|
# Run remotely
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->is_remote($strPathType))
|
2014-02-04 03:03:17 +03:00
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "path_create: remote ${strPathType} '${strCommand}'");
|
2014-02-04 03:03:17 +03:00
|
|
|
|
2014-02-06 05:39:08 +03:00
|
|
|
my $oSSH = $self->remote_get($strPathType);
|
2014-02-04 03:03:17 +03:00
|
|
|
$oSSH->system($strCommand) or confess &log("unable to create remote path ${strPathType}:${strPath}");
|
|
|
|
}
|
|
|
|
# Run locally
|
|
|
|
else
|
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "path_create: local '${strCommand}'");
|
2014-02-04 03:03:17 +03:00
|
|
|
system($strCommand) == 0 or confess &log(ERROR, "unable to create path ${strPath}");
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# IS_REMOTE
|
|
|
|
#
|
|
|
|
# Determine whether any operations are being performed remotely. If $strPathType is defined, the function will return true if that
|
|
|
|
# path is remote. If $strPathType is not defined, then function will return true if any path is remote.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub is_remote
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strPathType = shift;
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
# If the SSH object is defined then some paths are remote
|
2014-02-06 05:39:08 +03:00
|
|
|
if (defined($self->{oDbSSH}) || defined($self->{oBackupSSH}))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
|
|
|
# If path type is not defined but the SSH object is, then some paths are remote
|
|
|
|
if (!defined($strPathType))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
# If a host is defined for the path then it is remote
|
2014-02-06 05:39:08 +03:00
|
|
|
if (defined($self->{strBackupHost}) && $self->path_type_get($strPathType) eq PATH_BACKUP ||
|
|
|
|
defined($self->{strDbHost}) && $self->path_type_get($strPathType) eq PATH_DB)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# REMOTE_GET
|
|
|
|
#
|
|
|
|
# Get remote SSH object depending on the path type.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub remote_get
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strPathType = shift;
|
|
|
|
|
2014-02-04 03:03:17 +03:00
|
|
|
# Get the db SSH object
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->path_type_get($strPathType) eq PATH_DB && defined($self->{oDbSSH}))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
return $self->{oDbSSH};
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2014-02-04 03:03:17 +03:00
|
|
|
# Get the backup SSH object
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->path_type_get($strPathType) eq PATH_BACKUP && defined($self->{oBackupSSH}))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
return $self->{oBackupSSH}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2014-02-04 03:03:17 +03:00
|
|
|
# Error when no ssh object is found
|
2014-02-03 03:03:05 +03:00
|
|
|
confess &log(ASSERT, "path type ${strPathType} does not have a defined ssh object");
|
|
|
|
}
|
|
|
|
|
2014-02-05 02:48:39 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# FILE_MOVE
|
|
|
|
#
|
|
|
|
# Moves a file locally or remotely.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub file_move
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-05 21:10:36 +03:00
|
|
|
my $strSourcePathType = shift;
|
2014-02-05 02:48:39 +03:00
|
|
|
my $strSourceFile = shift;
|
2014-02-05 21:10:36 +03:00
|
|
|
my $strDestinationPathType = shift;
|
2014-02-05 02:48:39 +03:00
|
|
|
my $strDestinationFile = shift;
|
2014-02-14 17:05:14 +03:00
|
|
|
my $bPathCreate = shift;
|
|
|
|
|
|
|
|
# if bPathCreate is not defined, default to true
|
|
|
|
$bPathCreate = defined($bPathCreate) ? $bPathCreate : true;
|
2014-02-05 21:10:36 +03:00
|
|
|
|
2014-02-19 21:03:52 +03:00
|
|
|
&log(TRACE, "file_move: ${strSourcePathType}: " . (defined($strSourceFile) ? ":${strSourceFile}" : "") .
|
|
|
|
" to ${strDestinationPathType}" . (defined($strDestinationFile) ? ":${strDestinationFile}" : ""));
|
|
|
|
|
|
|
|
# Get source and desination files
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->path_type_get($strSourcePathType) ne $self->path_type_get($strSourcePathType))
|
2014-02-05 21:10:36 +03:00
|
|
|
{
|
|
|
|
confess &log(ASSERT, "source and destination path types must be equal");
|
|
|
|
}
|
|
|
|
|
2014-02-06 05:39:08 +03:00
|
|
|
my $strSource = $self->path_get($strSourcePathType, $strSourceFile);
|
|
|
|
my $strDestination = $self->path_get($strDestinationPathType, $strDestinationFile);
|
2014-02-19 21:03:52 +03:00
|
|
|
|
2014-02-05 02:48:39 +03:00
|
|
|
# If the destination path is backup and does not exist, create it
|
2014-02-14 17:05:14 +03:00
|
|
|
if ($bPathCreate && $self->path_type_get($strDestinationPathType) eq PATH_BACKUP)
|
2014-02-05 02:48:39 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$self->path_create(PATH_BACKUP_ABSOLUTE, dirname($strDestination));
|
2014-02-05 02:48:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
my $strCommand = "mv ${strSource} ${strDestination}";
|
|
|
|
|
|
|
|
# Run remotely
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->is_remote($strDestinationPathType))
|
2014-02-05 02:48:39 +03:00
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_move: remote ${strDestinationPathType} '${strCommand}'");
|
2014-02-05 02:48:39 +03:00
|
|
|
|
2014-02-06 05:39:08 +03:00
|
|
|
my $oSSH = $self->remote_get($strDestinationPathType);
|
2014-02-05 21:10:36 +03:00
|
|
|
$oSSH->system($strCommand)
|
|
|
|
or confess &log("unable to move remote ${strDestinationPathType}:${strSourceFile} to ${strDestinationFile}");
|
2014-02-05 02:48:39 +03:00
|
|
|
}
|
|
|
|
# Run locally
|
|
|
|
else
|
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_move: '${strCommand}'");
|
2014-02-05 02:48:39 +03:00
|
|
|
|
|
|
|
system($strCommand) == 0 or confess &log("unable to move local ${strSourceFile} to ${strDestinationFile}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# FILE_COPY
|
|
|
|
####################################################################################################################################
|
|
|
|
sub file_copy
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strSourcePathType = shift;
|
|
|
|
my $strSourceFile = shift;
|
|
|
|
my $strDestinationPathType = shift;
|
|
|
|
my $strDestinationFile = shift;
|
|
|
|
my $bNoCompressionOverride = shift;
|
2014-02-05 19:35:09 +03:00
|
|
|
my $lModificationTime = shift;
|
|
|
|
my $strPermission = shift;
|
2014-02-14 17:05:14 +03:00
|
|
|
my $bPathCreate = shift;
|
2014-02-18 23:42:51 +03:00
|
|
|
my $bConfessCopyError = shift;
|
2014-02-14 17:05:14 +03:00
|
|
|
|
|
|
|
# if bPathCreate is not defined, default to true
|
|
|
|
$bPathCreate = defined($bPathCreate) ? $bPathCreate : true;
|
2014-05-13 18:23:15 +03:00
|
|
|
$bConfessCopyError = defined($bConfessCopyError) ? $bConfessCopyError : true;
|
2014-02-05 19:35:09 +03:00
|
|
|
|
2014-02-19 21:03:52 +03:00
|
|
|
&log(TRACE, "file_copy: ${strSourcePathType}: " . (defined($strSourceFile) ? ":${strSourceFile}" : "") .
|
|
|
|
" to ${strDestinationPathType}" . (defined($strDestinationFile) ? ":${strDestinationFile}" : ""));
|
|
|
|
|
2014-02-05 19:35:09 +03:00
|
|
|
# Modification time and permissions cannot be set remotely
|
2014-02-06 05:39:08 +03:00
|
|
|
if ((defined($lModificationTime) || defined($strPermission)) && $self->is_remote($strDestinationPathType))
|
2014-02-05 19:35:09 +03:00
|
|
|
{
|
|
|
|
confess &log(ASSERT, "modification time and permissions cannot be set on remote destination file");
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Generate source, destination and tmp filenames
|
2014-02-06 05:39:08 +03:00
|
|
|
my $strSource = $self->path_get($strSourcePathType, $strSourceFile);
|
|
|
|
my $strDestination = $self->path_get($strDestinationPathType, $strDestinationFile);
|
|
|
|
my $strDestinationTmp = $self->path_get($strDestinationPathType, $strDestinationFile, true);
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Is this already a compressed file?
|
2014-02-06 05:39:08 +03:00
|
|
|
my $bAlreadyCompressed = $strSource =~ "^.*\.$self->{strCompressExtension}\$";
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($bAlreadyCompressed && $strDestination !~ "^.*\.$self->{strCompressExtension}\$")
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$strDestination .= ".$self->{strCompressExtension}";
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
# Does the file need compression?
|
2014-02-05 16:21:27 +03:00
|
|
|
my $bCompress = !((defined($bNoCompressionOverride) && $bNoCompressionOverride) ||
|
2014-02-06 05:39:08 +03:00
|
|
|
(!defined($bNoCompressionOverride) && $self->{bNoCompression}));
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# If the destination path is backup and does not exist, create it
|
2014-02-14 17:05:14 +03:00
|
|
|
if ($bPathCreate && $self->path_type_get($strDestinationPathType) eq PATH_BACKUP)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$self->path_create(PATH_BACKUP_ABSOLUTE, dirname($strDestination));
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2014-02-05 16:21:27 +03:00
|
|
|
# Generate the command string depending on compression/decompression/cat
|
2014-02-06 05:39:08 +03:00
|
|
|
my $strCommand = $self->{strCommandCat};
|
2014-02-05 16:21:27 +03:00
|
|
|
|
2014-02-15 22:18:15 +03:00
|
|
|
if (!$bAlreadyCompressed && $bCompress)
|
2014-02-05 16:21:27 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$strCommand = $self->{strCommandCompress};
|
2014-02-05 16:21:27 +03:00
|
|
|
$strDestination .= ".gz";
|
|
|
|
}
|
|
|
|
elsif ($bAlreadyCompressed && !$bCompress)
|
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
$strCommand = $self->{strCommandDecompress};
|
|
|
|
$strDestination = substr($strDestination, 0, length($strDestination) - length($self->{strCompressExtension}) - 1);
|
2014-02-05 16:21:27 +03:00
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
$strCommand =~ s/\%file\%/${strSource}/g;
|
|
|
|
|
|
|
|
# If this command is remote on only one side
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->is_remote($strSourcePathType) && !$self->is_remote($strDestinationPathType) ||
|
|
|
|
!$self->is_remote($strSourcePathType) && $self->is_remote($strDestinationPathType))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
|
|
|
# Else if the source is remote
|
2014-02-06 05:39:08 +03:00
|
|
|
if ($self->is_remote($strSourcePathType))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_copy: remote ${strSource} to local ${strDestination}");
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Open the destination file for writing (will be streamed from the ssh session)
|
|
|
|
my $hFile;
|
|
|
|
open($hFile, ">", $strDestinationTmp) or confess &log(ERROR, "cannot open ${strDestination}");
|
|
|
|
|
|
|
|
# Execute the command through ssh
|
2014-02-06 05:39:08 +03:00
|
|
|
my $oSSH = $self->remote_get($strSourcePathType);
|
2014-02-18 23:42:51 +03:00
|
|
|
|
2014-05-27 16:00:24 +03:00
|
|
|
unless ($oSSH->system({stderr_file => $self->path_get(PATH_LOCK_ERR, "file"), stdout_fh => $hFile}, $strCommand))
|
2014-02-18 23:42:51 +03:00
|
|
|
{
|
|
|
|
close($hFile) or confess &log(ERROR, "cannot close file ${strDestinationTmp}");
|
|
|
|
|
2014-05-27 16:00:24 +03:00
|
|
|
my $strResult = "unable to execute ssh '${strCommand}': " . $self->error_get();
|
2014-02-18 23:42:51 +03:00
|
|
|
$bConfessCopyError ? confess &log(ERROR, $strResult) : return false;
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Close the destination file handle
|
2014-02-18 23:42:51 +03:00
|
|
|
close($hFile) or confess &log(ERROR, "cannot close file ${strDestinationTmp}");
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
# Else if the destination is remote
|
2014-02-06 05:39:08 +03:00
|
|
|
elsif ($self->is_remote($strDestinationPathType))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_copy: local ${strSource} ($strCommand) to remote ${strDestination}");
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-05-27 16:00:24 +03:00
|
|
|
if (defined($self->path_get(PATH_LOCK_ERR, "file")))
|
|
|
|
{
|
|
|
|
$strCommand .= " 2> " . $self->path_get(PATH_LOCK_ERR, "file");
|
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
# Open the input command as a stream
|
|
|
|
my $hOut;
|
|
|
|
my $pId = open3(undef, $hOut, undef, $strCommand) or confess(ERROR, "unable to execute '${strCommand}'");
|
|
|
|
|
|
|
|
# Execute the command though ssh
|
2014-02-06 05:39:08 +03:00
|
|
|
my $oSSH = $self->remote_get($strDestinationPathType);
|
2014-05-27 16:00:24 +03:00
|
|
|
$oSSH->system({stderr_file => $self->path_get(PATH_LOCK_ERR, "file"), stdin_fh => $hOut}, "cat > ${strDestinationTmp}") or confess &log(ERROR, "unable to execute ssh 'cat'");
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Wait for the stream process to finish
|
|
|
|
waitpid($pId, 0);
|
|
|
|
my $iExitStatus = ${^CHILD_ERROR_NATIVE} >> 8;
|
2014-05-27 16:00:24 +03:00
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
if ($iExitStatus != 0)
|
|
|
|
{
|
2014-05-27 16:00:24 +03:00
|
|
|
my $strResult = "command '${strCommand}' returned " . $iExitStatus . ": " . $self->error_get();
|
2014-02-18 23:42:51 +03:00
|
|
|
$bConfessCopyError ? confess &log(ERROR, $strResult) : return false;
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# If the source and destination are both remote but not the same remote
|
2014-02-06 05:39:08 +03:00
|
|
|
elsif ($self->is_remote($strSourcePathType) && $self->is_remote($strDestinationPathType) &&
|
|
|
|
$self->path_type_get($strSourcePathType) ne $self->path_type_get($strDestinationPathType))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_copy: remote ${strSource} to remote ${strDestination}");
|
2014-02-03 03:03:05 +03:00
|
|
|
confess &log(ASSERT, "remote source and destination not supported");
|
|
|
|
}
|
|
|
|
# Else this is a local command or remote where both sides are the same remote
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Complete the command by redirecting to the destination tmp file
|
|
|
|
$strCommand .= " > ${strDestinationTmp}";
|
|
|
|
|
2014-02-06 06:26:10 +03:00
|
|
|
if ($self->is_remote($strSourcePathType))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_copy: remote ${strSourcePathType} '${strCommand}'");
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-02-06 05:39:08 +03:00
|
|
|
my $oSSH = $self->remote_get($strSourcePathType);
|
2014-05-27 16:00:24 +03:00
|
|
|
$oSSH->system({stderr_file => $self->path_get(PATH_LOCK_ERR, "file")}, $strCommand);
|
2014-02-18 23:42:51 +03:00
|
|
|
|
2014-05-14 22:07:37 +03:00
|
|
|
if ($oSSH->error)
|
2014-02-18 23:42:51 +03:00
|
|
|
{
|
2014-05-14 22:07:37 +03:00
|
|
|
my $strResult = "unable to execute copy ${strCommand}: " . $oSSH->error;
|
2014-02-18 23:42:51 +03:00
|
|
|
$bConfessCopyError ? confess &log(ERROR, $strResult) : return false;
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_copy: local '${strCommand}'");
|
2014-02-18 23:42:51 +03:00
|
|
|
|
2014-05-27 16:00:24 +03:00
|
|
|
if (defined($self->path_get(PATH_LOCK_ERR, "file")))
|
|
|
|
{
|
|
|
|
$strCommand .= " 2> " . $self->path_get(PATH_LOCK_ERR, "file");
|
|
|
|
}
|
|
|
|
|
2014-02-18 23:42:51 +03:00
|
|
|
unless(system($strCommand) == 0)
|
|
|
|
{
|
|
|
|
my $strResult = "unable to copy local ${strSource} to local ${strDestinationTmp}";
|
|
|
|
$bConfessCopyError ? confess &log(ERROR, $strResult) : return false;
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
}
|
2014-02-05 19:35:09 +03:00
|
|
|
|
|
|
|
# Set the file permission if required (this only works locally for now)
|
|
|
|
if (defined($strPermission))
|
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_copy: chmod ${strPermission}");
|
2014-02-05 19:35:09 +03:00
|
|
|
|
|
|
|
system("chmod ${strPermission} ${strDestinationTmp}") == 0
|
|
|
|
or confess &log(ERROR, "unable to set permissions for local ${strDestinationTmp}");
|
|
|
|
}
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-05 19:35:09 +03:00
|
|
|
# Set the file modification time if required (this only works locally for now)
|
|
|
|
if (defined($lModificationTime))
|
|
|
|
{
|
2014-02-13 21:54:43 +03:00
|
|
|
&log(TRACE, "file_copy: time ${lModificationTime}");
|
2014-02-05 19:35:09 +03:00
|
|
|
|
|
|
|
utime($lModificationTime, $lModificationTime, $strDestinationTmp)
|
|
|
|
or confess &log(ERROR, "unable to set time for local ${strDestinationTmp}");
|
|
|
|
}
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
# Move the file from tmp to final destination
|
2014-02-06 05:39:08 +03:00
|
|
|
$self->file_move($self->path_type_get($strSourcePathType) . ":absolute", $strDestinationTmp,
|
2014-02-14 17:05:14 +03:00
|
|
|
$self->path_type_get($strDestinationPathType) . ":absolute", $strDestination, $bPathCreate);
|
2014-02-18 23:42:51 +03:00
|
|
|
|
|
|
|
return true;
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2014-06-02 00:23:33 +03:00
|
|
|
# HASH
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-06-02 00:23:33 +03:00
|
|
|
sub hash
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strPathType = shift;
|
|
|
|
my $strFile = shift;
|
2014-06-02 00:23:33 +03:00
|
|
|
my $strHashType = shift;
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-02-04 03:03:17 +03:00
|
|
|
# For now this operation is not supported remotely. Not currently needed.
|
2014-06-02 00:23:33 +03:00
|
|
|
my $strHash;
|
|
|
|
my $strErrorPrefix = "File->hash";
|
|
|
|
my $bRemote = $self->is_remote($strPathType);
|
|
|
|
my $strPath = $self->path_get($strPathType, $strFile);
|
|
|
|
|
|
|
|
&log(TRACE, "${strErrorPrefix}: " . ($bRemote ? "remote" : "local") . " ${strPathType}:${strPath}");
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
if ($bRemote)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
# Run remotely
|
|
|
|
my $oSSH = $self->remote_get($strPathType);
|
|
|
|
my $strOutput = $oSSH->capture($self->{strCommand} . " hash ${strPath}");
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
# Capture any errors
|
|
|
|
if ($oSSH->error)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "${strErrorPrefix} remote: " . (defined($strOutput) ? $strOutput : $oSSH->error));
|
|
|
|
}
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
$strHash = $strOutput;
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
my $hFile;
|
|
|
|
|
|
|
|
if (!open($hFile, "<", $strPath))
|
|
|
|
{
|
|
|
|
my $strError = "${strPath} could not be read" . $!;
|
|
|
|
my $iErrorCode = 2;
|
|
|
|
|
|
|
|
unless (-e $strPath)
|
|
|
|
{
|
|
|
|
$strError = "${strPath} does not exist";
|
|
|
|
$iErrorCode = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($strPathType eq PATH_ABSOLUTE)
|
|
|
|
{
|
|
|
|
print $strError;
|
|
|
|
exit ($iErrorCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
confess &log(ERROR, "${strErrorPrefix}: " . $strError);
|
|
|
|
}
|
|
|
|
|
|
|
|
my $oSHA = Digest::SHA->new(defined($strHashType) ? $strHashType : 'sha1');
|
|
|
|
|
|
|
|
$oSHA->addfile($hFile);
|
|
|
|
|
|
|
|
close($hFile);
|
|
|
|
|
|
|
|
$strHash = $oSHA->hexdigest();
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
return $strHash;
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
2014-06-02 00:23:33 +03:00
|
|
|
|
2014-02-15 22:18:15 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# FILE_COMPRESS
|
|
|
|
####################################################################################################################################
|
|
|
|
sub file_compress
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $strPathType = shift;
|
|
|
|
my $strFile = shift;
|
|
|
|
|
|
|
|
# For now this operation is not supported remotely. Not currently needed.
|
|
|
|
if ($self->is_remote($strPathType))
|
|
|
|
{
|
|
|
|
confess &log(ASSERT, "remote operation not supported");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!defined($self->{strCommandCompress}))
|
|
|
|
{
|
2014-03-26 01:56:05 +03:00
|
|
|
confess &log(ASSERT, "\$strCommandCompress not defined");
|
2014-02-15 22:18:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
my $strPath = $self->path_get($strPathType, $strFile);
|
|
|
|
|
|
|
|
# Build the command
|
|
|
|
my $strCommand = $self->{strCommandCompress};
|
|
|
|
$strCommand =~ s/\%file\%/${strPath}/g;
|
|
|
|
$strCommand =~ s/\ \-\-stdout//g;
|
|
|
|
|
|
|
|
system($strCommand) == 0 or confess &log(ERROR, "unable to compress ${strPath}: ${strCommand}");
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
####################################################################################################################################
|
2014-06-02 00:23:33 +03:00
|
|
|
# LIST
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-06-02 00:23:33 +03:00
|
|
|
sub list
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-04 03:03:17 +03:00
|
|
|
my $strPathType = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strPath = shift;
|
|
|
|
my $strExpression = shift;
|
|
|
|
my $strSortOrder = shift;
|
2014-02-04 03:03:17 +03:00
|
|
|
|
2014-03-28 18:40:37 +03:00
|
|
|
# Get the root path for the file list
|
2014-06-02 00:23:33 +03:00
|
|
|
my @stryFileList;
|
|
|
|
my $strErrorPrefix = "File->list";
|
|
|
|
my $bRemote = $self->is_remote($strPathType);
|
|
|
|
my $strPathOp = $self->path_get($strPathType, $strPath);
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
&log(TRACE, "${strErrorPrefix}: " . ($bRemote ? "remote" : "local") . " ${strPathType}:${strPathOp}" .
|
|
|
|
", expression " . (defined($strExpression) ? $strExpression : "[UNDEF]") .
|
|
|
|
", sort " . (defined($strSortOrder) ? $strSortOrder : "[UNDEF]"));
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-03-26 01:56:05 +03:00
|
|
|
# Run remotely
|
|
|
|
if ($self->is_remote($strPathType))
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
my $strCommand = $self->{strCommand} .
|
|
|
|
(defined($strSortOrder) && $strSortOrder eq "reverse" ? " --sort=reverse" : "") .
|
|
|
|
(defined($strExpression) ? " --expression=\"" . $strExpression . "\"" : "") .
|
|
|
|
" list ${strPathOp}";
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
# Run via SSH
|
2014-03-26 01:56:05 +03:00
|
|
|
my $oSSH = $self->remote_get($strPathType);
|
2014-06-02 00:23:33 +03:00
|
|
|
my $strOutput = $oSSH->capture($strCommand);
|
|
|
|
|
|
|
|
# Handle any errors
|
2014-04-28 16:13:25 +03:00
|
|
|
if ($oSSH->error)
|
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
confess &log(ERROR, "${strErrorPrefix} remote (${strCommand}): " . (defined($strOutput) ? $strOutput : $oSSH->error));
|
2014-04-28 16:13:25 +03:00
|
|
|
}
|
2014-06-02 00:23:33 +03:00
|
|
|
|
|
|
|
@stryFileList = split(/\n/, $strOutput);
|
2014-03-26 01:56:05 +03:00
|
|
|
}
|
|
|
|
# Run locally
|
|
|
|
else
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
my $hPath;
|
2014-02-07 00:37:37 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
if (!opendir($hPath, $strPathOp))
|
|
|
|
{
|
|
|
|
my $strError = "${strPathOp} could not be read:" . $!;
|
|
|
|
my $iErrorCode = 2;
|
2014-03-26 01:56:05 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
unless (-e $strPath)
|
|
|
|
{
|
|
|
|
$strError = "${strPathOp} does not exist";
|
|
|
|
$iErrorCode = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($strPathType eq PATH_ABSOLUTE)
|
|
|
|
{
|
|
|
|
print $strError;
|
|
|
|
exit ($iErrorCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
confess &log(ERROR, "${strErrorPrefix}: " . $strError);
|
|
|
|
}
|
|
|
|
|
|
|
|
@stryFileList = grep(!/^(\.)|(\.\.)$/i, readdir($hPath));
|
|
|
|
|
|
|
|
close($hPath);
|
|
|
|
|
|
|
|
if (defined($strExpression))
|
|
|
|
{
|
|
|
|
@stryFileList = grep(/$strExpression/i, @stryFileList);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Reverse sort
|
|
|
|
if (defined($strSortOrder) && $strSortOrder eq "reverse")
|
|
|
|
{
|
|
|
|
@stryFileList = sort {$b cmp $a} @stryFileList;
|
|
|
|
}
|
|
|
|
# Normal sort
|
|
|
|
else
|
|
|
|
{
|
|
|
|
@stryFileList = sort @stryFileList;
|
|
|
|
}
|
2014-03-28 18:40:37 +03:00
|
|
|
}
|
2014-06-02 00:23:33 +03:00
|
|
|
|
|
|
|
# Return file list
|
|
|
|
return @stryFileList;
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2014-02-18 23:42:51 +03:00
|
|
|
####################################################################################################################################
|
2014-06-02 00:23:33 +03:00
|
|
|
# EXISTS - Checks for the existence of a file, but does not imply that the file is readable/writeable.
|
|
|
|
#
|
|
|
|
# Return: true if file exists, false otherwise
|
2014-02-18 23:42:51 +03:00
|
|
|
####################################################################################################################################
|
2014-06-02 00:23:33 +03:00
|
|
|
sub exists
|
2014-02-18 23:42:51 +03:00
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $strPathType = shift;
|
|
|
|
my $strPath = shift;
|
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
# Set error prefix, remote, and path
|
|
|
|
my $bExists = false;
|
|
|
|
my $strErrorPrefix = "File->exists";
|
|
|
|
my $bRemote = $self->is_remote($strPathType);
|
|
|
|
my $strPathOp = $self->path_get($strPathType, $strPath);
|
2014-02-18 23:42:51 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
&log(TRACE, "${strErrorPrefix}: " . ($bRemote ? "remote" : "local") . " ${strPathType}:${strPathOp}");
|
2014-02-18 23:42:51 +03:00
|
|
|
|
|
|
|
# Run remotely
|
2014-06-02 00:23:33 +03:00
|
|
|
if ($bRemote)
|
2014-02-18 23:42:51 +03:00
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
# Build the command
|
|
|
|
my $strCommand = $self->{strCommand} . " exists ${strPathOp}";
|
|
|
|
|
|
|
|
# Run it remotely
|
2014-02-18 23:42:51 +03:00
|
|
|
my $oSSH = $self->remote_get($strPathType);
|
2014-06-02 00:23:33 +03:00
|
|
|
my $strOutput = $oSSH->capture($strCommand);
|
2014-05-27 16:00:24 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
# Capture any errors
|
2014-05-13 18:23:15 +03:00
|
|
|
if ($oSSH->error)
|
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
confess &log(ERROR, "${strErrorPrefix} remote (${strCommand}): " . (defined($strOutput) ? $strOutput : $oSSH->error));
|
2014-05-13 18:23:15 +03:00
|
|
|
}
|
2014-05-27 16:00:24 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
$bExists = $strOutput eq "Y";
|
2014-02-18 23:42:51 +03:00
|
|
|
}
|
|
|
|
# Run locally
|
|
|
|
else
|
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
if (-e $strPathOp)
|
2014-05-27 16:00:24 +03:00
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
$bExists = true;
|
2014-05-27 16:00:24 +03:00
|
|
|
}
|
2014-02-18 23:42:51 +03:00
|
|
|
}
|
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
return $bExists;
|
2014-02-18 23:42:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2014-06-02 00:23:33 +03:00
|
|
|
# REMOVE
|
2014-02-18 23:42:51 +03:00
|
|
|
####################################################################################################################################
|
2014-06-02 00:23:33 +03:00
|
|
|
sub remove
|
2014-02-18 23:42:51 +03:00
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $strPathType = shift;
|
|
|
|
my $strPath = shift;
|
|
|
|
my $bTemp = shift;
|
2014-06-02 00:23:33 +03:00
|
|
|
my $bIgnoreMissing = shift;
|
2014-02-18 23:42:51 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
if (!defined($bIgnoreMissing))
|
2014-02-18 23:42:51 +03:00
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
$bIgnoreMissing = true;
|
2014-02-18 23:42:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
# Get the root path for the manifest
|
2014-06-02 00:23:33 +03:00
|
|
|
my $bRemoved = true;
|
|
|
|
my $strErrorPrefix = "File->remove";
|
|
|
|
my $bRemote = $self->is_remote($strPathType);
|
|
|
|
my $strPathOp = $self->path_get($strPathType, $strPath, $bTemp);
|
2014-02-18 23:42:51 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
&log(TRACE, "${strErrorPrefix}: " . ($bRemote ? "remote" : "local") . " ${strPathType}:${strPathOp}");
|
2014-02-18 23:42:51 +03:00
|
|
|
|
|
|
|
# Run remotely
|
2014-06-02 00:23:33 +03:00
|
|
|
if ($bRemote)
|
2014-02-18 23:42:51 +03:00
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
# Build the command
|
|
|
|
my $strCommand = $self->{strCommand} . ($bIgnoreMissing ? " --ignore-missing" : "") . " remove ${strPathOp}";
|
2014-05-13 18:23:15 +03:00
|
|
|
|
2014-06-02 00:23:33 +03:00
|
|
|
# Run it remotely
|
|
|
|
my $oSSH = $self->remote_get($strPathType);
|
|
|
|
my $strOutput = $oSSH->capture($strCommand);
|
|
|
|
|
2014-05-13 18:23:15 +03:00
|
|
|
if ($oSSH->error)
|
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
confess &log(ERROR, "${strErrorPrefix} remote (${strCommand}): " . (defined($strOutput) ? $strOutput : $oSSH->error));
|
2014-05-13 18:23:15 +03:00
|
|
|
}
|
2014-06-02 00:23:33 +03:00
|
|
|
|
|
|
|
$bRemoved = $strOutput eq "Y";
|
2014-02-18 23:42:51 +03:00
|
|
|
}
|
|
|
|
# Run locally
|
|
|
|
else
|
|
|
|
{
|
2014-06-02 00:23:33 +03:00
|
|
|
if (unlink($strPathOp) != 1)
|
|
|
|
{
|
|
|
|
$bRemoved = false;
|
|
|
|
|
|
|
|
if (-e $strPathOp || !$bIgnoreMissing)
|
|
|
|
{
|
|
|
|
my $strError = "${strPathOp} could not be removed: " . $!;
|
|
|
|
my $iErrorCode = 2;
|
|
|
|
|
|
|
|
unless (-e $strPathOp)
|
|
|
|
{
|
|
|
|
$strError = "${strPathOp} does not exist";
|
|
|
|
$iErrorCode = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($strPathType eq PATH_ABSOLUTE)
|
|
|
|
{
|
|
|
|
print $strError;
|
|
|
|
exit ($iErrorCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
confess &log(ERROR, "${strErrorPrefix}: " . $strError);
|
|
|
|
}
|
|
|
|
}
|
2014-02-18 23:42:51 +03:00
|
|
|
}
|
2014-06-02 00:23:33 +03:00
|
|
|
|
|
|
|
return $bRemoved;
|
2014-02-18 23:42:51 +03:00
|
|
|
}
|
|
|
|
|
2014-02-03 03:03:05 +03:00
|
|
|
####################################################################################################################################
|
2014-06-02 01:39:35 +03:00
|
|
|
# MANIFEST
|
2014-02-03 03:03:05 +03:00
|
|
|
#
|
|
|
|
# Builds a path/file manifest starting with the base path and including all subpaths. The manifest contains all the information
|
|
|
|
# needed to perform a backup or a delta with a previous backup.
|
|
|
|
####################################################################################################################################
|
2014-06-02 01:39:35 +03:00
|
|
|
sub manifest
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-02-06 05:39:08 +03:00
|
|
|
my $self = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
my $strPathType = shift;
|
|
|
|
my $strPath = shift;
|
2014-06-02 01:39:35 +03:00
|
|
|
my $oManifestHashRef = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Get the root path for the manifest
|
2014-06-02 01:39:35 +03:00
|
|
|
my $strErrorPrefix = "File->manifest";
|
|
|
|
my $bRemote = $self->is_remote($strPathType);
|
|
|
|
my $strPathOp = $self->path_get($strPathType, $strPath);
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-06-02 01:39:35 +03:00
|
|
|
&log(TRACE, "${strErrorPrefix}: " . ($bRemote ? "remote" : "local") . " ${strPathType}:${strPathOp}");
|
2014-02-03 03:03:05 +03:00
|
|
|
|
|
|
|
# Run remotely
|
2014-06-02 01:39:35 +03:00
|
|
|
if ($bRemote)
|
2014-02-03 03:03:05 +03:00
|
|
|
{
|
2014-06-02 01:39:35 +03:00
|
|
|
# Build the command
|
|
|
|
my $strCommand = $self->{strCommand} . " manifest ${strPathOp}";
|
|
|
|
|
|
|
|
# Run it remotely
|
2014-02-06 05:39:08 +03:00
|
|
|
my $oSSH = $self->remote_get($strPathType);
|
2014-06-02 01:39:35 +03:00
|
|
|
my $strOutput = $oSSH->capture($strCommand);
|
|
|
|
|
|
|
|
if ($oSSH->error)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "${strErrorPrefix} remote (${strCommand}): " . (defined($strOutput) ? $strOutput : $oSSH->error));
|
|
|
|
}
|
|
|
|
|
2014-06-02 23:26:37 +03:00
|
|
|
return data_hash_build($oManifestHashRef, $strOutput, "\t", ".");
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
# Run locally
|
|
|
|
else
|
|
|
|
{
|
2014-06-02 23:26:37 +03:00
|
|
|
manifest_recurse($strPathType, $strPathOp, undef, 0, $oManifestHashRef);
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
2014-06-02 01:39:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
sub manifest_recurse
|
|
|
|
{
|
2014-06-02 23:26:37 +03:00
|
|
|
my $strPathType = shift;
|
|
|
|
my $strPathOp = shift;
|
|
|
|
my $strPathFileOp = shift;
|
|
|
|
my $iDepth = shift;
|
2014-06-02 01:39:35 +03:00
|
|
|
my $oManifestHashRef = shift;
|
2014-02-03 03:03:05 +03:00
|
|
|
|
2014-06-02 23:26:37 +03:00
|
|
|
my $strErrorPrefix = "File->manifest";
|
|
|
|
my $strPathRead = $strPathOp . (defined($strPathFileOp) ? "/${strPathFileOp}" : "");
|
|
|
|
my $hPath;
|
|
|
|
|
|
|
|
if (!opendir($hPath, $strPathRead))
|
|
|
|
{
|
|
|
|
my $strError = "${strPathRead} could not be read:" . $!;
|
|
|
|
my $iErrorCode = 2;
|
|
|
|
|
|
|
|
unless (-e $strPathRead)
|
|
|
|
{
|
|
|
|
$strError = "${strPathRead} does not exist";
|
|
|
|
$iErrorCode = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($strPathType eq PATH_ABSOLUTE)
|
|
|
|
{
|
|
|
|
print $strError;
|
|
|
|
exit ($iErrorCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
confess &log(ERROR, "${strErrorPrefix}: " . $strError);
|
|
|
|
}
|
|
|
|
|
|
|
|
my @stryFileList = grep(!/^\..$/i, readdir($hPath));
|
|
|
|
|
|
|
|
close($hPath);
|
|
|
|
|
|
|
|
foreach my $strFile (@stryFileList)
|
|
|
|
{
|
|
|
|
my $strPathFile = "${strPathRead}/$strFile";
|
|
|
|
my $bCurrentDir = $strFile eq ".";
|
|
|
|
|
|
|
|
if ($iDepth != 0)
|
|
|
|
{
|
|
|
|
if ($bCurrentDir)
|
|
|
|
{
|
|
|
|
$strFile = $strPathFileOp;
|
|
|
|
$strPathFile = $strPathRead;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$strFile = "${strPathFileOp}/${strFile}";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
my $oStat = lstat($strPathFile);
|
|
|
|
|
|
|
|
if (!defined($oStat))
|
|
|
|
{
|
|
|
|
if (-e $strPathFile)
|
|
|
|
{
|
|
|
|
my $strError = "${strPathFile} could not be read: " . $!;
|
|
|
|
|
|
|
|
if ($strPathType eq PATH_ABSOLUTE)
|
|
|
|
{
|
|
|
|
print $strError;
|
|
|
|
exit COMMAND_ERR_FILE_READ;
|
|
|
|
}
|
|
|
|
|
|
|
|
confess &log(ERROR, "${strErrorPrefix}: " . $strError);
|
|
|
|
}
|
|
|
|
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Check for regular file
|
|
|
|
if (S_ISREG($oStat->mode))
|
|
|
|
{
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{type} = "f";
|
|
|
|
|
|
|
|
# Get inode
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{inode} = $oStat->ino;
|
|
|
|
|
|
|
|
# Get size
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{size} = $oStat->size;
|
|
|
|
|
|
|
|
# Get modification time
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{modification_time} = $oStat->mtime;
|
|
|
|
}
|
|
|
|
# Check for directory
|
|
|
|
elsif (S_ISDIR($oStat->mode))
|
|
|
|
{
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{type} = "d";
|
|
|
|
}
|
|
|
|
# Check for link
|
|
|
|
elsif (S_ISLNK($oStat->mode))
|
|
|
|
{
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{type} = "l";
|
|
|
|
|
|
|
|
# Get link destination
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{link_destination} = readlink($strPathFile);
|
|
|
|
|
|
|
|
if (!defined(${$oManifestHashRef}{name}{"${strFile}"}{link_destination}))
|
|
|
|
{
|
|
|
|
if (-e $strPathFile)
|
|
|
|
{
|
|
|
|
my $strError = "${strPathFile} error reading link: " . $!;
|
|
|
|
|
|
|
|
if ($strPathType eq PATH_ABSOLUTE)
|
|
|
|
{
|
|
|
|
print $strError;
|
|
|
|
exit COMMAND_ERR_LINK_READ;
|
|
|
|
}
|
|
|
|
|
|
|
|
confess &log(ERROR, "${strErrorPrefix}: " . $strError);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
my $strError = "${strPathFile} is not of type directory, file, or link";
|
|
|
|
|
|
|
|
if ($strPathType eq PATH_ABSOLUTE)
|
|
|
|
{
|
|
|
|
print $strError;
|
|
|
|
exit COMMAND_ERR_FILE_TYPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
confess &log(ERROR, "${strErrorPrefix}: " . $strError);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Get user name
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{user} = getpwuid($oStat->uid);
|
|
|
|
|
|
|
|
# Get group name
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{group} = getgrgid($oStat->gid);
|
|
|
|
|
|
|
|
# Get permissions
|
|
|
|
if (${$oManifestHashRef}{name}{"${strFile}"}{type} ne "l")
|
|
|
|
{
|
|
|
|
${$oManifestHashRef}{name}{"${strFile}"}{permission} = sprintf("%04o", S_IMODE($oStat->mode));
|
|
|
|
}
|
|
|
|
|
|
|
|
# Recurse into directories
|
|
|
|
if (${$oManifestHashRef}{name}{"${strFile}"}{type} eq "d" && !$bCurrentDir)
|
|
|
|
{
|
|
|
|
manifest_recurse($strPathType, $strPathOp,
|
|
|
|
$strFile,
|
|
|
|
$iDepth + 1, $oManifestHashRef);
|
|
|
|
}
|
|
|
|
}
|
2014-02-03 03:03:05 +03:00
|
|
|
}
|
|
|
|
|
2014-02-06 05:39:08 +03:00
|
|
|
no Moose;
|
2014-06-02 00:23:33 +03:00
|
|
|
__PACKAGE__->meta->make_immutable;
|