1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-12-21 23:47:33 +02:00
Files
pgbackrest/lib/BackRest/Config.pm
David Steele a59284a4cc v0.80: DBI Support, Stability, and Convenience Features
* Fixed an issue that caused the formatted timestamp for both the oldest and newest backups to be reported as the current time by the info command.  Only text output was affected -- json output reported the correct epoch values.  Reported by Michael Renner.

* Fixed protocol issue that was preventing ssh errors (especially on connection) from being logged.

* Now using Perl DBI and DBD::Pg for connections to PostgreSQL rather than psql.  The cmd-psql and cmd-psql-option settings have been removed and replaced with db-port and db-socket-path.

* Add stop-auto option to allow failed backups to automatically be stopped when a new backup starts.

* Add db-timeout option to limit the amount of time pgBackRest will wait for pg_start_backup() and pg_stop_backup() to return.

* Remove pg_control file at the beginning of the restore and copy it back at the very end.  This prevents the possibility that a partial restore can be started by PostgreSQL.

* The repository is now created and updated with consistent directory and file modes.  By default umask is set to 0000 but this can be disabled with the neutral-umask setting.

* Added checks to be sure the db-path setting is consistent with db-port by comparing the data_directory as reported by the cluster against the db-path setting and the version as reported by the cluster against the value read from pg_control.  The db-socket-path setting is checked to be sure it is an absolute path.

* Experimental support for PostgreSQL 9.5 alpha1.  This may break when the control version or WAL magic changes in future versions but will be updated in each pgBackRest release to keep pace.  All regression tests pass except for --target-resume tests (this functionality has changed in 9.5) and there is no testing yet for .partial WAL segments.

* Major refactoring of the protocol layer to support future development.

* Added vagrant test configurations for Ubuntu 14.04 and CentOS 7.

* Split most of README.md out into USERGUIDE.md and CHANGELOG.md because it was becoming unwieldy.  Changed most references to "database" in the user guide to "database cluster" for clarity.
2015-08-09 11:20:43 -04:00

