1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00
pgbackrest/lib/BackRest/Config.pm
David Steele 1afbab010f v0.75: New repository format, info command and experimental 9.5 support
* IMPORTANT NOTE: This flag day release breaks compatibility with older versions of PgBackRest.  The manifest format, on-disk structure, and the binary names have all changed.  You must create a new repository to hold backups for this version of PgBackRest and keep your older repository for a time in case you need to do a restore.  The `pg_backrest.conf` file has not changed but you'll need to change any references to `pg_backrest.pl` in cron (or elsewhere) to `pg_backrest` (without the `.pl` extension).

* Add info command.

* More efficient file ordering for backup.  Files are copied in descending size order so a single thread does not end up copying a large file at the end.  This had already been implemented for restore.

* Logging now uses unbuffered output.  This should make log files that are being written by multiple threads less chaotic.  Suggested by Michael Renner.

* Experimental support for PostgreSQL 9.5.  This may break when the control version or WAL magic changes but will be updated in each release.
2015-06-14 20:59:32 -04:00

1866 lines
67 KiB
Perl

####################################################################################################################################
# CONFIG MODULE
####################################################################################################################################
package BackRest::Config;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Cwd qw(abs_path);
use Exporter qw(import);
use File::Basename qw(dirname);
use Getopt::Long qw(GetOptions);
use Pod::Usage;
use lib dirname($0) . '/../lib';
use BackRest::Exception;
use BackRest::Ini;
use BackRest::Utility;
####################################################################################################################################
# Export functions
####################################################################################################################################
our @EXPORT = qw(configLoad optionGet optionTest optionRuleGet optionRequired optionDefault operationGet operationTest
operationSet operationWrite optionRemoteType optionRemoteTypeTest optionRemote optionRemoteTest
remoteDestroy);
####################################################################################################################################
# DB/BACKUP Constants
####################################################################################################################################
use constant
{
DB => 'db',
BACKUP => 'backup',
NONE => 'none'
};
push @EXPORT, qw(DB BACKUP NONE);
####################################################################################################################################
# Operation constants - basic operations that are allowed in backrest
####################################################################################################################################
use constant
{
OP_ARCHIVE_GET => 'archive-get',
OP_ARCHIVE_PUSH => 'archive-push',
OP_BACKUP => 'backup',
OP_INFO => 'info',
OP_RESTORE => 'restore',
OP_EXPIRE => 'expire'
};
push @EXPORT, qw(OP_ARCHIVE_GET OP_ARCHIVE_PUSH OP_BACKUP OP_INFO OP_RESTORE OP_EXPIRE);
####################################################################################################################################
# BACKUP Type Constants
####################################################################################################################################
use constant
{
BACKUP_TYPE_FULL => 'full',
BACKUP_TYPE_DIFF => 'diff',
BACKUP_TYPE_INCR => 'incr'
};
push @EXPORT, qw(BACKUP_TYPE_FULL BACKUP_TYPE_DIFF BACKUP_TYPE_INCR);
####################################################################################################################################
# INFO Output Constants
####################################################################################################################################
use constant INFO_OUTPUT_TEXT => 'text'; push @EXPORT, qw(INFO_OUTPUT_TEXT);
use constant INFO_OUTPUT_JSON => 'json'; push @EXPORT, qw(INFO_OUTPUT_JSON);
####################################################################################################################################
# SOURCE Constants
####################################################################################################################################
use constant
{
SOURCE_CONFIG => 'config',
SOURCE_PARAM => 'param',
SOURCE_DEFAULT => 'default'
};
####################################################################################################################################
# RECOVERY Type Constants
####################################################################################################################################
use constant
{
RECOVERY_TYPE_NAME => 'name',
RECOVERY_TYPE_TIME => 'time',
RECOVERY_TYPE_XID => 'xid',
RECOVERY_TYPE_PRESERVE => 'preserve',
RECOVERY_TYPE_NONE => 'none',
RECOVERY_TYPE_DEFAULT => 'default'
};
push @EXPORT, qw(RECOVERY_TYPE_NAME RECOVERY_TYPE_TIME RECOVERY_TYPE_XID RECOVERY_TYPE_PRESERVE RECOVERY_TYPE_NONE
RECOVERY_TYPE_DEFAULT);
####################################################################################################################################
# Configuration section constants
####################################################################################################################################
use constant
{
CONFIG_GLOBAL => 'global',
CONFIG_SECTION_ARCHIVE => 'archive',
CONFIG_SECTION_BACKUP => 'backup',
CONFIG_SECTION_COMMAND => 'command',
CONFIG_SECTION_GENERAL => 'general',
CONFIG_SECTION_LOG => 'log',
CONFIG_SECTION_RESTORE_RECOVERY_SETTING => 'restore:recovery-setting',
CONFIG_SECTION_RESTORE_TABLESPACE_MAP => 'restore:tablespace-map',
CONFIG_SECTION_EXPIRE => 'expire',
CONFIG_SECTION_STANZA => 'stanza'
};
push @EXPORT, qw(CONFIG_GLOBAL
CONFIG_SECTION_ARCHIVE CONFIG_SECTION_BACKUP CONFIG_SECTION_COMMAND
CONFIG_SECTION_GENERAL CONFIG_SECTION_LOG CONFIG_SECTION_RESTORE_RECOVERY_SETTING
CONFIG_SECTION_EXPIRE CONFIG_SECTION_STANZA CONFIG_SECTION_RESTORE_TABLESPACE_MAP);
####################################################################################################################################
# Option constants
####################################################################################################################################
use constant
{
# Command-line-only options
OPTION_CONFIG => 'config',
OPTION_DELTA => 'delta',
OPTION_FORCE => 'force',
OPTION_NO_START_STOP => 'no-start-stop',
OPTION_SET => 'set',
OPTION_STANZA => 'stanza',
OPTION_TARGET => 'target',
OPTION_TARGET_EXCLUSIVE => 'target-exclusive',
OPTION_TARGET_RESUME => 'target-resume',
OPTION_TARGET_TIMELINE => 'target-timeline',
OPTION_TYPE => 'type',
OPTION_OUTPUT => 'output',
# Command-line/conf file options
# GENERAL Section
OPTION_BUFFER_SIZE => 'buffer-size',
OPTION_COMPRESS => 'compress',
OPTION_COMPRESS_LEVEL => 'compress-level',
OPTION_COMPRESS_LEVEL_NETWORK => 'compress-level-network',
OPTION_REPO_PATH => 'repo-path',
OPTION_REPO_REMOTE_PATH => 'repo-remote-path',
OPTION_THREAD_MAX => 'thread-max',
OPTION_THREAD_TIMEOUT => 'thread-timeout',
# ARCHIVE Section
OPTION_ARCHIVE_MAX_MB => 'archive-max-mb',
OPTION_ARCHIVE_ASYNC => 'archive-async',
# BACKUP Section
OPTION_BACKUP_ARCHIVE_CHECK => 'archive-check',
OPTION_BACKUP_ARCHIVE_COPY => 'archive-copy',
OPTION_BACKUP_HOST => 'backup-host',
OPTION_BACKUP_USER => 'backup-user',
OPTION_HARDLINK => 'hardlink',
OPTION_MANIFEST_SAVE_THRESHOLD => 'manifest-save-threshold',
OPTION_RESUME => 'resume',
OPTION_START_FAST => 'start-fast',
# COMMAND Section
OPTION_COMMAND_REMOTE => 'cmd-remote',
OPTION_COMMAND_PSQL => 'cmd-psql',
OPTION_COMMAND_PSQL_OPTION => 'cmd-psql-option',
# LOG Section
OPTION_LOG_LEVEL_CONSOLE => 'log-level-console',
OPTION_LOG_LEVEL_FILE => 'log-level-file',
# EXPIRE Section
OPTION_RETENTION_ARCHIVE => 'retention-archive',
OPTION_RETENTION_ARCHIVE_TYPE => 'retention-archive-type',
OPTION_RETENTION_DIFF => 'retention-' . BACKUP_TYPE_DIFF,
OPTION_RETENTION_FULL => 'retention-' . BACKUP_TYPE_FULL,
# RESTORE Section
OPTION_TABLESPACE => 'tablespace',
OPTION_RESTORE_TABLESPACE_MAP => 'tablespace-map',
OPTION_RESTORE_RECOVERY_SETTING => 'recovery-setting',
# STANZA Section
OPTION_DB_HOST => 'db-host',
OPTION_DB_PATH => 'db-path',
OPTION_DB_USER => 'db-user',
# Command-line-only help/version options
OPTION_HELP => 'help',
OPTION_VERSION => 'version',
# Command-line-only test options
OPTION_TEST => 'test',
OPTION_TEST_DELAY => 'test-delay',
OPTION_TEST_NO_FORK => 'no-fork'
};
push @EXPORT, qw(OPTION_CONFIG OPTION_DELTA OPTION_FORCE OPTION_NO_START_STOP OPTION_SET OPTION_STANZA OPTION_TARGET
OPTION_TARGET_EXCLUSIVE OPTION_TARGET_RESUME OPTION_TARGET_TIMELINE OPTION_TYPE
OPTION_DB_HOST OPTION_BACKUP_HOST OPTION_ARCHIVE_MAX_MB OPTION_BACKUP_ARCHIVE_CHECK OPTION_BACKUP_ARCHIVE_COPY
OPTION_ARCHIVE_ASYNC
OPTION_BUFFER_SIZE OPTION_COMPRESS OPTION_COMPRESS_LEVEL OPTION_COMPRESS_LEVEL_NETWORK OPTION_HARDLINK
OPTION_MANIFEST_SAVE_THRESHOLD OPTION_RESUME OPTION_PATH_ARCHIVE OPTION_REPO_PATH OPTION_REPO_REMOTE_PATH
OPTION_DB_PATH OPTION_OUTPUT
OPTION_LOG_LEVEL_CONSOLE OPTION_LOG_LEVEL_FILE
OPTION_RESTORE_RECOVERY_SETTING OPTION_RETENTION_ARCHIVE OPTION_RETENTION_ARCHIVE_TYPE OPTION_RETENTION_FULL
OPTION_RETENTION_DIFF OPTION_START_FAST OPTION_THREAD_MAX OPTION_THREAD_TIMEOUT
OPTION_DB_USER OPTION_BACKUP_USER OPTION_COMMAND_PSQL OPTION_COMMAND_PSQL_OPTION OPTION_COMMAND_REMOTE
OPTION_TABLESPACE OPTION_RESTORE_TABLESPACE_MAP
OPTION_TEST OPTION_TEST_DELAY OPTION_TEST_NO_FORK);
####################################################################################################################################
# Option Defaults
####################################################################################################################################
use constant
{
OPTION_DEFAULT_BUFFER_SIZE => 4194304,
OPTION_DEFAULT_BUFFER_SIZE_MIN => 16384,
OPTION_DEFAULT_BUFFER_SIZE_MAX => 8388608,
OPTION_DEFAULT_COMPRESS => true,
OPTION_DEFAULT_COMPRESS_LEVEL => 6,
OPTION_DEFAULT_COMPRESS_LEVEL_MIN => 0,
OPTION_DEFAULT_COMPRESS_LEVEL_MAX => 9,
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK => 3,
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK_MIN => 0,
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK_MAX => 9,
OPTION_DEFAULT_CONFIG => '/etc/pg_backrest.conf',
OPTION_DEFAULT_LOG_LEVEL_CONSOLE => lc(WARN),
OPTION_DEFAULT_LOG_LEVEL_FILE => lc(INFO),
OPTION_DEFAULT_THREAD_MAX => 1,
OPTION_DEFAULT_ARCHIVE_ASYNC => false,
OPTION_DEFAULT_COMMAND_PSQL => '/usr/bin/psql -X',
OPTION_DEFAULT_COMMAND_REMOTE => dirname(abs_path($0)) . '/pg_backrest_remote',
OPTION_DEFAULT_BACKUP_ARCHIVE_CHECK => true,
OPTION_DEFAULT_BACKUP_ARCHIVE_COPY => false,
OPTION_DEFAULT_BACKUP_FORCE => false,
OPTION_DEFAULT_BACKUP_HARDLINK => false,
OPTION_DEFAULT_BACKUP_NO_START_STOP => false,
OPTION_DEFAULT_BACKUP_MANIFEST_SAVE_THRESHOLD => 1073741824,
OPTION_DEFAULT_BACKUP_RESUME => true,
OPTION_DEFAULT_BACKUP_START_FAST => false,
OPTION_DEFAULT_BACKUP_TYPE => BACKUP_TYPE_INCR,
OPTION_DEFAULT_INFO_OUTPUT => INFO_OUTPUT_TEXT,
OPTION_DEFAULT_REPO_PATH => '/var/lib/backup',
OPTION_DEFAULT_RESTORE_DELTA => false,
OPTION_DEFAULT_RESTORE_FORCE => false,
OPTION_DEFAULT_RESTORE_SET => 'latest',
OPTION_DEFAULT_RESTORE_TABLESPACE => true,
OPTION_DEFAULT_RESTORE_TYPE => RECOVERY_TYPE_DEFAULT,
OPTION_DEFAULT_RESTORE_TARGET_EXCLUSIVE => false,
OPTION_DEFAULT_RESTORE_TARGET_RESUME => false,
OPTION_DEFAULT_RETENTION_ARCHIVE_TYPE => BACKUP_TYPE_FULL,
OPTION_DEFAULT_RETENTION_MIN => 1,
OPTION_DEFAULT_RETENTION_MAX => 999999999,
OPTION_DEFAULT_TEST => false,
OPTION_DEFAULT_TEST_DELAY => 5,
OPTION_DEFAULT_TEST_NO_FORK => false
};
push @EXPORT, qw(OPTION_DEFAULT_BUFFER_SIZE OPTION_DEFAULT_COMPRESS OPTION_DEFAULT_CONFIG OPTION_LEVEL_CONSOLE OPTION_LEVEL_FILE
OPTION_DEFAULT_THREAD_MAX
OPTION_DEFAULT_COMPRESS OPTION_DEFAULT_COMPRESS_LEVEL OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK
OPTION_DEFAULT_COMMAND_REMOTE
OPTION_DEFAULT_BACKUP_FORCE OPTION_DEFAULT_BACKUP_NO_START_STOP OPTION_DEFAULT_BACKUP_TYPE
OPTION_DEFAULT_RESTORE_DELTA OPTION_DEFAULT_RESTORE_FORCE OPTION_DEFAULT_RESTORE_SET OPTION_DEFAULT_RESTORE_TYPE
OPTION_DEFAULT_RESTORE_TARGET_EXCLUSIVE OPTION_DEFAULT_RESTORE_TARGET_RESUME
OPTION_DEFAULT_TEST OPTION_DEFAULT_TEST_DELAY OPTION_DEFAULT_TEST_NO_FORK);
####################################################################################################################################
# Option Rules
####################################################################################################################################
use constant
{
OPTION_RULE_ALLOW_LIST => 'allow-list',
OPTION_RULE_ALLOW_RANGE => 'allow-range',
OPTION_RULE_DEFAULT => 'default',
OPTION_RULE_DEPEND => 'depend',
OPTION_RULE_DEPEND_OPTION => 'depend-option',
OPTION_RULE_DEPEND_LIST => 'depend-list',
OPTION_RULE_DEPEND_VALUE => 'depend-value',
OPTION_RULE_HINT => 'hint',
OPTION_RULE_NEGATE => 'negate',
OPTION_RULE_OPERATION => 'operation',
OPTION_RULE_REQUIRED => 'required',
OPTION_RULE_SECTION => 'section',
OPTION_RULE_SECTION_INHERIT => 'section-inherit',
OPTION_RULE_TYPE => 'type'
};
push @EXPORT, qw(OPTION_RULE_ALLOW_LIST OPTION_RULE_ALLOW_RANGE OPTION_RULE_DEFAULT OPTION_RULE_DEPEND OPTION_RULE_DEPEND_OPTION
OPTION_RULE_DEPEND_LIST OPTION_RULE_DEPEND_VALUE OPTION_RULE_NEGATE OPTION_RULE_OPERATION OPTION_RULE_REQUIRED
OPTION_RULE_SECTION OPTION_RULE_SECTION_INHERIT OPTION_RULE_TYPE);
####################################################################################################################################
# Option Types
####################################################################################################################################
use constant
{
OPTION_TYPE_BOOLEAN => 'boolean',
OPTION_TYPE_FLOAT => 'float',
OPTION_TYPE_HASH => 'hash',
OPTION_TYPE_INTEGER => 'integer',
OPTION_TYPE_STRING => 'string'
};
push @EXPORT, qw(OPTION_TYPE_BOOLEAN OPTION_TYPE_FLOAT OPTION_TYPE_INTEGER OPTION_TYPE_STRING);
####################################################################################################################################
# Option Rule Hash
####################################################################################################################################
my %oOptionRule =
(
# Command-line-only option rule
#-------------------------------------------------------------------------------------------------------------------------------
&OPTION_CONFIG =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_CONFIG,
&OPTION_RULE_NEGATE => true
},
&OPTION_DELTA =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_DELTA,
}
}
},
&OPTION_FORCE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_FORCE,
},
&OP_BACKUP =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_FORCE,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_NO_START_STOP,
&OPTION_RULE_DEPEND_VALUE => true
}
}
}
},
&OPTION_NO_START_STOP =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_NO_START_STOP
}
}
},
&OPTION_SET =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_SET,
}
}
},
&OPTION_STANZA =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET =>
{
&OPTION_RULE_REQUIRED => true
},
&OP_ARCHIVE_PUSH =>
{
&OPTION_RULE_REQUIRED => true
},
&OP_BACKUP =>
{
&OPTION_RULE_REQUIRED => true
},
&OP_EXPIRE =>
{
&OPTION_RULE_REQUIRED => true
},
&OP_RESTORE =>
{
&OPTION_RULE_REQUIRED => true
}
}
},
&OPTION_TARGET =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
}
}
},
&OPTION_TARGET_EXCLUSIVE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TARGET_EXCLUSIVE,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
}
}
},
&OPTION_TARGET_RESUME =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TARGET_RESUME,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
}
}
},
&OPTION_TARGET_TIMELINE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_DEFAULT => true,
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
}
}
},
&OPTION_TYPE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_TYPE,
&OPTION_RULE_ALLOW_LIST =>
{
&BACKUP_TYPE_FULL => true,
&BACKUP_TYPE_DIFF => true,
&BACKUP_TYPE_INCR => true,
}
},
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TYPE,
&OPTION_RULE_ALLOW_LIST =>
{
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true,
&RECOVERY_TYPE_PRESERVE => true,
&RECOVERY_TYPE_NONE => true,
&RECOVERY_TYPE_DEFAULT => true
}
}
}
},
&OPTION_OUTPUT =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_INFO =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_INFO_OUTPUT,
&OPTION_RULE_ALLOW_LIST =>
{
&INFO_OUTPUT_TEXT => true,
&INFO_OUTPUT_JSON => true
}
}
}
},
# Command-line/conf option rules
#-------------------------------------------------------------------------------------------------------------------------------
&OPTION_COMMAND_REMOTE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_COMMAND_REMOTE,
&OPTION_RULE_SECTION => CONFIG_SECTION_COMMAND,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET => true,
&OP_ARCHIVE_PUSH => true,
&OP_BACKUP => true,
&OP_INFO => true,
&OP_RESTORE => true
}
},
&OPTION_COMMAND_PSQL =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_COMMAND_PSQL,
&OPTION_RULE_SECTION => CONFIG_SECTION_COMMAND,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true
}
},
&OPTION_COMMAND_PSQL_OPTION =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_SECTION => CONFIG_SECTION_COMMAND,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true
},
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_COMMAND_PSQL
}
},
&OPTION_ARCHIVE_ASYNC =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_ARCHIVE_ASYNC,
&OPTION_RULE_SECTION => CONFIG_SECTION_ARCHIVE,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_PUSH => true
}
},
&OPTION_DB_HOST =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_STANZA,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true
}
},
&OPTION_DB_USER =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_SECTION => CONFIG_SECTION_STANZA,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true
},
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_DB_HOST
}
},
&OPTION_BACKUP_HOST =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_BACKUP,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET => true,
&OP_ARCHIVE_PUSH => true,
&OP_INFO => true,
&OP_RESTORE => true
},
},
&OPTION_BACKUP_USER =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_SECTION => CONFIG_SECTION_BACKUP,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET => true,
&OP_ARCHIVE_PUSH => true,
&OP_INFO => true,
&OP_RESTORE => true
},
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_BACKUP_HOST
}
},
&OPTION_REPO_PATH =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_REPO_PATH,
&OPTION_RULE_SECTION => CONFIG_SECTION_GENERAL,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET => true,
&OP_ARCHIVE_PUSH => true,
&OP_BACKUP => true,
&OP_INFO => true,
&OP_RESTORE => true,
&OP_EXPIRE => true
},
},
&OPTION_REPO_REMOTE_PATH =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_GENERAL,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET => true,
&OP_ARCHIVE_PUSH => true,
&OP_INFO => true,
&OP_RESTORE => true
},
},
&OPTION_DB_PATH =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_SECTION => CONFIG_SECTION_STANZA,
&OPTION_RULE_HINT => "Does this stanza exist?",
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET =>
{
&OPTION_RULE_REQUIRED => false
},
&OP_ARCHIVE_PUSH =>
{
&OPTION_RULE_REQUIRED => false
},
&OP_BACKUP => true
},
},
&OPTION_BUFFER_SIZE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BUFFER_SIZE,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_SECTION_INHERIT => CONFIG_SECTION_GENERAL,
&OPTION_RULE_ALLOW_RANGE => [OPTION_DEFAULT_BUFFER_SIZE_MIN, OPTION_DEFAULT_BUFFER_SIZE_MAX]
},
&OPTION_ARCHIVE_MAX_MB =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_ARCHIVE,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_PUSH => true
}
},
&OPTION_BACKUP_ARCHIVE_CHECK =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_ARCHIVE_CHECK,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP =>
{
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_NO_START_STOP,
&OPTION_RULE_DEPEND_VALUE => false
}
}
}
},
&OPTION_BACKUP_ARCHIVE_COPY =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_ARCHIVE_COPY,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP =>
{
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_BACKUP_ARCHIVE_CHECK,
&OPTION_RULE_DEPEND_VALUE => true
}
}
}
},
&OPTION_COMPRESS =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_COMPRESS,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_SECTION_INHERIT => CONFIG_SECTION_GENERAL,
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET => true,
&OP_ARCHIVE_PUSH => true,
&OP_BACKUP => true,
&OP_RESTORE => true
}
},
&OPTION_COMPRESS_LEVEL =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_COMPRESS_LEVEL,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_SECTION_INHERIT => CONFIG_SECTION_GENERAL,
&OPTION_RULE_ALLOW_RANGE => [OPTION_DEFAULT_COMPRESS_LEVEL_MIN, OPTION_DEFAULT_COMPRESS_LEVEL_MAX],
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET => true,
&OP_ARCHIVE_PUSH => true,
&OP_BACKUP => true,
&OP_INFO => true,
&OP_RESTORE => true
}
},
&OPTION_COMPRESS_LEVEL_NETWORK =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_SECTION_INHERIT => CONFIG_SECTION_GENERAL,
&OPTION_RULE_ALLOW_RANGE => [OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK_MIN, OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK_MAX],
&OPTION_RULE_OPERATION =>
{
&OP_ARCHIVE_GET => true,
&OP_ARCHIVE_PUSH => true,
&OP_BACKUP => true,
&OP_INFO => true,
&OP_RESTORE => true
}
},
&OPTION_HARDLINK =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_HARDLINK,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true
}
},
&OPTION_LOG_LEVEL_CONSOLE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_LOG_LEVEL_CONSOLE,
&OPTION_RULE_SECTION => CONFIG_SECTION_LOG,
&OPTION_RULE_ALLOW_LIST =>
{
lc(OFF) => true,
lc(ERROR) => true,
lc(WARN) => true,
lc(INFO) => true,
lc(DEBUG) => true,
lc(TRACE) => true
}
},
&OPTION_LOG_LEVEL_FILE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_LOG_LEVEL_FILE,
&OPTION_RULE_SECTION => CONFIG_SECTION_LOG,
&OPTION_RULE_ALLOW_LIST =>
{
lc(OFF) => true,
lc(ERROR) => true,
lc(WARN) => true,
lc(INFO) => true,
lc(DEBUG) => true,
lc(TRACE) => true
}
},
&OPTION_TABLESPACE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TABLESPACE,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE => true
}
},
&OPTION_RESTORE_TABLESPACE_MAP =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_HASH,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_RESTORE_TABLESPACE_MAP,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE => 1
},
},
&OPTION_RESTORE_RECOVERY_SETTING =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_HASH,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_RESTORE_RECOVERY_SETTING,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE => 1
},
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_DEFAULT => true,
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
},
&OPTION_RETENTION_ARCHIVE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_EXPIRE,
&OPTION_RULE_ALLOW_RANGE => [OPTION_DEFAULT_RETENTION_MIN, OPTION_DEFAULT_RETENTION_MAX],
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true,
&OP_EXPIRE => true
}
},
&OPTION_RETENTION_ARCHIVE_TYPE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_REQUIRED => true,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RETENTION_ARCHIVE_TYPE,
&OPTION_RULE_SECTION => CONFIG_SECTION_EXPIRE,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true,
&OP_EXPIRE => true
},
&OPTION_RULE_ALLOW_LIST =>
{
&BACKUP_TYPE_FULL => 1,
&BACKUP_TYPE_DIFF => 1,
&BACKUP_TYPE_INCR => 1
},
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_RETENTION_ARCHIVE
}
},
&OPTION_RETENTION_DIFF =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_EXPIRE,
&OPTION_RULE_ALLOW_RANGE => [OPTION_DEFAULT_RETENTION_MIN, OPTION_DEFAULT_RETENTION_MAX],
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true,
&OP_EXPIRE => true
}
},
&OPTION_RETENTION_FULL =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_EXPIRE,
&OPTION_RULE_ALLOW_RANGE => [OPTION_DEFAULT_RETENTION_MIN, OPTION_DEFAULT_RETENTION_MAX],
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true,
&OP_EXPIRE => true
}
},
&OPTION_MANIFEST_SAVE_THRESHOLD =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_MANIFEST_SAVE_THRESHOLD,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true
}
},
&OPTION_RESUME =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_RESUME,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true
}
},
&OPTION_START_FAST =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_START_FAST,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true
}
},
&OPTION_THREAD_MAX =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_THREAD_MAX,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_SECTION_INHERIT => CONFIG_SECTION_GENERAL,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true,
&OP_RESTORE => true
}
},
&OPTION_THREAD_TIMEOUT =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_SECTION_INHERIT => CONFIG_SECTION_GENERAL,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true,
&OP_RESTORE => true
}
},
# Command-line-only test option rules
#-------------------------------------------------------------------------------------------------------------------------------
&OPTION_TEST =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_TEST
},
&OPTION_TEST_DELAY =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_FLOAT,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_TEST_DELAY,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TEST,
&OPTION_RULE_DEPEND_VALUE => true
}
},
&OPTION_TEST_NO_FORK =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_TEST_NO_FORK,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TEST
}
}
);
####################################################################################################################################
# Global variables
####################################################################################################################################
my %oOption; # Option hash
my $strOperation; # Operation (backup, archive-get, ...)
my $strRemoteType; # Remote type (DB, BACKUP, NONE)
my $oRemote; # Global remote object that is created on first request (NOT THREADSAFE!)
####################################################################################################################################
# configLoad
#
# Load configuration.
####################################################################################################################################
sub configLoad
{
# Clear option in case it was loaded before
%oOption = ();
# Build hash with all valid command-line options
my %oOptionAllow;
foreach my $strKey (keys(%oOptionRule))
{
my $strOption = $strKey;
if (!defined($oOptionRule{$strKey}{&OPTION_RULE_TYPE}))
{
confess &log(ASSERT, "Option ${strKey} does not have a defined type", ERROR_ASSERT);
}
elsif ($oOptionRule{$strKey}{&OPTION_RULE_TYPE} eq OPTION_TYPE_HASH)
{
$strOption .= '=s@';
}
elsif ($oOptionRule{$strKey}{&OPTION_RULE_TYPE} ne OPTION_TYPE_BOOLEAN)
{
$strOption .= '=s';
}
$oOptionAllow{$strOption} = $strOption;
# Check if the option can be negated
if ((defined($oOptionRule{$strKey}{&OPTION_RULE_NEGATE}) &&
$oOptionRule{$strKey}{&OPTION_RULE_NEGATE}) ||
($oOptionRule{$strKey}{&OPTION_RULE_TYPE} eq OPTION_TYPE_BOOLEAN &&
defined($oOptionRule{$strKey}{&OPTION_RULE_SECTION})))
{
$strOption = "no-${strKey}";
$oOptionAllow{$strOption} = $strOption;
$oOptionRule{$strKey}{&OPTION_RULE_NEGATE} = true;
}
}
# Add help and version options
$oOptionAllow{'help'} = 'help';
$oOptionAllow{'version'} = 'version';
# Get command-line options
my %oOptionTest;
if (!GetOptions(\%oOptionTest, %oOptionAllow))
{
syswrite(*STDOUT, "\npg_backrest " . version_get() . "\n\n");
pod2usage(2);
};
# Display version and exit if requested
if (defined($oOptionTest{&OPTION_VERSION}) || defined($oOptionTest{&OPTION_HELP}))
{
syswrite(*STDOUT, 'pg_backrest ' . version_get() . "\n");
if (!defined($oOptionTest{&OPTION_HELP}))
{
exit 0;
}
}
# Display help and exit if requested
if (defined($oOptionTest{&OPTION_HELP}))
{
syswrite(*STDOUT, "\n");
pod2usage();
exit 0;
}
# Validate and store options
optionValid(\%oOptionTest);
# Replace command psql options if set
if (optionTest(OPTION_COMMAND_PSQL) && optionTest(OPTION_COMMAND_PSQL_OPTION))
{
$oOption{&OPTION_COMMAND_PSQL}{value} =~ s/\%option\%/$oOption{&OPTION_COMMAND_PSQL_OPTION}{value}/g;
}
# Set repo-remote-path to repo-path if it is not set
if (optionTest(OPTION_REPO_PATH) && !optionTest(OPTION_REPO_REMOTE_PATH))
{
$oOption{&OPTION_REPO_REMOTE_PATH}{value} = optionGet(OPTION_REPO_PATH);
}
# Check if the backup host is remote
if (optionTest(OPTION_BACKUP_HOST))
{
$strRemoteType = BACKUP;
}
# Else check if db is remote
elsif (optionTest(OPTION_DB_HOST))
{
# Don't allow both sides to be remote
if (defined($strRemoteType))
{
confess &log(ERROR, 'db and backup cannot both be configured as remote', ERROR_CONFIG);
}
$strRemoteType = DB;
}
else
{
$strRemoteType = NONE;
}
}
####################################################################################################################################
# optionValid
#
# Make sure the command-line options are valid based on the operation.
####################################################################################################################################
sub optionValid
{
my $oOptionTest = shift;
# Check that the operation is present and valid
$strOperation = $ARGV[0];
if (!defined($strOperation))
{
confess &log(ERROR, "operation must be specified", ERROR_OPERATION_REQUIRED);
}
if ($strOperation ne OP_ARCHIVE_GET &&
$strOperation ne OP_ARCHIVE_PUSH &&
$strOperation ne OP_BACKUP &&
$strOperation ne OP_INFO &&
$strOperation ne OP_RESTORE &&
$strOperation ne OP_EXPIRE)
{
confess &log(ERROR, "invalid operation ${strOperation}", ERROR_OPERATION_INVALID);
}
# Set the operation section - because of the various archive commands this is not always the operation
my $strOperationSection;
if (operationTest(OP_ARCHIVE_GET) || operationTest(OP_ARCHIVE_PUSH))
{
$strOperationSection = CONFIG_SECTION_ARCHIVE;
}
else
{
$strOperationSection = $strOperation;
}
# Hash to store contents of the config file. The file will be loaded one the config dependency is resolved unless all options
# are set on the command line or --no-config is specified.
my $oConfig;
my $bConfigExists = true;
# Keep track of unresolved dependencies
my $bDependUnresolved = true;
my %oOptionResolved;
# Loop through all possible options
while ($bDependUnresolved)
{
# Assume that all dependencies will be resolved in this loop
$bDependUnresolved = false;
foreach my $strOption (sort(keys(%oOptionRule)))
{
# Skip the option if it has been resolved in a prior loop
if (defined($oOptionResolved{$strOption}))
{
next;
}
# Store the option value since it is used a lot
my $strValue = $$oOptionTest{$strOption};
# Check to see if an option can be negated. Make sure that it is not set and negated at the same time.
my $bNegate = false;
if (defined($oOptionRule{$strOption}{&OPTION_RULE_NEGATE}) && $oOptionRule{$strOption}{&OPTION_RULE_NEGATE})
{
$bNegate = defined($$oOptionTest{'no-' . $strOption});
if ($bNegate && defined($strValue))
{
confess &log(ERROR, "option '${strOption}' cannot be both set and negated", ERROR_OPTION_NEGATE);
}
if ($bNegate && $oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_BOOLEAN)
{
$strValue = false;
}
}
# If the operation has rules store them for later evaluation
my $oOperationRule = optionOperationRule($strOption, $strOperation);
# Check dependency for the operation then for the option
my $bDependResolved = true;
my $oDepend = defined($oOperationRule) ? $$oOperationRule{&OPTION_RULE_DEPEND} :
$oOptionRule{$strOption}{&OPTION_RULE_DEPEND};
my $strDependOption;
my $strDependValue;
my $strDependType;
if (defined($oDepend))
{
# Check if the depend option has a value
$strDependOption = $$oDepend{&OPTION_RULE_DEPEND_OPTION};
$strDependValue = $oOption{$strDependOption}{value};
# Make sure the depend option has been resolved, otherwise skip this option for now
if (!defined($oOptionResolved{$strDependOption}))
{
$bDependUnresolved = true;
next;
}
if (!defined($strDependValue))
{
$bDependResolved = false;
$strDependType = 'source';
}
# If a depend value exists, make sure the option value matches
if ($bDependResolved && defined($$oDepend{&OPTION_RULE_DEPEND_VALUE}) &&
$$oDepend{&OPTION_RULE_DEPEND_VALUE} ne $strDependValue)
{
$bDependResolved = false;
$strDependType = 'value';
}
# If a depend list exists, make sure the value is in the list
if ($bDependResolved && defined($$oDepend{&OPTION_RULE_DEPEND_LIST}) &&
!defined($$oDepend{&OPTION_RULE_DEPEND_LIST}{$strDependValue}))
{
$bDependResolved = false;
$strDependType = 'list';
}
}
# If the option value is undefined and not negated, see if it can be loaded from pg_backrest.conf
if (!defined($strValue) && !$bNegate && $strOption ne OPTION_CONFIG &&
$oOptionRule{$strOption}{&OPTION_RULE_SECTION} && $bDependResolved)
{
# If the config option has not been resolved yet then continue processing
if (!defined($oOptionResolved{&OPTION_CONFIG}) || !defined($oOptionResolved{&OPTION_STANZA}))
{
$bDependUnresolved = true;
next;
}
# If the config option is defined try to get the option from the config file
if ($bConfigExists && defined($oOption{&OPTION_CONFIG}{value}))
{
# Attempt to load the config file if it has not been loaded
if (!defined($oConfig))
{
my $strConfigFile = $oOption{&OPTION_CONFIG}{value};
$bConfigExists = -e $strConfigFile;
if ($bConfigExists)
{
if (!-f $strConfigFile)
{
confess &log(ERROR, "'${strConfigFile}' is not a file", ERROR_FILE_INVALID);
}
$oConfig = iniLoad($strConfigFile, undef, true);
}
}
# Get the section that the value should be in
my $strSection = defined($oOptionRule{$strOption}{&OPTION_RULE_SECTION}) ?
($oOptionRule{$strOption}{&OPTION_RULE_SECTION} eq '1' ?
$strOperationSection : $oOptionRule{$strOption}{&OPTION_RULE_SECTION}) : undef;
# Only look in the stanza section when $strSection = true
if ($strSection eq CONFIG_SECTION_STANZA)
{
if (optionTest(OPTION_STANZA))
{
$strValue = $$oConfig{optionGet(OPTION_STANZA)}{$strOption};
}
}
# Else do a full search
else
{
# First check in the stanza section
if (optionTest(OPTION_STANZA))
{
$strValue = $oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_HASH ?
$$oConfig{optionGet(OPTION_STANZA) . ":${strSection}"} :
$$oConfig{optionGet(OPTION_STANZA) . ":${strSection}"}{$strOption};
}
# Else check for an inherited stanza section
if (!defined($strValue))
{
my $strInheritedSection = undef;
$strInheritedSection = $oOptionRule{$strOption}{&OPTION_RULE_SECTION_INHERIT};
if (defined($strInheritedSection))
{
if (optionTest(OPTION_STANZA))
{
$strValue = $$oConfig{optionGet(OPTION_STANZA) . ":${strInheritedSection}"}{$strOption};
}
}
# Else check the global section
if (!defined($strValue))
{
$strValue = $oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_HASH ?
$$oConfig{&CONFIG_GLOBAL . ":${strSection}"} :
$$oConfig{&CONFIG_GLOBAL . ":${strSection}"}{$strOption};
# Else check the global inherited section
if (!defined($strValue) && defined($strInheritedSection))
{
$strValue = $$oConfig{&CONFIG_GLOBAL . ":${strInheritedSection}"}{$strOption};
}
}
}
}
# Fix up data types
if (defined($strValue))
{
if ($strValue eq '')
{
$strValue = undef;
}
elsif ($oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_BOOLEAN)
{
if ($strValue eq 'y')
{
$strValue = true;
}
elsif ($strValue eq 'n')
{
$strValue = false;
}
else
{
confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option",
ERROR_OPTION_INVALID_VALUE);
}
}
$oOption{$strOption}{source} = SOURCE_CONFIG;
}
}
}
if (defined($oDepend) && !$bDependResolved && defined($strValue))
{
my $strError = "option '${strOption}' not valid without option '${strDependOption}'";
if ($strDependType eq 'source')
{
confess &log(ERROR, $strError, ERROR_OPTION_INVALID);
}
# If a depend value exists, make sure the option value matches
if ($strDependType eq 'value')
{
if ($oOptionRule{$strDependOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_BOOLEAN)
{
if (!$$oDepend{&OPTION_RULE_DEPEND_VALUE})
{
confess &log(ASSERT, "no error has been created for unused case where depend value = false");
}
}
else
{
$strError .= " = '$$oDepend{&OPTION_RULE_DEPEND_VALUE}'";
}
confess &log(ERROR, $strError, ERROR_OPTION_INVALID);
}
# If a depend list exists, make sure the value is in the list
if ($strDependType eq 'list')
{
my @oyValue;
foreach my $strValue (sort(keys($$oDepend{&OPTION_RULE_DEPEND_LIST})))
{
push(@oyValue, "'${strValue}'");
}
$strError .= @oyValue == 1 ? " = $oyValue[0]" : " in (" . join(", ", @oyValue) . ")";
confess &log(ERROR, $strError, ERROR_OPTION_INVALID);
}
}
# Is the option defined?
if (defined($strValue))
{
# Check that floats and integers are valid
if ($oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_INTEGER ||
$oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_FLOAT)
{
# Test that the string is a valid float or integer by adding 1 to it. It's pretty hokey but it works and it
# beats requiring Scalar::Util::Numeric to do it properly.
eval
{
my $strTest = $strValue + 1;
};
my $bError = $@ ? true : false;
# Check that integers are really integers
if (!$bError && $oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_INTEGER &&
(int($strValue) . 'S') ne ($strValue . 'S'))
{
$bError = true;
}
# Error if the value did not pass tests
!$bError
or confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE);
}
# Process an allow list for the operation then for the option
my $oAllow = defined($oOperationRule) ? $$oOperationRule{&OPTION_RULE_ALLOW_LIST} :
$oOptionRule{$strOption}{&OPTION_RULE_ALLOW_LIST};
if (defined($oAllow) && !defined($$oAllow{$strValue}))
{
confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE);
}
# Process an allow range for the operation then for the option
$oAllow = defined($oOperationRule) ? $$oOperationRule{&OPTION_RULE_ALLOW_RANGE} :
$oOptionRule{$strOption}{&OPTION_RULE_ALLOW_RANGE};
if (defined($oAllow) && ($strValue < $$oAllow[0] || $strValue > $$oAllow[1]))
{
confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_RANGE);
}
# Set option value
if ($oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_HASH && ref($strValue) eq 'ARRAY')
{
foreach my $strItem (@{$strValue})
{
# Check for = and make sure there is a least one character on each side
my $iEqualPos = index($strItem, '=');
if ($iEqualPos < 1 || length($strItem) <= $iEqualPos + 1)
{
confess &log(ERROR, "'${strItem}' not valid key/value for '${strOption}' option",
ERROR_OPTION_INVALID_PAIR);
}
# Check that the key has not already been set
my $strKey = substr($strItem, 0, $iEqualPos);
if (defined($oOption{$strOption}{$strKey}{value}))
{
confess &log(ERROR, "'${$strItem}' already defined for '${strOption}' option",
ERROR_OPTION_DUPLICATE_KEY);
}
$oOption{$strOption}{value}{$strKey} = substr($strItem, $iEqualPos + 1);
}
}
else
{
$oOption{$strOption}{value} = $strValue;
}
# If not config sourced then it must be a param
if (!defined($oOption{$strOption}{source}))
{
$oOption{$strOption}{source} = SOURCE_PARAM;
}
}
# Else try to set a default
elsif ($bDependResolved &&
(!defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) ||
defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation})))
{
# Source is default for this option
$oOption{$strOption}{source} = SOURCE_DEFAULT;
# Check for default in operation then option
my $strDefault = optionDefault($strOption, $strOperation);
# If default is defined
if (defined($strDefault))
{
# Only set default if dependency is resolved
$oOption{$strOption}{value} = $strDefault if !$bNegate;
}
# Else check required
elsif (optionRequired($strOption, $strOperation))
{
confess &log(ERROR, "${strOperation} operation requires option: ${strOption}" .
(defined($oOptionRule{$strOption}{&OPTION_RULE_HINT}) ?
"\nHINT: " . $oOptionRule{$strOption}{&OPTION_RULE_HINT} : ''),
ERROR_OPTION_REQUIRED);
}
}
$oOptionResolved{$strOption} = true;
}
}
}
####################################################################################################################################
# optionOperationRule
#
# Returns the option rules based on the operation.
####################################################################################################################################
sub optionOperationRule
{
my $strOption = shift;
my $strOperation = shift;
if (defined($strOperation))
{
return defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) &&
defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) &&
ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH' ?
$oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation} : undef;
}
return undef;
}
####################################################################################################################################
# optionRequired
#
# Is the option required for this operation?
####################################################################################################################################
sub optionRequired
{
my $strOption = shift;
my $strOperation = shift;
# Get the operation rule
my $oOperationRule = optionOperationRule($strOption, $strOperation);
# Check for required in operation then option
my $bRequired = defined($oOperationRule) ? $$oOperationRule{&OPTION_RULE_REQUIRED} :
$oOptionRule{$strOption}{&OPTION_RULE_REQUIRED};
# Return required
return !defined($bRequired) || $bRequired;
}
####################################################################################################################################
# optionDefault
#
# Does the option have a default for this operation?
####################################################################################################################################
sub optionDefault
{
my $strOption = shift;
my $strOperation = shift;
# Get the operation rule
my $oOperationRule = optionOperationRule($strOption, $strOperation);
# Check for default in operation
my $strDefault = defined($oOperationRule) ? $$oOperationRule{&OPTION_RULE_DEFAULT} : undef;
# If defined return, else try to grab the global default
return defined($strDefault) ? $strDefault : $oOptionRule{$strOption}{&OPTION_RULE_DEFAULT};
}
####################################################################################################################################
# operationGet
#
# Get the current operation.
####################################################################################################################################
sub operationGet
{
return $strOperation;
}
####################################################################################################################################
# operationTest
#
# Test the current operation.
####################################################################################################################################
sub operationTest
{
my $strOperationTest = shift;
return $strOperationTest eq $strOperation;
}
####################################################################################################################################
# operationSet
#
# Set current operation (usually for triggering follow-on operations).
####################################################################################################################################
sub operationSet
{
my $strValue = shift;
$strOperation = $strValue;
}
####################################################################################################################################
# optionGet
#
# Get option value.
####################################################################################################################################
sub optionGet
{
my $strOption = shift;
my $bRequired = shift;
if (!defined($oOption{$strOption}{value}) && (!defined($bRequired) || $bRequired))
{
confess &log(ASSERT, "option ${strOption} is required");
}
return $oOption{$strOption}{value};
}
####################################################################################################################################
# operationWrite
#
# Using the options that were passed to the current operations, write the command string for another operation. For example, this
# can be used to write the archive-get command for recovery.conf during a restore.
####################################################################################################################################
sub operationWrite
{
my $strNewOperation = shift;
my $strCommand = abs_path($0);
foreach my $strOption (sort(keys(%oOption)))
{
if ((!defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) ||
defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strNewOperation})) &&
$oOption{$strOption}{source} eq SOURCE_PARAM)
{
my $strParam = "--${strOption}=$oOption{$strOption}{value}";
if (index($oOption{$strOption}{value}, " ") != -1)
{
$strCommand .= " \"${strParam}\"";
}
else
{
$strCommand .= " ${strParam}";
}
}
}
$strCommand .= " ${strNewOperation}";
}
####################################################################################################################################
# commandWrite
#
# Test a option value.
####################################################################################################################################
sub optionTest
{
my $strOption = shift;
my $strValue = shift;
if (defined($strValue))
{
return optionGet($strOption) eq $strValue;
}
return defined($oOption{$strOption}{value});
}
####################################################################################################################################
# optionRemoteType
#
# Returns the remote type.
####################################################################################################################################
sub optionRemoteType
{
return $strRemoteType;
}
####################################################################################################################################
# optionRemoteTypeTest
#
# Test the remote type.
####################################################################################################################################
sub optionRemoteTypeTest
{
my $strTest = shift;
return $strRemoteType eq $strTest ? true : false;
}
####################################################################################################################################
# optionRemote
#
# Get the remote object or create it if does not exist. Shared remotes are used because they create an SSH connection to the remote
# host and the number of these connections should be minimized. A remote can be shared without a single thread - for new threads
# clone() should be called on the shared remote.
####################################################################################################################################
sub optionRemote
{
my $bForceLocal = shift;
my $bStore = shift;
# If force local or remote = NONE then create a local remote and return it
if ((defined($bForceLocal) && $bForceLocal) || optionRemoteTypeTest(NONE))
{
return new BackRest::Remote
(
undef, undef, undef, undef, undef,
optionGet(OPTION_BUFFER_SIZE),
operationTest(OP_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL : optionGet(OPTION_COMPRESS_LEVEL),
operationTest(OP_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK : optionGet(OPTION_COMPRESS_LEVEL_NETWORK)
);
}
# Return the remote if is already defined
if (defined($oRemote))
{
return $oRemote;
}
# Return the remote when required
my $oRemoteTemp = new BackRest::Remote
(
optionRemoteTypeTest(DB) ? optionGet(OPTION_DB_HOST) : optionGet(OPTION_BACKUP_HOST),
optionRemoteTypeTest(DB) ? optionGet(OPTION_DB_USER) : optionGet(OPTION_BACKUP_USER),
optionGet(OPTION_COMMAND_REMOTE),
optionGet(OPTION_STANZA, false),
optionGet(OPTION_REPO_REMOTE_PATH),
optionGet(OPTION_BUFFER_SIZE),
operationTest(OP_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL : optionGet(OPTION_COMPRESS_LEVEL),
operationTest(OP_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK : optionGet(OPTION_COMPRESS_LEVEL_NETWORK)
);
if ($bStore)
{
$oRemote = $oRemoteTemp;
}
return $oRemoteTemp;
}
####################################################################################################################################
# remoteDestroy
#
# Undefine the remote if it is stored locally.
####################################################################################################################################
sub remoteDestroy
{
if (defined($oRemote))
{
undef($oRemote);
}
}
####################################################################################################################################
# optionRemoteTest
#
# Test if the remote DB or BACKUP.
####################################################################################################################################
sub optionRemoteTest
{
return $strRemoteType ne NONE ? true : false;
}
####################################################################################################################################
# optionRuleGet
#
# Get the option rules.
####################################################################################################################################
sub optionRuleGet
{
use Storable qw(dclone);
return dclone(\%oOptionRule);
}
1;