You've already forked pgbackrest
							
							
				mirror of
				https://github.com/pgbackrest/pgbackrest.git
				synced 2025-10-30 23:37:45 +02:00 
			
		
		
		
	Split command-line parameter processing out into a separate file. This is in preparation allowing all parameters to be specified/overridden on the command line, with pg_backrest.conf being option.
This commit is contained in:
		| @@ -15,6 +15,7 @@ use Pod::Usage; | ||||
|  | ||||
| use lib dirname($0) . '/../lib'; | ||||
| use BackRest::Utility; | ||||
| use BackRest::Param; | ||||
| use BackRest::Config; | ||||
| use BackRest::Remote; | ||||
| use BackRest::File; | ||||
| @@ -196,7 +197,7 @@ else | ||||
| #################################################################################################################################### | ||||
| # ARCHIVE-PUSH Command | ||||
| #################################################################################################################################### | ||||
| if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
| if (operationTest(OP_ARCHIVE_PUSH)) | ||||
| { | ||||
|     # Make sure the archive push operation happens on the db side | ||||
|     if ($strRemote eq DB) | ||||
| @@ -222,7 +223,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
|  | ||||
|     if ($bArchiveLocal) | ||||
|     { | ||||
|         $strStopFile = "${strArchivePath}/lock/" . param_get(PARAM_STANZA) . "-archive.stop"; | ||||
|         $strStopFile = "${strArchivePath}/lock/" . optionGet(OPTION_STANZA) . "-archive.stop"; | ||||
|     } | ||||
|  | ||||
|     # If an archive file is defined, then push it | ||||
| @@ -244,7 +245,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
|         # Create the file object | ||||
|         my $oFile = new BackRest::File | ||||
|         ( | ||||
|             param_get(PARAM_STANZA), | ||||
|             optionGet(OPTION_STANZA), | ||||
|             config_key_load($strSection, CONFIG_KEY_PATH, true), | ||||
|             $bArchiveLocal ? NONE : $strRemote, | ||||
|             remote_get($bArchiveLocal, config_key_load(CONFIG_SECTION_ARCHIVE, CONFIG_KEY_COMPRESS_LEVEL), | ||||
| @@ -272,7 +273,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
|         } | ||||
|  | ||||
|         # Fork and exit the parent process so the async process can continue | ||||
|         if (!param_get(PARAM_TEST_NO_FORK)) | ||||
|         if (!optionTest(OPTION_TEST_NO_FORK)) | ||||
|         { | ||||
|             if (fork()) | ||||
|             { | ||||
| @@ -295,7 +296,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
|     &log(INFO, 'starting async archive-push'); | ||||
|  | ||||
|     # Create a lock file to make sure async archive-push does not run more than once | ||||
|     my $strLockPath = "${strArchivePath}/lock/" . param_get(PARAM_STANZA) . "-archive.lock"; | ||||
|     my $strLockPath = "${strArchivePath}/lock/" . optionGet(OPTION_STANZA) . "-archive.lock"; | ||||
|  | ||||
|     if (!lock_file_create($strLockPath)) | ||||
|     { | ||||
| @@ -304,7 +305,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
|     } | ||||
|  | ||||
|     # Build the basic command string that will be used to modify the command during processing | ||||
|     my $strCommand = $^X . ' ' . $0 . " --stanza=" . param_get(PARAM_STANZA); | ||||
|     my $strCommand = $^X . ' ' . $0 . " --stanza=" . optionGet(OPTION_STANZA); | ||||
|  | ||||
|     # Get the new operational flags | ||||
|     my $bCompress = config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_COMPRESS, true, 'y') eq 'y' ? true : false; | ||||
| @@ -315,7 +316,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
|         # Create the file object | ||||
|         my $oFile = new BackRest::File | ||||
|         ( | ||||
|             param_get(PARAM_STANZA), | ||||
|             optionGet(OPTION_STANZA), | ||||
|             config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true), | ||||
|             $strRemote, | ||||
|             remote_get(false, config_key_load(CONFIG_SECTION_ARCHIVE, CONFIG_KEY_COMPRESS_LEVEL), | ||||
| @@ -340,7 +341,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
|  | ||||
|         while (!defined($iLogTotal) || $iLogTotal > 0) | ||||
|         { | ||||
|             $iLogTotal = archive_xfer($strArchivePath . "/archive/" . param_get(PARAM_STANZA), $strStopFile, | ||||
|             $iLogTotal = archive_xfer($strArchivePath . "/archive/" . optionGet(OPTION_STANZA), $strStopFile, | ||||
|                                       $strCommand, $iArchiveMaxMB); | ||||
|  | ||||
|             if ($iLogTotal > 0) | ||||
| @@ -402,7 +403,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH) | ||||
| #################################################################################################################################### | ||||
| # ARCHIVE-GET Command | ||||
| #################################################################################################################################### | ||||
| if (operation_get() eq OP_ARCHIVE_GET) | ||||
| if (operationTest(OP_ARCHIVE_GET)) | ||||
| { | ||||
|     # Make sure the archive file is defined | ||||
|     if (!defined($ARGV[1])) | ||||
| @@ -419,7 +420,7 @@ if (operation_get() eq OP_ARCHIVE_GET) | ||||
|     # Init the file object | ||||
|     my $oFile = new BackRest::File | ||||
|     ( | ||||
|         param_get(PARAM_STANZA), | ||||
|         optionGet(OPTION_STANZA), | ||||
|         config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true), | ||||
|         $strRemote, | ||||
|         remote_get(false, | ||||
| @@ -446,7 +447,7 @@ if (operation_get() eq OP_ARCHIVE_GET) | ||||
| #################################################################################################################################### | ||||
| my $oFile = new BackRest::File | ||||
| ( | ||||
|     param_get(PARAM_STANZA), | ||||
|     optionGet(OPTION_STANZA), | ||||
|     config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true), | ||||
|     $strRemote, | ||||
|     remote_get(false, | ||||
| @@ -457,7 +458,7 @@ my $oFile = new BackRest::File | ||||
| #################################################################################################################################### | ||||
| # RESTORE | ||||
| #################################################################################################################################### | ||||
| if (operation_get() eq OP_RESTORE) | ||||
| if (operationTest(OP_RESTORE)) | ||||
| { | ||||
|     if ($strRemote eq DB) | ||||
|     { | ||||
| @@ -465,31 +466,31 @@ if (operation_get() eq OP_RESTORE) | ||||
|     } | ||||
|  | ||||
|     # Open the log file | ||||
|     log_file_set(config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_PATH, true) . '/log/' . param_get(PARAM_STANZA) . '-restore'); | ||||
|     log_file_set(config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_PATH, true) . '/log/' . optionGet(OPTION_STANZA) . '-restore'); | ||||
|  | ||||
|     # Set the lock path | ||||
|     my $strLockPath = config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_PATH, true) .  '/lock/' . | ||||
|                                       param_get(PARAM_STANZA) . '-' . operation_get() . '.lock'; | ||||
|                                       optionGet(OPTION_STANZA) . '-' . operationGet() . '.lock'; | ||||
|  | ||||
|     # Do the restore | ||||
|     new BackRest::Restore | ||||
|     ( | ||||
|         config_key_load(CONFIG_SECTION_STANZA, CONFIG_KEY_PATH, true), | ||||
|         param_get(PARAM_SET), | ||||
|         optionGet(OPTION_SET), | ||||
|         config_section_load(CONFIG_SECTION_TABLESPACE_MAP), | ||||
|         $oFile, | ||||
|         config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_THREAD_MAX, true), | ||||
|         param_get(PARAM_DELTA), | ||||
|         param_get(PARAM_FORCE), | ||||
|         param_get(PARAM_TYPE), | ||||
|         param_get(PARAM_TARGET), | ||||
|         param_get(PARAM_TARGET_EXCLUSIVE), | ||||
|         param_get(PARAM_TARGET_RESUME), | ||||
|         param_get(PARAM_TARGET_TIMELINE), | ||||
|         optionGet(OPTION_DELTA), | ||||
|         optionGet(OPTION_FORCE), | ||||
|         optionGet(OPTION_TYPE), | ||||
|         optionGet(OPTION_TARGET, false), | ||||
|         optionGet(OPTION_TARGET_EXCLUSIVE, false), | ||||
|         optionGet(OPTION_TARGET_RESUME, false), | ||||
|         optionGet(OPTION_TARGET_TIMELINE, false), | ||||
|         config_section_load(CONFIG_SECTION_RECOVERY_OPTION), | ||||
|         param_get(PARAM_STANZA), | ||||
|         optionGet(OPTION_STANZA), | ||||
|         $0, | ||||
|         param_get(PARAM_CONFIG) | ||||
|         optionGet(OPTION_CONFIG) | ||||
|     )->restore; | ||||
|  | ||||
|     remote_exit(0); | ||||
| @@ -499,7 +500,7 @@ if (operation_get() eq OP_RESTORE) | ||||
| # GET MORE CONFIG INFO | ||||
| #################################################################################################################################### | ||||
| # Open the log file | ||||
| log_file_set(config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true) . '/log/' . param_get(PARAM_STANZA)); | ||||
| log_file_set(config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true) . '/log/' . optionGet(OPTION_STANZA)); | ||||
|  | ||||
| # Make sure backup and expire operations happen on the backup side | ||||
| if ($strRemote eq BACKUP) | ||||
| @@ -512,18 +513,18 @@ my $bCompress = config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_COMPRESS, true | ||||
|  | ||||
| # Set the lock path | ||||
| my $strLockPath = config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true) .  '/lock/' . | ||||
|                                   param_get(PARAM_STANZA) . '-' . operation_get() . '.lock'; | ||||
|                                   optionGet(OPTION_STANZA) . '-' . operationGet() . '.lock'; | ||||
|  | ||||
| if (!lock_file_create($strLockPath)) | ||||
| { | ||||
|     &log(ERROR, 'backup process is already running for stanza ' . param_get(PARAM_STANZA) . ' - exiting'); | ||||
|     &log(ERROR, 'backup process is already running for stanza ' . optionGet(OPTION_STANZA) . ' - exiting'); | ||||
|     remote_exit(0); | ||||
| } | ||||
|  | ||||
| # Initialize the db object | ||||
| my $oDb; | ||||
|  | ||||
| if (!param_get(PARAM_NO_START_STOP)) | ||||
| if (!optionGet(OPTION_NO_START_STOP)) | ||||
| { | ||||
|     $oDb = new BackRest::Db | ||||
|     ( | ||||
| @@ -538,31 +539,31 @@ backup_init | ||||
| ( | ||||
|     $oDb, | ||||
|     $oFile, | ||||
|     param_get(PARAM_TYPE), | ||||
|     optionGet(OPTION_TYPE), | ||||
|     config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_COMPRESS, true, 'y') eq 'y' ? true : false, | ||||
|     config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_HARDLINK, true, 'y') eq 'y' ? true : false, | ||||
|     config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_THREAD_MAX), | ||||
|     config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_ARCHIVE_REQUIRED, true, 'y') eq 'y' ? true : false, | ||||
|     config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_THREAD_TIMEOUT), | ||||
|     param_get(PARAM_NO_START_STOP), | ||||
|     param_get(PARAM_FORCE) | ||||
|     optionGet(OPTION_NO_START_STOP), | ||||
|     optionTest(OPTION_FORCE) | ||||
| ); | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # BACKUP | ||||
| #################################################################################################################################### | ||||
| if (operation_get() eq OP_BACKUP) | ||||
| if (operationTest(OP_BACKUP)) | ||||
| { | ||||
|     backup(config_key_load(CONFIG_SECTION_STANZA, CONFIG_KEY_PATH), | ||||
|            config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_START_FAST, true, 'n') eq 'y' ? true : false); | ||||
|  | ||||
|     operation_set(OP_EXPIRE); | ||||
|     operationSet(OP_EXPIRE); | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # EXPIRE | ||||
| #################################################################################################################################### | ||||
| if (operation_get() eq OP_EXPIRE) | ||||
| if (operationTest(OP_EXPIRE)) | ||||
| { | ||||
|     backup_expire | ||||
|     ( | ||||
|   | ||||
| @@ -16,6 +16,7 @@ use Thread::Queue; | ||||
| use lib dirname($0); | ||||
| use BackRest::Utility; | ||||
| use BackRest::Exception; | ||||
| use BackRest::Param; | ||||
| use BackRest::Config; | ||||
| use BackRest::Manifest; | ||||
| use BackRest::File; | ||||
|   | ||||
| @@ -7,33 +7,21 @@ use strict; | ||||
| use warnings FATAL => qw(all); | ||||
| use Carp qw(confess); | ||||
|  | ||||
| use Pod::Usage; | ||||
| use File::Basename; | ||||
| use Getopt::Long; | ||||
|  | ||||
| use lib dirname($0) . '/../lib'; | ||||
| use BackRest::Exception; | ||||
| use BackRest::Utility; | ||||
| use BackRest::Param; | ||||
|  | ||||
| use Exporter qw(import); | ||||
|  | ||||
| our @EXPORT = qw(config_load config_key_load config_section_load operation_get operation_set param_get | ||||
| our @EXPORT = qw(config_load config_key_load config_section_load | ||||
|  | ||||
|                  FILE_MANIFEST FILE_VERSION FILE_POSTMASTER_PID FILE_RECOVERY_CONF | ||||
|                  PATH_LATEST | ||||
|  | ||||
|                  OP_ARCHIVE_GET OP_ARCHIVE_PUSH OP_BACKUP OP_RESTORE OP_EXPIRE | ||||
|  | ||||
|                  BACKUP_TYPE_FULL BACKUP_TYPE_DIFF BACKUP_TYPE_INCR | ||||
|  | ||||
|                  RECOVERY_TYPE_NAME RECOVERY_TYPE_TIME RECOVERY_TYPE_XID RECOVERY_TYPE_PRESERVE RECOVERY_TYPE_NONE | ||||
|                  RECOVERY_TYPE_DEFAULT | ||||
|  | ||||
|                  PARAM_CONFIG PARAM_STANZA PARAM_TYPE PARAM_DELTA PARAM_SET PARAM_NO_START_STOP PARAM_FORCE PARAM_TARGET | ||||
|                  PARAM_TARGET_EXCLUSIVE PARAM_TARGET_RESUME PARAM_TARGET_TIMELINE CONFIG_SECTION_RECOVERY | ||||
|  | ||||
|                  PARAM_VERSION PARAM_HELP PARAM_TEST PARAM_TEST_DELAY PARAM_TEST_NO_FORK | ||||
|  | ||||
|                  CONFIG_SECTION_COMMAND CONFIG_SECTION_GENERAL CONFIG_SECTION_COMMAND_OPTION CONFIG_SECTION_LOG CONFIG_SECTION_BACKUP | ||||
|                  CONFIG_SECTION_RESTORE CONFIG_SECTION_RECOVERY CONFIG_SECTION_RECOVERY_OPTION CONFIG_SECTION_TABLESPACE_MAP | ||||
|                  CONFIG_SECTION_ARCHIVE CONFIG_SECTION_RETENTION CONFIG_SECTION_STANZA | ||||
| @@ -65,68 +53,6 @@ use constant | ||||
|     FILE_VERSION        => 'version', | ||||
|     FILE_POSTMASTER_PID => 'postmaster.pid', | ||||
|     FILE_RECOVERY_CONF  => 'recovery.conf', | ||||
|  | ||||
|     PATH_LATEST         => 'latest' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # 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_RESTORE       => 'restore', | ||||
|     OP_EXPIRE        => 'expire' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # BACKUP Type Constants | ||||
| #################################################################################################################################### | ||||
| use constant | ||||
| { | ||||
|     BACKUP_TYPE_FULL          => 'full', | ||||
|     BACKUP_TYPE_DIFF          => 'diff', | ||||
|     BACKUP_TYPE_INCR          => 'incr' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # 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' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # Parameter constants | ||||
| #################################################################################################################################### | ||||
| use constant | ||||
| { | ||||
|     PARAM_CONFIG            => 'config', | ||||
|     PARAM_STANZA            => 'stanza', | ||||
|     PARAM_TYPE              => 'type', | ||||
|     PARAM_NO_START_STOP     => 'no-start-stop', | ||||
|     PARAM_DELTA             => 'delta', | ||||
|     PARAM_SET               => 'set', | ||||
|     PARAM_FORCE             => 'force', | ||||
|     PARAM_VERSION           => 'version', | ||||
|     PARAM_HELP              => 'help', | ||||
|  | ||||
|     PARAM_TARGET            => 'target', | ||||
|     PARAM_TARGET_EXCLUSIVE  => 'target-exclusive', | ||||
|     PARAM_TARGET_RESUME     => 'target-resume', | ||||
|     PARAM_TARGET_TIMELINE   => 'target-timeline', | ||||
|  | ||||
|     PARAM_TEST              => 'test', | ||||
|     PARAM_TEST_DELAY        => 'test-delay', | ||||
|     PARAM_TEST_NO_FORK      => 'no-fork' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| @@ -216,8 +142,6 @@ use constant | ||||
| # Global variables | ||||
| #################################################################################################################################### | ||||
| my %oConfig;            # Configuration hash | ||||
| my %oParam = ();        # Parameter hash | ||||
| my $strOperation;       # Operation (backup, archive-get, ...) | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # CONFIG_LOAD | ||||
| @@ -228,61 +152,9 @@ sub config_load | ||||
| { | ||||
|     my $strFile = shift;    # Full path to ini file to load from | ||||
|  | ||||
|     # Default for general parameters | ||||
|     param_set(PARAM_NO_START_STOP, false); # Do not perform start/stop backup (and archive-required gets set to false) | ||||
|     param_set(PARAM_FORCE, false);         # Force an action that would not normally be allowed (varies by action) | ||||
|     param_set(PARAM_VERSION, false);       # Display version and exit | ||||
|     param_set(PARAM_HELP, false);          # Display help and exit | ||||
|  | ||||
|     # Defaults for test parameters - not for general use | ||||
|     param_set(PARAM_TEST_NO_FORK, false);  # Prevents the archive process from forking when local archiving is enabled | ||||
|     param_set(PARAM_TEST, false);          # Enters test mode - not harmful, but adds special logging and pauses for unit testing | ||||
|     param_set(PARAM_TEST_DELAY, 5);        # Seconds to delay after a test point (default is not enough for manual tests) | ||||
|  | ||||
|     # Get command line parameters | ||||
|     GetOptions (\%oParam, PARAM_CONFIG . '=s', PARAM_STANZA . '=s', PARAM_TYPE . '=s', PARAM_DELTA, PARAM_SET . '=s', | ||||
|                           PARAM_NO_START_STOP, PARAM_FORCE, PARAM_TARGET . '=s', PARAM_TARGET_EXCLUSIVE, PARAM_TARGET_RESUME, | ||||
|                           PARAM_TARGET_TIMELINE . '=s', PARAM_VERSION, PARAM_HELP, | ||||
|                           PARAM_TEST, PARAM_TEST_DELAY . '=s', PARAM_TEST_NO_FORK) | ||||
|         or pod2usage(2); | ||||
|  | ||||
|     # Display version and exit if requested | ||||
|     if (param_get(PARAM_VERSION) || param_get(PARAM_HELP)) | ||||
|     { | ||||
|         print 'pg_backrest ' . version_get() . "\n"; | ||||
|  | ||||
|         if (!param_get(PARAM_HELP)) | ||||
|         { | ||||
|             exit 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     # Display help and exit if requested | ||||
|     if (param_get(PARAM_HELP)) | ||||
|     { | ||||
|         print "\n"; | ||||
|         pod2usage(); | ||||
|     } | ||||
|  | ||||
|     # Get and validate the operation | ||||
|     $strOperation = $ARGV[0]; | ||||
|  | ||||
|     # Validate params | ||||
|     param_valid(); | ||||
|  | ||||
|     # # Validate thread parameter | ||||
|     # if (defined(param_get(PARAM_THREAD)) && !(param_get(PARAM_THREAD) >= 1)) | ||||
|     # { | ||||
|     #     confess &log(ERROR, 'thread parameter should be >= 1'); | ||||
|     # } | ||||
|  | ||||
|     # Get configuration parameter and load it | ||||
|     if (!defined(param_get(PARAM_CONFIG))) | ||||
|     { | ||||
|         param_set(PARAM_CONFIG, '/etc/pg_backrest.conf'); | ||||
|     } | ||||
|  | ||||
|     ini_load(param_get(PARAM_CONFIG), \%oConfig); | ||||
|     # Load parameters | ||||
|     configLoad(); | ||||
|     ini_load(optionGet(OPTION_CONFIG), \%oConfig); | ||||
|  | ||||
|     # If this is a restore, then try to default config | ||||
|     if (!defined(config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_PATH))) | ||||
| @@ -304,9 +176,6 @@ sub config_load | ||||
|  | ||||
|     # Validate config | ||||
|     config_valid(); | ||||
|  | ||||
|     # Set test parameters | ||||
|     test_set(param_get(PARAM_TEST), param_get(PARAM_TEST_DELAY)); | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| @@ -316,7 +185,7 @@ sub config_section_load | ||||
| { | ||||
|     my $strSection = shift; | ||||
|  | ||||
|     $strSection = param_get(PARAM_STANZA) . ':' . $strSection; | ||||
|     $strSection = optionGet(OPTION_STANZA) . ':' . $strSection; | ||||
|  | ||||
|     return $oConfig{$strSection}; | ||||
| } | ||||
| @@ -342,13 +211,13 @@ sub config_key_load | ||||
|     # Look in the default stanza section | ||||
|     if ($strSection eq CONFIG_SECTION_STANZA) | ||||
|     { | ||||
|         $strValue = $oConfig{param_get(PARAM_STANZA)}{"${strKey}"}; | ||||
|         $strValue = $oConfig{optionGet(OPTION_STANZA)}{"${strKey}"}; | ||||
|     } | ||||
|     # Else look in the supplied section | ||||
|     else | ||||
|     { | ||||
|         # First check the stanza section | ||||
|         $strValue = $oConfig{param_get(PARAM_STANZA) . ":${strSection}"}{"${strKey}"}; | ||||
|         $strValue = $oConfig{optionGet(OPTION_STANZA) . ":${strSection}"}{"${strKey}"}; | ||||
|  | ||||
|         # If the stanza section value is undefined then check global | ||||
|         if (!defined($strValue)) | ||||
| @@ -396,7 +265,7 @@ sub config_key_set | ||||
|     } | ||||
|  | ||||
|     # Set the value | ||||
|     $strSection = param_get(PARAM_STANZA) . ':' . $strSection; | ||||
|     $strSection = optionGet(OPTION_STANZA) . ':' . $strSection; | ||||
|  | ||||
|     $oConfig{$strSection}{$strKey} = $strValue; | ||||
| } | ||||
| @@ -413,7 +282,7 @@ sub config_valid | ||||
|     my $oSectionHashRef; | ||||
|  | ||||
|     # Check [stanza]:recovery:option section | ||||
|     $strSection = param_get(PARAM_STANZA) . ':' . CONFIG_SECTION_RECOVERY_OPTION; | ||||
|     $strSection = optionGet(OPTION_STANZA) . ':' . CONFIG_SECTION_RECOVERY_OPTION; | ||||
|     $oSectionHashRef = $oConfig{$strSection}; | ||||
|  | ||||
|     if (defined($oSectionHashRef) && keys($oSectionHashRef) != 0) | ||||
| @@ -511,7 +380,7 @@ sub config_key_valid | ||||
|     $oConfig{$strSection}{$strKey} = $strValue; | ||||
|  | ||||
|     # Also do validation for the stanza section | ||||
|     my $strStanza = param_get(PARAM_STANZA); | ||||
|     my $strStanza = optionGet(OPTION_STANZA); | ||||
|  | ||||
|     if (substr($strSection, 0, length($strStanza) + 1) ne "${strStanza}:") | ||||
|     { | ||||
| @@ -519,177 +388,4 @@ sub config_key_valid | ||||
|     } | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # PARAM_VALID | ||||
| # | ||||
| # Make sure the command-line parameters are valid. | ||||
| #################################################################################################################################### | ||||
| sub param_valid | ||||
| { | ||||
|     # Check the stanza | ||||
|     if (!defined(param_get(PARAM_STANZA))) | ||||
|     { | ||||
|         confess 'a backup stanza must be specified'; | ||||
|     } | ||||
|  | ||||
|     # Check that the operation is present and valid | ||||
|     if (!defined($strOperation)) | ||||
|     { | ||||
|         confess &log(ERROR, "operation must be specified", ERROR_PARAM); | ||||
|     } | ||||
|  | ||||
|     if ($strOperation ne OP_ARCHIVE_GET && | ||||
|         $strOperation ne OP_ARCHIVE_PUSH && | ||||
|         $strOperation ne OP_BACKUP && | ||||
|         $strOperation ne OP_RESTORE && | ||||
|         $strOperation ne OP_EXPIRE) | ||||
|     { | ||||
|         confess &log(ERROR, "invalid operation ${strOperation}"); | ||||
|     } | ||||
|  | ||||
|     # Check type param | ||||
|     my $strParam = PARAM_TYPE; | ||||
|     my $strType = param_get($strParam); | ||||
|  | ||||
|     # Type is only valid for backup and restore operations | ||||
|     if (operation_test(OP_BACKUP) || operation_test(OP_RESTORE)) | ||||
|     { | ||||
|         # Check types for backup | ||||
|         if (operation_test(OP_BACKUP)) | ||||
|         { | ||||
|             # If type is not defined set to BACKUP_TYPE_INCR | ||||
|             if (!defined($strType)) | ||||
|             { | ||||
|                 $strType = BACKUP_TYPE_INCR; | ||||
|                 param_set($strParam, $strType); | ||||
|             } | ||||
|  | ||||
|             # Check that type is in valid list | ||||
|             if (!($strType eq BACKUP_TYPE_FULL || $strType eq BACKUP_TYPE_DIFF || $strType eq BACKUP_TYPE_INCR)) | ||||
|             { | ||||
|                 confess &log(ERROR, "invalid type '${strType}' for ${strOperation}, must be: '" . BACKUP_TYPE_FULL . "', '" . | ||||
|                              BACKUP_TYPE_DIFF . "', '" . BACKUP_TYPE_INCR . "'", ERROR_PARAM); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         # Check types for restore | ||||
|         elsif (operation_test(OP_RESTORE)) | ||||
|         { | ||||
|             # If type is not defined set to RECOVERY_TYPE_DEFAULT | ||||
|             if (!defined($strType)) | ||||
|             { | ||||
|                 $strType = RECOVERY_TYPE_DEFAULT; | ||||
|                 param_set($strParam, $strType); | ||||
|             } | ||||
|  | ||||
|             if (!($strType eq RECOVERY_TYPE_NAME || $strType eq RECOVERY_TYPE_TIME || $strType eq RECOVERY_TYPE_XID || | ||||
|                   $strType eq RECOVERY_TYPE_PRESERVE || $strType eq RECOVERY_TYPE_NONE || $strType eq RECOVERY_TYPE_DEFAULT)) | ||||
|             { | ||||
|                 confess &log(ERROR, "invalid type '${strType}' for ${strOperation}, must be: '" . RECOVERY_TYPE_NAME . | ||||
|                              "', '" . RECOVERY_TYPE_TIME . "', '" . RECOVERY_TYPE_XID . "', '" . RECOVERY_TYPE_PRESERVE . | ||||
|                              "', '" . RECOVERY_TYPE_NONE . "', '" . RECOVERY_TYPE_DEFAULT . "'", ERROR_PARAM); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         if (defined($strType)) | ||||
|         { | ||||
|             confess &log(ERROR, PARAM_TYPE . ' is only valid for '. OP_BACKUP . ' and ' . OP_RESTORE . ' operations', ERROR_PARAM); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     # Check target param | ||||
|     $strParam = PARAM_TARGET; | ||||
|     my $strTarget = param_get($strParam); | ||||
|     my $strTargetMessage = 'for ' . OP_RESTORE . " operations where type is '" . RECOVERY_TYPE_NAME . | ||||
|                            "', '" . RECOVERY_TYPE_TIME . "', or '" . RECOVERY_TYPE_XID . "'"; | ||||
|  | ||||
|     if (operation_test(OP_RESTORE) && | ||||
|         ($strType eq RECOVERY_TYPE_NAME || $strType eq RECOVERY_TYPE_TIME || $strType eq RECOVERY_TYPE_XID)) | ||||
|     { | ||||
|          if (!defined($strTarget)) | ||||
|          { | ||||
|              confess &log(ERROR, PARAM_TARGET . ' is required ' . $strTargetMessage, ERROR_PARAM); | ||||
|          } | ||||
|     } | ||||
|     elsif (defined($strTarget)) | ||||
|     { | ||||
|         confess &log(ERROR, PARAM_TARGET . ' is only required ' . $strTargetMessage, ERROR_PARAM); | ||||
|     } | ||||
|  | ||||
|     # Check target-resume - can only be used when target is specified | ||||
|     if (defined(param_get(PARAM_TARGET_RESUME)) && !defined($strTarget)) | ||||
|     { | ||||
|         confess &log(ERROR, PARAM_TARGET_RESUME . ' and ' . PARAM_TARGET_TIMELINE . | ||||
|                             ' are only valid when target is specified', ERROR_PARAM); | ||||
|     } | ||||
|  | ||||
|     # Check target-exclusive - can only be used when target is time or xid | ||||
|     if (defined(param_get(PARAM_TARGET_EXCLUSIVE)) && !($strType eq RECOVERY_TYPE_TIME || $strType eq RECOVERY_TYPE_XID)) | ||||
|     { | ||||
|         confess &log(ERROR, PARAM_TARGET_EXCLUSIVE . ' is only valid when target is specified and recovery type is ' . | ||||
|                             RECOVERY_TYPE_TIME . ' or ' . RECOVERY_TYPE_XID, ERROR_PARAM); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # OPERATION_GET | ||||
| # | ||||
| # Get the current operation. | ||||
| #################################################################################################################################### | ||||
| sub operation_get | ||||
| { | ||||
|     return $strOperation; | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # OPERATION_TEST | ||||
| # | ||||
| # Test the current operation. | ||||
| #################################################################################################################################### | ||||
| sub operation_test | ||||
| { | ||||
|     my $strOperationTest = shift; | ||||
|  | ||||
|     return $strOperationTest eq $strOperation; | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # OPERATION_SET | ||||
| # | ||||
| # Set current operation (usually for triggering follow-on operations). | ||||
| #################################################################################################################################### | ||||
| sub operation_set | ||||
| { | ||||
|     my $strValue = shift; | ||||
|  | ||||
|     $strOperation = $strValue; | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # PARAM_GET | ||||
| # | ||||
| # Get param value. | ||||
| #################################################################################################################################### | ||||
| sub param_get | ||||
| { | ||||
|     my $strParam = shift; | ||||
|  | ||||
|     return $oParam{$strParam}; | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # PARAM_SET | ||||
| # | ||||
| # Set param value. | ||||
| #################################################################################################################################### | ||||
| sub param_set | ||||
| { | ||||
|     my $strParam = shift; | ||||
|     my $strValue = shift; | ||||
|  | ||||
|     $oParam{$strParam} = $strValue; | ||||
| } | ||||
|  | ||||
| 1; | ||||
|   | ||||
| @@ -11,21 +11,25 @@ use Carp qw(confess); | ||||
| # Exports | ||||
| #################################################################################################################################### | ||||
| use Exporter qw(import); | ||||
| our @EXPORT = qw(ERROR_CHECKSUM ERROR_CONFIG ERROR_PARAM ERROR_POSTMASTER_RUNNING ERROR_PROTOCOL ERROR_RESTORE_PATH_NOT_EMPTY | ||||
|                  ERROR_FORMAT); | ||||
| our @EXPORT = qw(ERROR_ASSERT ERROR_CHECKSUM ERROR_CONFIG ERROR_OPERATION_REQUIRED ERROR_OPTION_REQUIRED ERROR_OPTION_INVALID | ||||
|                  ERROR_OPTION_INVALID_VALUE ERROR_POSTMASTER_RUNNING ERROR_PROTOCOL ERROR_RESTORE_PATH_NOT_EMPTY ERROR_FORMAT); | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # Exception Codes | ||||
| #################################################################################################################################### | ||||
| use constant | ||||
| { | ||||
|     ERROR_CHECKSUM                     => 100, | ||||
|     ERROR_CONFIG                       => 101, | ||||
|     ERROR_PARAM                        => 102, | ||||
|     ERROR_RESTORE_PATH_NOT_EMPTY       => 103, | ||||
|     ERROR_POSTMASTER_RUNNING           => 104, | ||||
|     ERROR_PROTOCOL                     => 105, | ||||
|     ERROR_FORMAT                       => 106 | ||||
|     ERROR_ASSERT                       => 100, | ||||
|     ERROR_CHECKSUM                     => 101, | ||||
|     ERROR_CONFIG                       => 102, | ||||
|     ERROR_FORMAT                       => 103, | ||||
|     ERROR_OPERATION_REQUIRED           => 104, | ||||
|     ERROR_OPTION_REQUIRED              => 105, | ||||
|     ERROR_OPTION_INVALID               => 106, | ||||
|     ERROR_OPTION_INVALID_VALUE         => 107, | ||||
|     ERROR_POSTMASTER_RUNNING           => 108, | ||||
|     ERROR_PROTOCOL                     => 109, | ||||
|     ERROR_RESTORE_PATH_NOT_EMPTY       => 110 | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
|   | ||||
							
								
								
									
										785
									
								
								lib/BackRest/Param.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										785
									
								
								lib/BackRest/Param.pm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,785 @@ | ||||
| #################################################################################################################################### | ||||
| # PARAM MODULE | ||||
| #################################################################################################################################### | ||||
| package BackRest::Param; | ||||
|  | ||||
| use strict; | ||||
| use warnings FATAL => qw(all); | ||||
| use Carp qw(confess); | ||||
|  | ||||
| use Pod::Usage; | ||||
| use File::Basename; | ||||
| use Getopt::Long qw(GetOptions); | ||||
| use Storable qw(dclone); | ||||
|  | ||||
| use lib dirname($0) . '/../lib'; | ||||
| use BackRest::Exception; | ||||
| use BackRest::Utility; | ||||
|  | ||||
| use Exporter qw(import); | ||||
|  | ||||
| our @EXPORT = qw(configLoad optionGet optionTest optionRuleGet operationGet operationTest operationSet | ||||
|  | ||||
|                  OP_ARCHIVE_GET OP_ARCHIVE_PUSH OP_BACKUP OP_RESTORE OP_EXPIRE | ||||
|  | ||||
|                  BACKUP_TYPE_FULL BACKUP_TYPE_DIFF BACKUP_TYPE_INCR | ||||
|  | ||||
|                  RECOVERY_TYPE_NAME RECOVERY_TYPE_TIME RECOVERY_TYPE_XID RECOVERY_TYPE_PRESERVE RECOVERY_TYPE_NONE | ||||
|                  RECOVERY_TYPE_DEFAULT | ||||
|  | ||||
|                  OPTION_CONFIG OPTION_STANZA OPTION_TYPE OPTION_DELTA OPTION_SET OPTION_NO_START_STOP OPTION_FORCE OPTION_TARGET | ||||
|                  OPTION_TARGET_EXCLUSIVE OPTION_TARGET_RESUME OPTION_TARGET_TIMELINE OPTION_THREAD_MAX | ||||
|  | ||||
|                  OPTION_VERSION OPTION_HELP OPTION_TEST OPTION_TEST_DELAY OPTION_TEST_NO_FORK | ||||
|  | ||||
|                  OPTION_DEFAULT_RESTORE_SET); | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # 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_RESTORE       => 'restore', | ||||
|     OP_EXPIRE        => 'expire' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # BACKUP Type Constants | ||||
| #################################################################################################################################### | ||||
| use constant | ||||
| { | ||||
|     BACKUP_TYPE_FULL          => 'full', | ||||
|     BACKUP_TYPE_DIFF          => 'diff', | ||||
|     BACKUP_TYPE_INCR          => 'incr' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # 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' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # 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', | ||||
|  | ||||
|     # Command-line-only/conf file options | ||||
|     OPTION_THREAD_MAX        => 'thread-max', | ||||
|  | ||||
|     # 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' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # Option Defaults | ||||
| #################################################################################################################################### | ||||
| use constant | ||||
| { | ||||
|     OPTION_DEFAULT_CONFIG                   => '/etc/pg_backrest.conf', | ||||
|     OPTION_DEFAULT_THREAD_MAX               => 1, | ||||
|  | ||||
|     OPTION_DEFAULT_BACKUP_FORCE             => false, | ||||
|     OPTION_DEFAULT_BACKUP_NO_START_STOP     => false, | ||||
|     OPTION_DEFAULT_BACKUP_TYPE              => BACKUP_TYPE_INCR, | ||||
|  | ||||
|     OPTION_DEFAULT_RESTORE_DELTA            => false, | ||||
|     OPTION_DEFAULT_RESTORE_FORCE            => false, | ||||
|     OPTION_DEFAULT_RESTORE_SET              => 'latest', | ||||
|     OPTION_DEFAULT_RESTORE_TYPE             => RECOVERY_TYPE_DEFAULT, | ||||
|     OPTION_DEFAULT_RESTORE_TARGET_EXCLUSIVE => false, | ||||
|     OPTION_DEFAULT_RESTORE_TARGET_RESUME    => false, | ||||
|  | ||||
|     OPTION_DEFAULT_HELP                     => false, | ||||
|     OPTION_DEFAULT_VERSION                  => false, | ||||
|  | ||||
|     OPTION_DEFAULT_TEST                     => false, | ||||
|     OPTION_DEFAULT_TEST_DELAY               => 5, | ||||
|     OPTION_DEFAULT_TEST_NO_FORK             => false | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # Option Rules | ||||
| #################################################################################################################################### | ||||
| use constant | ||||
| { | ||||
|     OPTION_RULE_ALLOW_LIST       => 'allow-list', | ||||
|     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_REQUIRED         => 'required', | ||||
|     OPTION_RULE_OPERATION        => 'operation', | ||||
|     OPTION_RULE_TYPE             => 'type' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # Option Types | ||||
| #################################################################################################################################### | ||||
| use constant | ||||
| { | ||||
|     OPTION_TYPE_STRING       => 'string', | ||||
|     OPTION_TYPE_BOOLEAN      => 'boolean', | ||||
|     OPTION_TYPE_INTEGER      => 'integer', | ||||
|     OPTION_TYPE_FLOAT        => 'float' | ||||
| }; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # Option Rule Hash | ||||
| #################################################################################################################################### | ||||
| my %oOptionRule = | ||||
| ( | ||||
|     &OPTION_CONFIG => | ||||
|     { | ||||
|         &OPTION_RULE_TYPE => OPTION_TYPE_STRING, | ||||
|         &OPTION_RULE_DEFAULT => OPTION_DEFAULT_CONFIG | ||||
|     }, | ||||
|  | ||||
|     &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_TYPE, | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     &OPTION_STANZA => | ||||
|     { | ||||
|         &OPTION_RULE_TYPE => OPTION_TYPE_STRING | ||||
|     }, | ||||
|  | ||||
|     &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_THREAD_MAX => | ||||
|     { | ||||
|         &OPTION_RULE_TYPE => OPTION_TYPE_INTEGER, | ||||
|         &OPTION_RULE_DEFAULT => OPTION_DEFAULT_THREAD_MAX, | ||||
|         &OPTION_RULE_OPERATION => | ||||
|         { | ||||
|             &OP_BACKUP => true, | ||||
|             &OP_RESTORE => true | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     &OPTION_HELP => | ||||
|     { | ||||
|         &OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN, | ||||
|         &OPTION_RULE_DEFAULT => OPTION_DEFAULT_HELP | ||||
|     }, | ||||
|  | ||||
|     &OPTION_VERSION => | ||||
|     { | ||||
|         &OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN, | ||||
|         &OPTION_RULE_DEFAULT => OPTION_DEFAULT_VERSION | ||||
|     }, | ||||
|  | ||||
|     &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, ...) | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # configLoad | ||||
| # | ||||
| # Load configuration. | ||||
| #################################################################################################################################### | ||||
| sub configLoad | ||||
| { | ||||
|     # Clear option in case it was loaded before | ||||
|     %oOption = (); | ||||
|  | ||||
|     # Build hash with all valid command-line options | ||||
|     my %oOption; | ||||
|  | ||||
|     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} ne OPTION_TYPE_BOOLEAN) | ||||
|         { | ||||
|             $strOption .= '=s'; | ||||
|         } | ||||
|  | ||||
|         $oOption{$strOption} = $strOption; | ||||
|     } | ||||
|  | ||||
|     # Get command-line options | ||||
|     my %oOptionTest; | ||||
|  | ||||
|     GetOptions(\%oOptionTest, %oOption) | ||||
|         or pod2usage(2); | ||||
|  | ||||
|     # Validate and store options | ||||
|     optionValid(\%oOptionTest); | ||||
|  | ||||
|     # Display version and exit if requested | ||||
|     if (optionGet(OPTION_VERSION) || optionGet(OPTION_HELP)) | ||||
|     { | ||||
|         print 'pg_backrest ' . version_get() . "\n"; | ||||
|  | ||||
|         if (!OPTION_get(OPTION_HELP)) | ||||
|         { | ||||
|             exit 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     # Display help and exit if requested | ||||
|     if (optionGet(OPTION_HELP)) | ||||
|     { | ||||
|         print "\n"; | ||||
|         pod2usage(); | ||||
|     } | ||||
|  | ||||
|     # Set test options | ||||
|     !optionGet(OPTION_TEST) or test_set(optionGet(OPTION_TEST), optionGet(OPTION_TEST_DELAY)); | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # 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_RESTORE && | ||||
|         $strOperation ne OP_EXPIRE) | ||||
|     { | ||||
|         confess &log(ERROR, "invalid operation ${strOperation}"); | ||||
|     } | ||||
|  | ||||
|     # 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; | ||||
|             } | ||||
|  | ||||
|             # Check dependency for the operation then for the option | ||||
|             my $oDepend; | ||||
|             my $bDependResolved = true; | ||||
|  | ||||
|             if (defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) && | ||||
|                 defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) && | ||||
|                 ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH') | ||||
|             { | ||||
|                 $oDepend = $oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}{&OPTION_RULE_DEPEND}; | ||||
|             } | ||||
|  | ||||
|             if (!defined($oDepend)) | ||||
|             { | ||||
|                 $oDepend = $oOptionRule{$strOption}{&OPTION_RULE_DEPEND}; | ||||
|             } | ||||
|  | ||||
|             if (defined($oDepend)) | ||||
|             { | ||||
|                 # Make sure the depend option has been resolved, otherwise skip this option for now | ||||
|                 my $strDependOption = $$oDepend{&OPTION_RULE_DEPEND_OPTION}; | ||||
|  | ||||
|                 if (!defined($oOptionResolved{$strDependOption})) | ||||
|                 { | ||||
|                     $bDependUnresolved = true; | ||||
|                     next; | ||||
|                 } | ||||
|  | ||||
|                 # Check if the depend option has a value | ||||
|                 my $strDependValue = $oOption{$strDependOption}; | ||||
|                 my $strError = "option '${strOption}' not valid without option '${strDependOption}'"; | ||||
|  | ||||
|                 $bDependResolved = defined($strDependValue) ? true : false; | ||||
|  | ||||
|                 if (!$bDependResolved && defined($$oOptionTest{$strOption})) | ||||
|                 { | ||||
|                     confess &log(ERROR, $strError, ERROR_OPTION_INVALID); | ||||
|                 } | ||||
|  | ||||
|                 # 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; | ||||
|  | ||||
|                     if (defined($$oOptionTest{$strOption})) | ||||
|                     { | ||||
|                         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 ($bDependResolved && defined($$oDepend{&OPTION_RULE_DEPEND_LIST}) && | ||||
|                     !defined($$oDepend{&OPTION_RULE_DEPEND_LIST}{$strDependValue})) | ||||
|                 { | ||||
|                     $bDependResolved = false; | ||||
|  | ||||
|                     if (defined($$oOptionTest{$strOption})) | ||||
|                     { | ||||
|                         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($$oOptionTest{$strOption})) | ||||
|             { | ||||
|                 my $strValue = $$oOptionTest{$strOption}; | ||||
|  | ||||
|                 # Test option type | ||||
|                 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; | ||||
|  | ||||
|                 if (defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) && | ||||
|                     defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) && | ||||
|                     ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH') | ||||
|                 { | ||||
|                     $oAllow = $oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}{&OPTION_RULE_ALLOW_LIST}; | ||||
|                 } | ||||
|  | ||||
|                 if (!defined($oAllow)) | ||||
|                 { | ||||
|                     $oAllow = $oOptionRule{$strOption}{&OPTION_RULE_ALLOW_LIST}; | ||||
|                 } | ||||
|  | ||||
|                 if (defined($oAllow) && !defined($$oAllow{$$oOptionTest{$strOption}})) | ||||
|                 { | ||||
|                     confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE); | ||||
|                 } | ||||
|  | ||||
|                 # Set option value | ||||
|                 $oOption{$strOption} = $strValue; | ||||
|             } | ||||
|             # Else set the default if required | ||||
|             elsif (!defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) || | ||||
|                     defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation})) | ||||
|             { | ||||
|                 # Check for default in operation then option | ||||
|                 my $strDefault; | ||||
|  | ||||
|                 if (defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) && | ||||
|                     defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) && | ||||
|                     ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH') | ||||
|                 { | ||||
|                     $strDefault = $oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}{&OPTION_RULE_DEFAULT} | ||||
|                 } | ||||
|  | ||||
|                 if (!defined($strDefault)) | ||||
|                 { | ||||
|                     $strDefault = $oOptionRule{$strOption}{&OPTION_RULE_DEFAULT}; | ||||
|                 } | ||||
|  | ||||
|                 # If default is defined | ||||
|                 if (defined($strDefault)) | ||||
|                 { | ||||
|                     # Only set default if dependency is resolved | ||||
|                     $oOption{$strOption} = $strDefault if $bDependResolved; | ||||
|                 } | ||||
|                 # Else error | ||||
|                 else | ||||
|                 { | ||||
|                     # Check for required in operation then option | ||||
|                     my $bRequired; | ||||
|  | ||||
|                     if (defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) && | ||||
|                         defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) && | ||||
|                         ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH') | ||||
|                     { | ||||
|                         $bRequired = $oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}{&OPTION_RULE_REQUIRED}; | ||||
|                     } | ||||
|  | ||||
|                     if (!defined($bRequired)) | ||||
|                     { | ||||
|                         $bRequired = $oOptionRule{$strOption}{&OPTION_RULE_REQUIRED}; | ||||
|                     } | ||||
|  | ||||
|                     if (!defined($bRequired) || $bRequired) | ||||
|                     { | ||||
|                         if ($bDependResolved) | ||||
|                         { | ||||
|                             confess &log(ERROR, "${strOperation} operation requires option: ${strOption}", ERROR_OPTION_REQUIRED); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             $oOptionResolved{$strOption} = true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # 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}) && (!defined($bRequired) || $bRequired)) | ||||
|     { | ||||
|         confess &log(ASSERT, "option ${strOption} is required"); | ||||
|     } | ||||
|  | ||||
|     return $oOption{$strOption}; | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # 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}); | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # optionRuleGet | ||||
| # | ||||
| # Get the option rules. | ||||
| #################################################################################################################################### | ||||
| sub optionRuleGet | ||||
| { | ||||
|     return dclone(\%oOptionRule); | ||||
| } | ||||
|  | ||||
| 1; | ||||
| @@ -17,6 +17,7 @@ use lib dirname($0); | ||||
| use BackRest::Exception; | ||||
| use BackRest::Utility; | ||||
| use BackRest::ThreadGroup; | ||||
| use BackRest::Param; | ||||
| use BackRest::Config; | ||||
| use BackRest::Manifest; | ||||
| use BackRest::File; | ||||
| @@ -50,6 +51,7 @@ sub new | ||||
|  | ||||
|     # Initialize variables | ||||
|     $self->{strDbClusterPath} = $strDbClusterPath; | ||||
|     $self->{strBackupPath} = $strBackupPath; | ||||
|     $self->{oRemapRef} = $oRemapRef; | ||||
|     $self->{oFile} = $oFile; | ||||
|     $self->{iThreadTotal} = defined($iThreadTotal) ? $iThreadTotal : 1; | ||||
| @@ -65,16 +67,6 @@ sub new | ||||
|     $self->{strBackRestBin} = $strBackRestBin; | ||||
|     $self->{strConfigFile} = $strConfigFile; | ||||
|  | ||||
|     # If backup path is not specified then default to latest | ||||
|     if (defined($strBackupPath)) | ||||
|     { | ||||
|         $self->{strBackupPath} = $strBackupPath; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         $self->{strBackupPath} = PATH_LATEST; | ||||
|     } | ||||
|  | ||||
|     return $self; | ||||
| } | ||||
|  | ||||
| @@ -196,7 +188,7 @@ sub manifest_load | ||||
|         # If backup is latest then set it equal to backup label, else verify that requested backup and label match | ||||
|         my $strBackupLabel = $oManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL); | ||||
|  | ||||
|         if ($self->{strBackupPath} eq PATH_LATEST) | ||||
|         if ($self->{strBackupPath} eq OPTION_DEFAULT_RESTORE_SET) | ||||
|         { | ||||
|             $self->{strBackupPath} = $strBackupLabel; | ||||
|         } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #!/usr/bin/perl | ||||
| #################################################################################################################################### | ||||
| # BackupTest.pl - Unit Tests for BackRest::File | ||||
| # BackupTest.pl - Unit Tests for BackRest::Backup and BackRest::Restore | ||||
| #################################################################################################################################### | ||||
| package BackRestTest::BackupTest; | ||||
|  | ||||
| @@ -21,6 +21,7 @@ use DBI; | ||||
| use lib dirname($0) . '/../lib'; | ||||
| use BackRest::Exception; | ||||
| use BackRest::Utility; | ||||
| use BackRest::Param; | ||||
| use BackRest::Config; | ||||
| use BackRest::Manifest; | ||||
| use BackRest::File; | ||||
| @@ -1963,7 +1964,7 @@ sub BackRestTestBackup_Test | ||||
|  | ||||
|             # Static backup parameters | ||||
|             my $bSynthetic = false; | ||||
|             my $fTestDelay = .25; | ||||
|             my $fTestDelay = 1; | ||||
|  | ||||
|             # Variable backup parameters | ||||
|             my $bDelta = true; | ||||
|   | ||||
							
								
								
									
										392
									
								
								test/lib/BackRestTest/ConfigTest.pm
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										392
									
								
								test/lib/BackRestTest/ConfigTest.pm
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,392 @@ | ||||
| #!/usr/bin/perl | ||||
| #################################################################################################################################### | ||||
| # ConfigTest.pl - Unit Tests for BackRest::Param and BackRest::Config | ||||
| #################################################################################################################################### | ||||
| package BackRestTest::ConfigTest; | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # Perl includes | ||||
| #################################################################################################################################### | ||||
| use strict; | ||||
| use warnings FATAL => qw(all); | ||||
| use Carp qw(confess); | ||||
|  | ||||
| use File::Basename qw(dirname); | ||||
| use Scalar::Util 'blessed'; | ||||
| #use Data::Dumper qw(Dumper); | ||||
| #use Scalar::Util qw(blessed); | ||||
| # use Test::More qw(no_plan); | ||||
| # use Test::Deep; | ||||
|  | ||||
| use lib dirname($0) . '/../lib'; | ||||
| use BackRest::Exception; | ||||
| use BackRest::Utility; | ||||
| use BackRest::Param; | ||||
|  | ||||
| use BackRestTest::CommonTest; | ||||
|  | ||||
| use Exporter qw(import); | ||||
| our @EXPORT = qw(BackRestTestConfig_Test); | ||||
|  | ||||
| sub optionSetTest | ||||
| { | ||||
|     my $oOption = shift; | ||||
|     my $strKey = shift; | ||||
|     my $strValue = shift; | ||||
|  | ||||
|     $$oOption{option}{$strKey} = $strValue; | ||||
| } | ||||
|  | ||||
| sub optionSetBoolTest | ||||
| { | ||||
|     my $oOption = shift; | ||||
|     my $strKey = shift; | ||||
|  | ||||
|     $$oOption{boolean}{$strKey} = true; | ||||
| } | ||||
|  | ||||
| sub operationSetTest | ||||
| { | ||||
|     my $oOption = shift; | ||||
|     my $strOperation = shift; | ||||
|  | ||||
|     $$oOption{operation} = $strOperation; | ||||
| } | ||||
|  | ||||
| sub optionRemoveTest | ||||
| { | ||||
|     my $oOption = shift; | ||||
|     my $strKey = shift; | ||||
|  | ||||
|     delete($$oOption{option}{$strKey}); | ||||
|     delete($$oOption{boolean}{$strKey}); | ||||
| } | ||||
|  | ||||
| sub argvWriteTest | ||||
| { | ||||
|     my $oOption = shift; | ||||
|  | ||||
|     @ARGV = (); | ||||
|  | ||||
|     if (defined($$oOption{boolean})) | ||||
|     { | ||||
|         foreach my $strKey (keys $$oOption{boolean}) | ||||
|         { | ||||
|             if ($$oOption{boolean}{$strKey}) | ||||
|             { | ||||
|                 $ARGV[@ARGV] = "--${strKey}"; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 $ARGV[@ARGV] = "--no-${strKey}"; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (defined($$oOption{option})) | ||||
|     { | ||||
|         foreach my $strKey (keys $$oOption{option}) | ||||
|         { | ||||
|             $ARGV[@ARGV] = "--${strKey}="; | ||||
|  | ||||
|             if (defined($$oOption{option}{$strKey})) | ||||
|             { | ||||
|                 $ARGV[@ARGV - 1] .= $$oOption{option}{$strKey}; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     $ARGV[@ARGV] = $$oOption{operation}; | ||||
|  | ||||
|     &log(INFO, "    command line: " . join(" ", @ARGV)); | ||||
|  | ||||
|     %$oOption = (); | ||||
| } | ||||
|  | ||||
| sub configLoadExpectError | ||||
| { | ||||
|     my $oOption = shift; | ||||
|     my $strOperation = shift; | ||||
|     my $iExpectedError = shift; | ||||
|     my $strErrorParam1 = shift; | ||||
|     my $strErrorParam2 = shift; | ||||
|     my $strErrorParam3 = shift; | ||||
|  | ||||
|     my $oOptionRuleExpected = optionRuleGet(); | ||||
|  | ||||
|     operationSetTest($oOption, $strOperation); | ||||
|     argvWriteTest($oOption); | ||||
|  | ||||
|     eval | ||||
|     { | ||||
|         configLoad(); | ||||
|     }; | ||||
|  | ||||
|     if ($@) | ||||
|     { | ||||
|         if (!defined($iExpectedError)) | ||||
|         { | ||||
|             confess $@; | ||||
|         } | ||||
|  | ||||
|         my $oMessage = $@; | ||||
|  | ||||
|         if (blessed($oMessage) && $oMessage->isa('BackRest::Exception')) | ||||
|         { | ||||
|             if ($oMessage->code() != $iExpectedError) | ||||
|             { | ||||
|                 confess "expected error ${iExpectedError} from configLoad but got " . $oMessage->code(); | ||||
|             } | ||||
|  | ||||
|             my $strError; | ||||
|  | ||||
|             if ($iExpectedError == ERROR_OPTION_REQUIRED) | ||||
|             { | ||||
|                 $strError = "backup operation requires option: ${strErrorParam1}"; | ||||
|             } | ||||
|             elsif ($iExpectedError == ERROR_OPERATION_REQUIRED) | ||||
|             { | ||||
|                 $strError = "operation must be specified"; | ||||
|             } | ||||
|             elsif ($iExpectedError == ERROR_OPTION_INVALID) | ||||
|             { | ||||
|                 $strError = "option '${strErrorParam1}' not valid without option '${strErrorParam2}'"; | ||||
|  | ||||
|                 if (defined($strErrorParam3)) | ||||
|                 { | ||||
|                     $strError .= @{$strErrorParam3} == 1 ? " = '$$strErrorParam3[0]'" : | ||||
|                                  " in ('" . join("', '",@{ $strErrorParam3}) . "')"; | ||||
|                 } | ||||
|             } | ||||
|             elsif ($iExpectedError == ERROR_OPTION_INVALID_VALUE) | ||||
|             { | ||||
|                 $strError = "'${strErrorParam1}' is not valid for '${strErrorParam2}' option"; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 confess "must construct message for error ${iExpectedError}, use this as an example: '" . $oMessage->message() . "'"; | ||||
|             } | ||||
|  | ||||
|             if ($oMessage->message() ne $strError) | ||||
|             { | ||||
|                 confess "expected error message \"${strError}\" from configLoad but got \"" . $oMessage->message() . "\""; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             confess "configLoad should throw BackRest::Exception:\n$oMessage"; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         if (defined($iExpectedError)) | ||||
|         { | ||||
|             confess "expected error ${iExpectedError} from configLoad but got success"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     # cmp_deeply(OPTION_rule_get(), $oOptionRuleExpected, 'compare original and new rule hashes') | ||||
|     #     or die 'comparison failed'; | ||||
| } | ||||
|  | ||||
| sub optionTestExpect | ||||
| { | ||||
|     my $strOption = shift; | ||||
|     my $strExpectedValue = shift; | ||||
|  | ||||
|     if (defined($strExpectedValue)) | ||||
|     { | ||||
|         my $strActualValue = optionGet($strOption); | ||||
|  | ||||
|         $strActualValue eq $strExpectedValue | ||||
|             or confess "expected option ${strOption} to have value ${strExpectedValue}, but ${strActualValue} found instead"; | ||||
|     } | ||||
|     elsif (optionTest($strOption)) | ||||
|     { | ||||
|         confess "expected option ${strOption} to be [undef], but " . optionGet($strOption) . ' found instead'; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # BackRestTestConfig_Test | ||||
| #################################################################################################################################### | ||||
| sub BackRestTestConfig_Test | ||||
| { | ||||
|     my $strTest = shift; | ||||
|  | ||||
|     # Setup test variables | ||||
|     my $iRun; | ||||
|     my $bCreate; | ||||
|     my $strStanza = 'main'; | ||||
|     my $oOption = {}; | ||||
|     my @oyArray; | ||||
|     use constant BOGUS => 'bogus'; | ||||
|  | ||||
|     # Print test banner | ||||
|     &log(INFO, 'CONFIG MODULE ******************************************************************'); | ||||
|  | ||||
|     #------------------------------------------------------------------------------------------------------------------------------- | ||||
|     # Test config | ||||
|     #------------------------------------------------------------------------------------------------------------------------------- | ||||
|     if ($strTest eq 'all' || $strTest eq 'option') | ||||
|     { | ||||
|         &log(INFO, "Option module\n"); | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup with no stanza')) | ||||
|         { | ||||
|             configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_REQUIRED, OPTION_STANZA); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup with boolean stanza')) | ||||
|         { | ||||
|             optionSetBoolTest($oOption, OPTION_STANZA); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP, , ERROR_OPERATION_REQUIRED); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup type defaults to ' . BACKUP_TYPE_INCR)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP); | ||||
|             optionTestExpect(OPTION_TYPE, BACKUP_TYPE_INCR); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup type set to ' . BACKUP_TYPE_FULL)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_TYPE, BACKUP_TYPE_FULL); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP); | ||||
|             optionTestExpect(OPTION_TYPE, BACKUP_TYPE_FULL); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup type invalid')) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_TYPE, BOGUS); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_TYPE); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup invalid force')) | ||||
|         { | ||||
| #            $oOption = {}; | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetBoolTest($oOption, OPTION_FORCE); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP, ERROR_OPTION_INVALID, OPTION_FORCE, OPTION_NO_START_STOP); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup valid force')) | ||||
|         { | ||||
|             # $oOption = {}; | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetBoolTest($oOption, OPTION_NO_START_STOP); | ||||
|             optionSetBoolTest($oOption, OPTION_FORCE); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP); | ||||
|             optionTestExpect(OPTION_NO_START_STOP, true); | ||||
|             optionTestExpect(OPTION_FORCE, true); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup invalid value for ' . OPTION_TEST_DELAY)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetBoolTest($oOption, OPTION_TEST); | ||||
|             optionSetTest($oOption, OPTION_TEST_DELAY, BOGUS); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_TEST_DELAY); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup invalid ' . OPTION_TEST_DELAY)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_TEST_DELAY, 5); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID, OPTION_TEST_DELAY, OPTION_TEST); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'backup check ' . OPTION_TEST_DELAY . ' undef')) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP); | ||||
|             optionTestExpect(OPTION_TEST_DELAY); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'restore invalid ' . OPTION_TARGET)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_TYPE, RECOVERY_TYPE_DEFAULT); | ||||
|             optionSetTest($oOption, OPTION_TARGET, BOGUS); | ||||
|  | ||||
|             @oyArray = (RECOVERY_TYPE_NAME, RECOVERY_TYPE_TIME, RECOVERY_TYPE_XID); | ||||
|             configLoadExpectError($oOption, OP_RESTORE , ERROR_OPTION_INVALID, OPTION_TARGET, OPTION_TYPE, \@oyArray); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'restore ' . OPTION_TARGET)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_TYPE, RECOVERY_TYPE_NAME); | ||||
|             optionSetTest($oOption, OPTION_TARGET, BOGUS); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_RESTORE); | ||||
|             optionTestExpect(OPTION_TYPE, RECOVERY_TYPE_NAME); | ||||
|             optionTestExpect(OPTION_TARGET, BOGUS); | ||||
|             optionTestExpect(OPTION_TARGET_TIMELINE); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'invalid string ' . OPTION_THREAD_MAX)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_THREAD_MAX, BOGUS); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_THREAD_MAX); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'invalid float ' . OPTION_THREAD_MAX)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_THREAD_MAX, '0.0'); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID_VALUE, '0.0', OPTION_THREAD_MAX); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'valid ' . OPTION_THREAD_MAX)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_THREAD_MAX, '2'); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'valid float ' . OPTION_TEST_DELAY)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetBoolTest($oOption, OPTION_TEST); | ||||
|             optionSetTest($oOption, OPTION_TEST_DELAY, '0.25'); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'valid int ' . OPTION_TEST_DELAY)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetBoolTest($oOption, OPTION_TEST); | ||||
|             optionSetTest($oOption, OPTION_TEST_DELAY, 3); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_BACKUP); | ||||
|         } | ||||
|  | ||||
|         if (BackRestTestCommon_Run(++$iRun, 'restore valid ' . OPTION_TARGET_TIMELINE)) | ||||
|         { | ||||
|             optionSetTest($oOption, OPTION_STANZA, $strStanza); | ||||
|             optionSetTest($oOption, OPTION_TARGET_TIMELINE, 2); | ||||
|  | ||||
|             configLoadExpectError($oOption, OP_RESTORE); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| 1; | ||||
| @@ -1,6 +1,6 @@ | ||||
| #!/usr/bin/perl | ||||
| #################################################################################################################################### | ||||
| # BackupTest.pl - Unit Tests for BackRest::File | ||||
| # UtilityTest.pl - Unit Tests for BackRest::Utility | ||||
| #################################################################################################################################### | ||||
| package BackRestTest::UtilityTest; | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ use File::Basename; | ||||
| use Getopt::Long; | ||||
| use Cwd 'abs_path'; | ||||
| use Pod::Usage; | ||||
| #use Test::More; | ||||
|  | ||||
| use lib dirname($0) . '/../lib'; | ||||
| use BackRest::Utility; | ||||
| @@ -21,6 +22,7 @@ use BackRest::Utility; | ||||
| use lib dirname($0) . '/lib'; | ||||
| use BackRestTest::CommonTest; | ||||
| use BackRestTest::UtilityTest; | ||||
| use BackRestTest::ConfigTest; | ||||
| use BackRestTest::FileTest; | ||||
| use BackRestTest::BackupTest; | ||||
|  | ||||
| @@ -102,6 +104,8 @@ if ($bVersion || $bHelp) | ||||
|     exit 0; | ||||
| } | ||||
|  | ||||
| # Test::More->builder->output('/dev/null'); | ||||
|  | ||||
| #################################################################################################################################### | ||||
| # Setup | ||||
| #################################################################################################################################### | ||||
| @@ -229,6 +233,11 @@ do | ||||
|         BackRestTestUtility_Test($strModuleTest); | ||||
|     } | ||||
|  | ||||
|     if ($strModule eq 'all' || $strModule eq 'config') | ||||
|     { | ||||
|         BackRestTestConfig_Test($strModuleTest); | ||||
|     } | ||||
|  | ||||
|     if ($strModule eq 'all' || $strModule eq 'file') | ||||
|     { | ||||
|         BackRestTestFile_Test($strModuleTest); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user