2025 lines
75 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 basename);
use Getopt::Long qw(GetOptions);
use lib dirname($0) . '/../lib';
use BackRest::Exception;
use BackRest::Ini;
use BackRest::Protocol::Common;
use BackRest::Protocol::RemoteMaster;
use BackRest::Utility;
####################################################################################################################################
# Export functions
####################################################################################################################################
our @EXPORT = qw(configLoad optionGet optionTest optionRuleGet optionRequired optionDefault commandGet commandTest
commandSet commandWrite optionRemoteType optionRemoteTypeTest protocolGet optionRemoteTest
protocolDestroy);
####################################################################################################################################
# BackRest executable name used for help messages and default
####################################################################################################################################
use constant BACKREST_EXE => basename($0);
push @EXPORT, qw(BACKREST_EXE);
####################################################################################################################################
# DB/BACKUP Constants
####################################################################################################################################
use constant
{
DB => 'db',
BACKUP => 'backup',
NONE => 'none'
};
push @EXPORT, qw(DB BACKUP NONE);
####################################################################################################################################
# Command constants - basic commands that are allowed in backrest
####################################################################################################################################
use constant CMD_ARCHIVE_GET => 'archive-get';
push @EXPORT, qw(CMD_ARCHIVE_GET);
use constant CMD_ARCHIVE_PUSH => 'archive-push';
push @EXPORT, qw(CMD_ARCHIVE_PUSH);
use constant CMD_BACKUP => 'backup';
push @EXPORT, qw(CMD_BACKUP);
use constant CMD_INFO => 'info';
push @EXPORT, qw(CMD_INFO);
use constant CMD_REMOTE => 'remote';
push @EXPORT, qw(CMD_REMOTE);
use constant CMD_RESTORE => 'restore';
push @EXPORT, qw(CMD_RESTORE);
use constant CMD_EXPIRE => 'expire';
push @EXPORT, qw(CMD_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_DB_TIMEOUT => 'db-timeout',
OPTION_COMPRESS => 'compress',
OPTION_COMPRESS_LEVEL => 'compress-level',
OPTION_COMPRESS_LEVEL_NETWORK => 'compress-level-network',
OPTION_NEUTRAL_UMASK => 'neutral-umask',
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_STOP_AUTO => 'stop-auto',
OPTION_START_FAST => 'start-fast',
# COMMAND Section
OPTION_COMMAND_REMOTE => 'cmd-remote',
# 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_PORT => 'db-port',
OPTION_DB_SOCKET_PATH => 'db-socket-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_NEUTRAL_UMASK
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_STOP_AUTO OPTION_THREAD_MAX OPTION_THREAD_TIMEOUT
OPTION_DB_USER OPTION_BACKUP_USER OPTION_COMMAND_REMOTE OPTION_DB_TIMEOUT
OPTION_TABLESPACE OPTION_RESTORE_TABLESPACE_MAP OPTION_DB_PORT OPTION_DB_SOCKET_PATH
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_DB_TIMEOUT => 1800,
OPTION_DEFAULT_CONFIG => '/etc/' . BACKREST_EXE . '.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_DB_PORT => 5432,
OPTION_DEFAULT_DB_USER => 'postgres',
OPTION_DEFAULT_COMMAND_REMOTE => abs_path($0),
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_STOP_AUTO => false,
OPTION_DEFAULT_BACKUP_START_FAST => false,
OPTION_DEFAULT_BACKUP_TYPE => BACKUP_TYPE_INCR,
OPTION_DEFAULT_INFO_OUTPUT => INFO_OUTPUT_TEXT,
OPTION_DEFAULT_NEUTRAL_UMASK => true,
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_DEFAULT_DB_PORT
OPTION_DEFAULT_DB_USER);
####################################################################################################################################
# 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_COMMAND => 'command',
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_COMMAND 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_COMMAND =>
{
&CMD_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_DELTA,
}
}
},
&OPTION_FORCE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_COMMAND =>
{
&CMD_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_FORCE,
},
&CMD_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_COMMAND =>
{
&CMD_BACKUP =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_NO_START_STOP
}
}
},
&OPTION_SET =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_COMMAND =>
{
&CMD_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_SET,
}
}
},
&OPTION_STANZA =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_COMMAND =>
{
&CMD_ARCHIVE_GET =>
{
&OPTION_RULE_REQUIRED => true
},
&CMD_ARCHIVE_PUSH =>
{
&OPTION_RULE_REQUIRED => true
},
&CMD_BACKUP =>
{
&OPTION_RULE_REQUIRED => true
},
&CMD_EXPIRE =>
{
&OPTION_RULE_REQUIRED => true
},
&CMD_REMOTE =>
{
&OPTION_RULE_REQUIRED => false
},
&CMD_RESTORE =>
{
&OPTION_RULE_REQUIRED => true
}
}
},
&OPTION_TARGET =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_BACKUP =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_TYPE,
&OPTION_RULE_ALLOW_LIST =>
{
&BACKUP_TYPE_FULL => true,
&BACKUP_TYPE_DIFF => true,
&BACKUP_TYPE_INCR => true,
}
},
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_BACKUP => true,
&CMD_INFO => true,
&CMD_RESTORE => true
}
},
&OPTION_ARCHIVE_ASYNC =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_ARCHIVE_ASYNC,
&OPTION_RULE_SECTION => CONFIG_SECTION_ARCHIVE,
&OPTION_RULE_COMMAND =>
{
&CMD_ARCHIVE_PUSH => true
}
},
&OPTION_DB_HOST =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_STANZA,
&OPTION_RULE_COMMAND =>
{
&CMD_BACKUP => true
}
},
&OPTION_DB_PORT =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_DB_PORT,
&OPTION_RULE_SECTION => CONFIG_SECTION_STANZA,
&OPTION_RULE_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_REMOTE => true
}
},
&OPTION_DB_SOCKET_PATH =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_STANZA,
&OPTION_RULE_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_REMOTE => true
}
},
&OPTION_DB_USER =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_DB_USER,
&OPTION_RULE_SECTION => CONFIG_SECTION_STANZA,
&OPTION_RULE_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_INFO => true,
&CMD_RESTORE => true
},
},
&OPTION_BACKUP_USER =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_SECTION => CONFIG_SECTION_BACKUP,
&OPTION_RULE_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_INFO => true,
&CMD_RESTORE => true
},
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_BACKUP_HOST
}
},
&OPTION_NEUTRAL_UMASK =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_NEUTRAL_UMASK,
&OPTION_RULE_SECTION => CONFIG_SECTION_GENERAL
},
&OPTION_REPO_PATH =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_REPO_PATH,
&OPTION_RULE_SECTION => CONFIG_SECTION_GENERAL,
&OPTION_RULE_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_BACKUP => true,
&CMD_INFO => true,
&CMD_RESTORE => true,
&CMD_EXPIRE => true
},
},
&OPTION_REPO_REMOTE_PATH =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => CONFIG_SECTION_GENERAL,
&OPTION_RULE_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_INFO => true,
&CMD_REMOTE => true,
&CMD_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_COMMAND =>
{
&CMD_ARCHIVE_GET =>
{
&OPTION_RULE_REQUIRED => false
},
&CMD_ARCHIVE_PUSH =>
{
&OPTION_RULE_REQUIRED => false
},
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_BACKUP => true,
&CMD_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_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_BACKUP => true,
&CMD_INFO => true,
&CMD_REMOTE => true,
&CMD_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_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_BACKUP => true,
&CMD_INFO => true,
&CMD_REMOTE => true,
&CMD_RESTORE => true
}
},
&OPTION_HARDLINK =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_HARDLINK,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_COMMAND =>
{
&CMD_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_RULE_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_BACKUP => true,
&CMD_EXPIRE => true,
&CMD_INFO => true,
&CMD_RESTORE => 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_RULE_COMMAND =>
{
&CMD_ARCHIVE_GET => true,
&CMD_ARCHIVE_PUSH => true,
&CMD_BACKUP => true,
&CMD_EXPIRE => true,
&CMD_INFO => true,
&CMD_RESTORE => true
}
},
&OPTION_TABLESPACE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TABLESPACE,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_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_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_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_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_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_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_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_COMMAND =>
{
&CMD_BACKUP => true
}
},
&OPTION_RESUME =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_RESUME,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_COMMAND =>
{
&CMD_BACKUP => true
}
},
&OPTION_STOP_AUTO =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_STOP_AUTO,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_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_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_RESTORE => true
}
},
&OPTION_DB_TIMEOUT =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_DB_TIMEOUT,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_SECTION_INHERIT => CONFIG_SECTION_GENERAL,
&OPTION_RULE_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_REMOTE => true
}
},
&OPTION_THREAD_TIMEOUT =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_SECTION => true,
&OPTION_RULE_SECTION_INHERIT => CONFIG_SECTION_BACKUP,
&OPTION_RULE_COMMAND =>
{
&CMD_BACKUP => true,
&CMD_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
}
}
);
####################################################################################################################################
# Help text - not using Pod::Usage since some elements are dynamic
####################################################################################################################################
use constant BACKREST_HELP =>
"Usage:\n" .
" " . BACKREST_EXE . " [options] [command]\n" .
"\n" .
" Commands:\n" .
" archive-get retrieve an archive file from backup\n" .
" archive-push push an archive file to backup\n" .
" backup backup a cluster\n" .
" restore restore a cluster\n" .
" expire expire old backups (automatically run after backup)\n" .
"\n" .
" General Options:\n" .
" --stanza stanza (cluster) to operate on\n" .
" --config alternate path for configuration file\n" .
" (defaults to " . OPTION_DEFAULT_CONFIG . ")\n" .
" --version display version and exit\n" .
" --help display usage and exit\n" .
"\n" .
" Backup Options:\n" .
" --type type of backup to perform (full, diff, incr)\n" .
" --no-start-stop do not call pg_start/stop_backup(). Postmaster should not\n" .
" be running.\n" .
" --force force backup when --no-start-stop passed and\n" .
" postmaster.pid exists. Use with extreme caution as this\n" .
" will probably produce an inconsistent backup!\n" .
"\n" .
" Restore Options:\n" .
" --set backup set to restore (defaults to latest set).\n" .
" --delta perform a delta restore.\n" .
" --force force a restore and overwrite all existing files.\n" .
" with --delta forces size/timestamp deltas.\n" .
"\n" .
" Recovery Options:\n" .
" --type type of recovery:\n" .
" default - recover to end of archive log stream\n" .
" name - restore point target\n" .
" time - timestamp target\n" .
" xid - transaction id target\n" .
" preserve - preserve the existing recovery.conf\n" .
" none - no recovery.conf generated\n" .
" --target recovery target if type is name, time, or xid.\n" .
" --target-exclusive stop just before the recovery target\n" .
" (default is inclusive).\n" .
" --target-resume do not pause after recovery (default is to pause).\n" .
" --target-timeline recover into specified timeline\n" .
" (default is current timeline).\n" .
"\n" .
" Output Options:\n" .
" --log-level-console console log level (defaults to warn):\n" .
" off - No logging at all (not recommended)\n" .
" error - Log only errors\n" .
" warn - Log warnings and errors\n" .
" info - Log info, warnings, and errors\n" .
" debug - Log debug, info, warnings, and errors\n" .
" trace - Log trace (very verbose debugging), debug,\n" .
" info, warnings, and errors\n" .
" --log-level-file file log level (defaults to info). Same options as\n" .
" --log-level-console.";
####################################################################################################################################
# Global variables
####################################################################################################################################
my %oOption; # Option hash
my $strCommand; # Command (backup, archive-get, ...)
my $strRemoteType; # Remote type (DB, BACKUP, NONE)
my $oProtocol; # 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, "\n" . BACKREST_EXE . ' ' . BACKREST_VERSION . "\n\n");
pod2usage(2);
};
# Display version and exit if requested
if (defined($oOptionTest{&OPTION_VERSION}) || defined($oOptionTest{&OPTION_HELP}))
{
syswrite(*STDOUT, BACKREST_EXE . ' ' . BACKREST_VERSION . "\n");
if (!defined($oOptionTest{&OPTION_HELP}))
{
exit 0;
}
}
# Display help and exit if requested
if (defined($oOptionTest{&OPTION_HELP}))
{
syswrite(*STDOUT, "\n" . BACKREST_HELP . "\n");
exit 0;
}
# Validate and store options
optionValid(\%oOptionTest);
# Neutralize the umask to make the repository file/path modes more consistent
if (optionGet(OPTION_NEUTRAL_UMASK))
{
umask(0000);
}
# 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}{source} = $oOption{&OPTION_REPO_PATH}{source};
$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 command.
####################################################################################################################################
sub optionValid
{
my $oOptionTest = shift;
# Check that the command is present and valid
$strCommand = $ARGV[0];
if (!defined($strCommand))
{
confess &log(ERROR, "command must be specified", ERROR_COMMAND_REQUIRED);
}
if ($strCommand ne CMD_ARCHIVE_GET &&
$strCommand ne CMD_ARCHIVE_PUSH &&
$strCommand ne CMD_BACKUP &&
$strCommand ne CMD_INFO &&
$strCommand ne CMD_REMOTE &&
$strCommand ne CMD_RESTORE &&
$strCommand ne CMD_EXPIRE)
{
confess &log(ERROR, "invalid command ${strCommand}", ERROR_COMMAND_INVALID);
}
# Set the command section - because of the various archive commands this is not always the command
my $strCommandSection;
if (commandTest(CMD_ARCHIVE_GET) || commandTest(CMD_ARCHIVE_PUSH))
{
$strCommandSection = CONFIG_SECTION_ARCHIVE;
}
else
{
$strCommandSection = $strCommand;
}
# 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 command has rules store them for later evaluation
my $oCommandRule = optionCommandRule($strOption, $strCommand);
# Check dependency for the command then for the option
my $bDependResolved = true;
my $oDepend = defined($oCommandRule) ? $$oCommandRule{&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 the config file
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' ?
$strCommandSection : $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 command then for the option
my $oAllow = defined($oCommandRule) ? $$oCommandRule{&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 command then for the option
$oAllow = defined($oCommandRule) ? $$oCommandRule{&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_COMMAND}) ||
defined($oOptionRule{$strOption}{&OPTION_RULE_COMMAND}{$strCommand})))
{
# Source is default for this option
$oOption{$strOption}{source} = SOURCE_DEFAULT;
# Check for default in command then option
my $strDefault = optionDefault($strOption, $strCommand);
# 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, $strCommand))
{
confess &log(ERROR, "${strCommand} command requires option: ${strOption}" .
(defined($oOptionRule{$strOption}{&OPTION_RULE_HINT}) ?
"\nHINT: " . $oOptionRule{$strOption}{&OPTION_RULE_HINT} : ''),
ERROR_OPTION_REQUIRED);
}
}
$oOptionResolved{$strOption} = true;
}
}
}
####################################################################################################################################
# optionCommandRule
#
# Returns the option rules based on the command.
####################################################################################################################################
sub optionCommandRule
{
my $strOption = shift;
my $strCommand = shift;
if (defined($strCommand))
{
return defined($oOptionRule{$strOption}{&OPTION_RULE_COMMAND}) &&
defined($oOptionRule{$strOption}{&OPTION_RULE_COMMAND}{$strCommand}) &&
ref($oOptionRule{$strOption}{&OPTION_RULE_COMMAND}{$strCommand}) eq 'HASH' ?
$oOptionRule{$strOption}{&OPTION_RULE_COMMAND}{$strCommand} : undef;
}
return undef;
}
####################################################################################################################################
# optionRequired
#
# Is the option required for this command?
####################################################################################################################################
sub optionRequired
{
my $strOption = shift;
my $strCommand = shift;
# Get the command rule
my $oCommandRule = optionCommandRule($strOption, $strCommand);
# Check for required in command then option
my $bRequired = defined($oCommandRule) ? $$oCommandRule{&OPTION_RULE_REQUIRED} :
$oOptionRule{$strOption}{&OPTION_RULE_REQUIRED};
# Return required
return !defined($bRequired) || $bRequired;
}
####################################################################################################################################
# optionDefault
#
# Does the option have a default for this command?
####################################################################################################################################
sub optionDefault
{
my $strOption = shift;
my $strCommand = shift;
# Get the command rule
my $oCommandRule = optionCommandRule($strOption, $strCommand);
# Check for default in command
my $strDefault = defined($oCommandRule) ? $$oCommandRule{&OPTION_RULE_DEFAULT} : undef;
# If defined return, else try to grab the global default
return defined($strDefault) ? $strDefault : $oOptionRule{$strOption}{&OPTION_RULE_DEFAULT};
}
####################################################################################################################################
# commandGet
#
# Get the current command.
####################################################################################################################################
sub commandGet
{
return $strCommand;
}
####################################################################################################################################
# commandTest
#
# Test the current command.
####################################################################################################################################
sub commandTest
{
my $strCommandTest = shift;
return $strCommandTest eq $strCommand;
}
####################################################################################################################################
# commandSet
#
# Set current command (usually for triggering follow-on commands).
####################################################################################################################################
sub commandSet
{
my $strValue = shift;
$strCommand = $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};
}
####################################################################################################################################
# commandWrite
#
# Using the options that were passed to the current command, write the command string for another command. For example, this
# can be used to write the archive-get command for recovery.conf during a restore.
####################################################################################################################################
sub commandWrite
{
my $strNewCommand = shift;
my $bIncludeConfig = shift;
my $strCommandString = shift;
$strCommandString = defined($strCommandString) ? $strCommandString : abs_path($0);
# If config setting are included then also set --no-config
$bIncludeConfig = defined($bIncludeConfig) ? $bIncludeConfig : false;
if ($bIncludeConfig)
{
$strCommandString .= ' --no-config';
}
foreach my $strOption (sort(keys(%oOption)))
{
next if ($bIncludeConfig && $strOption eq OPTION_CONFIG);
# &log(WARN, "option ${strOption} = " . (defined($oOption{$strOption}{source}) ? $oOption{$strOption}{source} : 'undef') .
# ", " . (defined($oOption{$strOption}{value}) ? $oOption{$strOption}{value} : 'undef'));
if ((!defined($oOptionRule{$strOption}{&OPTION_RULE_COMMAND}) ||
defined($oOptionRule{$strOption}{&OPTION_RULE_COMMAND}{$strNewCommand})) &&
defined($oOption{$strOption}{value}) &&
($bIncludeConfig ? $oOption{$strOption}{source} ne SOURCE_DEFAULT : $oOption{$strOption}{source} eq SOURCE_PARAM))
{
my $strParam;
if ($oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_BOOLEAN)
{
$strParam = '--' . ($oOption{$strOption}{value} ? '' : 'no-') . $strOption;
}
else
{
$strParam = "--${strOption}=$oOption{$strOption}{value}";
}
if (index($oOption{$strOption}{value}, " ") != -1)
{
$strCommandString .= " \"${strParam}\"";
}
else
{
$strCommandString .= " ${strParam}";
}
}
}
$strCommandString .= " ${strNewCommand}";
}
####################################################################################################################################
# optionTest
#
# 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;
}
####################################################################################################################################
# protocolGet
#
# Get the protocol object or create it if does not exist. Shared protocol objects are used because they create an SSH connection
# to the remote host and the number of these connections should be minimized. A protocol object can be shared within a single
# thread - for new threads clone() should be called on the shared protocol object.
####################################################################################################################################
sub protocolGet
{
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::Protocol::Common
(
optionGet(OPTION_BUFFER_SIZE),
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL : optionGet(OPTION_COMPRESS_LEVEL),
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK : optionGet(OPTION_COMPRESS_LEVEL_NETWORK)
);
}
# Return the remote if is already defined
if (defined($oProtocol))
{
return $oProtocol;
}
# Return the remote when required
my $oProtocolTemp = new BackRest::Protocol::RemoteMaster
(
commandWrite(CMD_REMOTE, true, optionGet(OPTION_COMMAND_REMOTE)),
optionGet(OPTION_BUFFER_SIZE),
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL : optionGet(OPTION_COMPRESS_LEVEL),
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK : optionGet(OPTION_COMPRESS_LEVEL_NETWORK),
optionRemoteTypeTest(DB) ? optionGet(OPTION_DB_HOST) : optionGet(OPTION_BACKUP_HOST),
optionRemoteTypeTest(DB) ? optionGet(OPTION_DB_USER) : optionGet(OPTION_BACKUP_USER)
);
if (!defined($bStore) || $bStore)
{
$oProtocol = $oProtocolTemp;
}
return $oProtocolTemp;
}
####################################################################################################################################
# protocolDestroy
#
# Undefine the protocol if it is stored locally.
####################################################################################################################################
sub protocolDestroy
{
if (defined($oProtocol))
{
undef($oProtocol);
}
}
####################################################################################################################################
# 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;