You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-08-10 22:21:39 +02:00
Implement help command in C.
This commit is contained in:
@@ -6,7 +6,7 @@ pgBackRest aims to be a simple, reliable backup and restore system that can seam
|
||||
|
||||
Instead of relying on traditional backup tools like tar and rsync, pgBackRest implements all backup features internally and uses a custom protocol for communicating with remote systems. Removing reliance on tar and rsync allows for better solutions to database-specific backup challenges. The custom remote protocol allows for more flexibility and limits the types of connections that are required to perform a backup which increases security.
|
||||
|
||||
pgBackRest [v1.26](https://github.com/pgbackrest/pgbackrest/releases/tag/release/1.26) is the current stable release. Release notes are on the [Releases](http://www.pgbackrest.org/release.html) page.
|
||||
pgBackRest [v1.27](https://github.com/pgbackrest/pgbackrest/releases/tag/release/1.27) is the current stable release. Release notes are on the [Releases](http://www.pgbackrest.org/release.html) page.
|
||||
|
||||
## Features
|
||||
|
||||
|
@@ -18,6 +18,8 @@ use pgBackRest::Common::Log;
|
||||
use pgBackRest::Common::String;
|
||||
use pgBackRest::Version;
|
||||
|
||||
use BackRestDoc::Common::DocConfig;
|
||||
|
||||
use pgBackRestBuild::Build::Common;
|
||||
use pgBackRestBuild::Config::Data;
|
||||
|
||||
@@ -107,6 +109,69 @@ sub buildConfigDefineOptionEnum
|
||||
|
||||
push @EXPORT, qw(buildConfigDefineOptionEnum);
|
||||
|
||||
####################################################################################################################################
|
||||
# Helper function to format help text
|
||||
####################################################################################################################################
|
||||
sub helpFormatText
|
||||
{
|
||||
my $oManifest = shift;
|
||||
my $oDocRender = shift;
|
||||
my $oText = shift;
|
||||
my $iIndent = shift;
|
||||
my $iLength = shift;
|
||||
|
||||
# Split the string into lines for processing
|
||||
my @stryText = split("\n", trim($oManifest->variableReplace($oDocRender->processText($oText))));
|
||||
my $strText;
|
||||
my $iIndex = 0;
|
||||
|
||||
foreach my $strLine (@stryText)
|
||||
{
|
||||
# Add a linefeed if this is not the first line
|
||||
if (defined($strText))
|
||||
{
|
||||
$strText .= "\n";
|
||||
}
|
||||
|
||||
# Escape perl special characters
|
||||
$strLine =~ s/\"/\\"/g;
|
||||
|
||||
my $strPart;
|
||||
my $bFirst = true;
|
||||
|
||||
# Split the line for output if it's too long
|
||||
do
|
||||
{
|
||||
($strPart, $strLine) = stringSplit($strLine, ' ', defined($strPart) ? $iLength - 4 : $iLength);
|
||||
|
||||
$strText .= ' ' x $iIndent;
|
||||
|
||||
if (!$bFirst)
|
||||
{
|
||||
$strText .= " ";
|
||||
}
|
||||
|
||||
$strText .= "\"${strPart}";
|
||||
|
||||
if (defined($strLine))
|
||||
{
|
||||
$strText .= "\"\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$strText .= ($iIndex + 1 < @stryText ? '\n' : '') . '"';
|
||||
}
|
||||
|
||||
$bFirst = false;
|
||||
}
|
||||
while (defined($strLine));
|
||||
|
||||
$iIndex++;
|
||||
}
|
||||
|
||||
return $strText;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Helper functions for building optional option data
|
||||
####################################################################################################################################
|
||||
@@ -161,6 +226,11 @@ sub renderOptional
|
||||
{
|
||||
my $rhOptional = shift;
|
||||
my $bCommand = shift;
|
||||
my $rhOptionHelp = shift;
|
||||
my $oManifest = shift;
|
||||
my $oDocRender = shift;
|
||||
my $strCommand = shift;
|
||||
my $strOption = shift;
|
||||
|
||||
my $strIndent = $bCommand ? ' ' : '';
|
||||
my $strBuildSourceOptional;
|
||||
@@ -231,6 +301,25 @@ sub renderOptional
|
||||
$bSingleLine = true;
|
||||
}
|
||||
|
||||
if ($bCommand && defined($rhOptionHelp) && $rhOptionHelp->{&CONFIG_HELP_SOURCE} eq CONFIG_HELP_SOURCE_COMMAND)
|
||||
{
|
||||
my $strSummary = helpFormatText($oManifest, $oDocRender, $rhOptionHelp->{&CONFIG_HELP_SUMMARY}, 0, 72);
|
||||
|
||||
if (length($strSummary) > 74)
|
||||
{
|
||||
confess("summary for command '${strCommand}', option '${strOption}' may not be greater than 72 characters");
|
||||
}
|
||||
|
||||
$strBuildSourceOptional .=
|
||||
(defined($strBuildSourceOptional) ? "\n" : '') .
|
||||
"${strIndent} CFGDEFDATA_OPTION_OPTIONAL_HELP_SUMMARY(${strSummary})\n" .
|
||||
"${strIndent} CFGDEFDATA_OPTION_OPTIONAL_HELP_DESCRIPTION\n" .
|
||||
"${strIndent} (\n" .
|
||||
helpFormatText($oManifest, $oDocRender, $rhOptionHelp->{&CONFIG_HELP_DESCRIPTION}, 20, 111) . "\n" .
|
||||
"${strIndent} )\n";
|
||||
|
||||
}
|
||||
|
||||
return $strBuildSourceOptional;
|
||||
}
|
||||
|
||||
@@ -239,6 +328,26 @@ sub renderOptional
|
||||
####################################################################################################################################
|
||||
sub buildConfigDefine
|
||||
{
|
||||
# Load help data
|
||||
#-------------------------------------------------------------------------------------------------------------------------------
|
||||
require BackRestDoc::Common::Doc;
|
||||
require BackRestDoc::Common::DocManifest;
|
||||
|
||||
my $strDocPath = abs_path(dirname($0) . '/../doc');
|
||||
|
||||
my $oStorageDoc = new pgBackRest::Storage::Local(
|
||||
$strDocPath, new pgBackRest::Storage::Posix::Driver({bFileSync => false, bPathSync => false}));
|
||||
|
||||
my @stryEmpty = [];
|
||||
my $oManifest = new BackRestDoc::Common::DocManifest(
|
||||
$oStorageDoc, \@stryEmpty, \@stryEmpty, \@stryEmpty, \@stryEmpty, undef, $strDocPath, false, false);
|
||||
|
||||
my $oDocRender = new BackRestDoc::Common::DocRender('text', $oManifest, false);
|
||||
my $oDocConfig =
|
||||
new BackRestDoc::Common::DocConfig(
|
||||
new BackRestDoc::Common::Doc("${strDocPath}/xml/reference.xml"), $oDocRender);
|
||||
my $hConfigHelp = $oDocConfig->{oConfigHash};
|
||||
|
||||
# Build command constants and data
|
||||
#-------------------------------------------------------------------------------------------------------------------------------
|
||||
my $rhEnum = $rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_ENUM}{&BLDLCL_ENUM_COMMAND};
|
||||
@@ -249,6 +358,9 @@ sub buildConfigDefine
|
||||
|
||||
foreach my $strCommand (cfgDefineCommandList())
|
||||
{
|
||||
# Get command help
|
||||
my $hCommandHelp = $hConfigHelp->{&CONFIG_HELP_COMMAND}{$strCommand};
|
||||
|
||||
# Build C enum
|
||||
my $strCommandEnum = buildConfigDefineCommandEnum($strCommand);
|
||||
push(@{$rhEnum->{&BLD_LIST}}, $strCommandEnum);
|
||||
@@ -258,7 +370,35 @@ sub buildConfigDefine
|
||||
"\n" .
|
||||
" CFGDEFDATA_COMMAND\n" .
|
||||
" (\n" .
|
||||
" CFGDEFDATA_COMMAND_NAME(\"${strCommand}\")\n" .
|
||||
" CFGDEFDATA_COMMAND_NAME(\"${strCommand}\")\n";
|
||||
|
||||
|
||||
# Output help
|
||||
if (defined($hCommandHelp))
|
||||
{
|
||||
$strBuildSource .=
|
||||
"\n";
|
||||
|
||||
# Output command summary
|
||||
my $strSummary = helpFormatText($oManifest, $oDocRender, $hCommandHelp->{&CONFIG_HELP_SUMMARY}, 0, 72);
|
||||
|
||||
if (length($strSummary) > 74)
|
||||
{
|
||||
confess("summary for command '${strCommand}' may not be greater than 72 characters");
|
||||
}
|
||||
|
||||
$strBuildSource .=
|
||||
" CFGDEFDATA_COMMAND_HELP_SUMMARY(${strSummary})\n";
|
||||
|
||||
# Output description
|
||||
$strBuildSource .=
|
||||
" CFGDEFDATA_COMMAND_HELP_DESCRIPTION\n" .
|
||||
" (\n" .
|
||||
helpFormatText($oManifest, $oDocRender, $hCommandHelp->{&CONFIG_HELP_DESCRIPTION}, 12, 119) . "\n" .
|
||||
" )\n";
|
||||
}
|
||||
|
||||
$strBuildSource .=
|
||||
" )\n";
|
||||
};
|
||||
|
||||
@@ -289,6 +429,9 @@ sub buildConfigDefine
|
||||
|
||||
foreach my $strOption (sort(keys(%{$rhConfigDefine})))
|
||||
{
|
||||
# Get option help
|
||||
my $hOptionHelp = $hConfigHelp->{&CONFIG_HELP_OPTION}{$strOption};
|
||||
|
||||
# Build C enum
|
||||
my $strOptionEnum = buildConfigDefineOptionEnum($strOption);
|
||||
push(@{$rhEnum->{&BLD_LIST}}, $strOptionEnum);
|
||||
@@ -317,7 +460,45 @@ sub buildConfigDefine
|
||||
"\n" .
|
||||
" CFGDEFDATA_OPTION_INDEX_TOTAL(" . $rhOption->{&CFGDEF_INDEX_TOTAL} . ")\n" .
|
||||
" CFGDEFDATA_OPTION_NEGATE(" . ($rhOption->{&CFGDEF_NEGATE} ? 'true' : 'false') . ")\n" .
|
||||
" CFGDEFDATA_OPTION_SECURE(" . ($rhOption->{&CFGDEF_SECURE} ? 'true' : 'false') . ")\n" .
|
||||
" CFGDEFDATA_OPTION_SECURE(" . ($rhOption->{&CFGDEF_SECURE} ? 'true' : 'false') . ")\n";
|
||||
|
||||
if (defined($hOptionHelp))
|
||||
{
|
||||
$strBuildSource .=
|
||||
"\n";
|
||||
|
||||
# Output section
|
||||
my $strSection =
|
||||
defined($hOptionHelp->{&CONFIG_HELP_SECTION}) ? $hOptionHelp->{&CONFIG_HELP_SECTION} : 'general';
|
||||
|
||||
if (length($strSection) > 72)
|
||||
{
|
||||
confess("section for option '${strOption}' may not be greater than 72 characters");
|
||||
}
|
||||
|
||||
$strBuildSource .=
|
||||
" CFGDEFDATA_OPTION_HELP_SECTION(\"${strSection}\")\n";
|
||||
|
||||
# Output summary
|
||||
my $strSummary = helpFormatText($oManifest, $oDocRender, $hOptionHelp->{&CONFIG_HELP_SUMMARY}, 0, 72);
|
||||
|
||||
if (length($strSummary) > 74)
|
||||
{
|
||||
confess("summary for option '${strOption}' may not be greater than 72 characters");
|
||||
}
|
||||
|
||||
$strBuildSource .=
|
||||
" CFGDEFDATA_OPTION_HELP_SUMMARY(${strSummary})\n";
|
||||
|
||||
# Output description
|
||||
$strBuildSource .=
|
||||
" CFGDEFDATA_OPTION_HELP_DESCRIPTION\n" .
|
||||
" (\n" .
|
||||
helpFormatText($oManifest, $oDocRender, $hOptionHelp->{&CONFIG_HELP_DESCRIPTION}, 12, 119) . "\n" .
|
||||
" )\n";
|
||||
}
|
||||
|
||||
$strBuildSource .=
|
||||
"\n" .
|
||||
" CFGDEFDATA_OPTION_COMMAND_LIST\n" .
|
||||
" (\n";
|
||||
@@ -345,7 +526,9 @@ sub buildConfigDefine
|
||||
|
||||
if (defined($rhCommand))
|
||||
{
|
||||
$strBuildSourceOptionalCommand = renderOptional($rhCommand, true);
|
||||
$strBuildSourceOptionalCommand = renderOptional(
|
||||
$rhCommand, true, $hConfigHelp->{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption},
|
||||
$oManifest, $oDocRender, $strCommand, $strOption);
|
||||
|
||||
if (defined($strBuildSourceOptionalCommand))
|
||||
{
|
||||
|
18
doc/doc.pl
18
doc/doc.pl
@@ -240,14 +240,13 @@ eval
|
||||
|
||||
if ($oManifest->isBackRest())
|
||||
{
|
||||
push(@stryOutput, 'help');
|
||||
push(@stryOutput, 'man');
|
||||
}
|
||||
}
|
||||
|
||||
for my $strOutput (@stryOutput)
|
||||
{
|
||||
if (!(($strOutput eq 'help' || $strOutput eq 'man') && $oManifest->isBackRest()))
|
||||
if (!($strOutput eq 'man' && $oManifest->isBackRest()))
|
||||
{
|
||||
$oManifest->renderGet($strOutput);
|
||||
}
|
||||
@@ -267,7 +266,7 @@ eval
|
||||
|
||||
$oMarkdown->process();
|
||||
}
|
||||
elsif (($strOutput eq 'help' || $strOutput eq 'man') && $oManifest->isBackRest())
|
||||
elsif ($strOutput eq 'man' && $oManifest->isBackRest())
|
||||
{
|
||||
# Generate the command-line help
|
||||
my $oRender = new BackRestDoc::Common::DocRender('text', $oManifest, !$bNoExe);
|
||||
@@ -275,16 +274,9 @@ eval
|
||||
new BackRestDoc::Common::DocConfig(
|
||||
new BackRestDoc::Common::Doc("${strBasePath}/xml/reference.xml"), $oRender);
|
||||
|
||||
if ($strOutput eq 'help')
|
||||
{
|
||||
$oDocConfig->helpDataWrite($oManifest);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oStorageDoc->pathCreate(
|
||||
"${strBasePath}/output/man", {strMode => '0770', bIgnoreExists => true, bCreateParent => true});
|
||||
$oStorageDoc->put("${strBasePath}/output/man/" . lc(BACKREST_NAME) . '.1.txt', $oDocConfig->manGet($oManifest));
|
||||
}
|
||||
$oStorageDoc->pathCreate(
|
||||
"${strBasePath}/output/man", {strMode => '0770', bIgnoreExists => true, bCreateParent => true});
|
||||
$oStorageDoc->put("${strBasePath}/output/man/" . lc(BACKREST_NAME) . '.1.txt', $oDocConfig->manGet($oManifest));
|
||||
}
|
||||
elsif ($strOutput eq 'html')
|
||||
{
|
||||
|
@@ -30,12 +30,16 @@ use constant CONFIG_HELP_NAME => 'name';
|
||||
use constant CONFIG_HELP_OPTION => 'option';
|
||||
push @EXPORT, qw(CONFIG_HELP_OPTION);
|
||||
use constant CONFIG_HELP_SECTION => 'section';
|
||||
push @EXPORT, qw(CONFIG_HELP_SECTION);
|
||||
use constant CONFIG_HELP_SUMMARY => 'summary';
|
||||
push @EXPORT, qw(CONFIG_HELP_SUMMARY);
|
||||
|
||||
use constant CONFIG_HELP_SOURCE => 'source';
|
||||
push @EXPORT, qw(CONFIG_HELP_SOURCE);
|
||||
use constant CONFIG_HELP_SOURCE_DEFAULT => 'default';
|
||||
use constant CONFIG_HELP_SOURCE_SECTION => CONFIG_HELP_SECTION;
|
||||
use constant CONFIG_HELP_SOURCE_COMMAND => CONFIG_HELP_COMMAND;
|
||||
push @EXPORT, qw(CONFIG_HELP_SOURCE_COMMAND);
|
||||
|
||||
####################################################################################################################################
|
||||
# Config Section Types
|
||||
@@ -339,263 +343,6 @@ sub process
|
||||
logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# helpDataWrite
|
||||
#
|
||||
# Write help data into a perl module so it can be accessed by backrest for command-line help.
|
||||
####################################################################################################################################
|
||||
sub helpDataWrite
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$oManifest
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->helpDataWrite', \@_,
|
||||
{name => 'oManifest'}
|
||||
);
|
||||
|
||||
# Iterate options
|
||||
my $oConfigHash = $self->{oConfigHash};
|
||||
my $strOptionData;
|
||||
|
||||
foreach my $strOption (sort(keys(%{$$oConfigHash{&CONFIG_HELP_OPTION}})))
|
||||
{
|
||||
my $oOptionHash = $$oConfigHash{&CONFIG_HELP_OPTION}{$strOption};
|
||||
|
||||
if (defined($strOptionData))
|
||||
{
|
||||
$strOptionData .= ",\n\n";
|
||||
}
|
||||
|
||||
# Format the option for output
|
||||
$strOptionData .=
|
||||
' # ' . uc($strOption) . " Option Help\n" .
|
||||
' #' . ('-' x 123) . "\n" .
|
||||
" '${strOption}' =>\n" .
|
||||
" {\n" .
|
||||
(defined($$oOptionHash{&CONFIG_HELP_SECTION}) ? ' ' . &CONFIG_HELP_SECTION .
|
||||
' => \'' . $$oOptionHash{&CONFIG_HELP_SECTION} . "',\n" : '') .
|
||||
' ' . &CONFIG_HELP_SUMMARY . " =>\n" .
|
||||
helpDataWriteFormatText($oManifest, $self->{oDocRender}, $$oOptionHash{&CONFIG_HELP_SUMMARY}, 16, 112) . ",\n" .
|
||||
' ' . &CONFIG_HELP_DESCRIPTION . " =>\n" .
|
||||
helpDataWriteFormatText($oManifest, $self->{oDocRender}, $$oOptionHash{&CONFIG_HELP_DESCRIPTION}, 16, 112) . "\n" .
|
||||
" }";
|
||||
}
|
||||
|
||||
# Iterate commands
|
||||
my $strCommandData;
|
||||
|
||||
foreach my $strCommand (sort(keys(%{$$oConfigHash{&CONFIG_HELP_COMMAND}})))
|
||||
{
|
||||
my $oCommandHash = $$oConfigHash{&CONFIG_HELP_COMMAND}{$strCommand};
|
||||
|
||||
if (defined($strCommandData))
|
||||
{
|
||||
$strCommandData .= ",\n\n";
|
||||
}
|
||||
|
||||
# Format the command for output
|
||||
$strCommandData .=
|
||||
' # ' . uc($strCommand) . " Command Help\n" .
|
||||
' #' . ('-' x 123) . "\n" .
|
||||
" '${strCommand}' =>\n" .
|
||||
" {\n" .
|
||||
' ' . &CONFIG_HELP_SUMMARY . " =>\n" .
|
||||
helpDataWriteFormatText($oManifest, $self->{oDocRender}, $$oCommandHash{&CONFIG_HELP_SUMMARY}, 16, 112) . ",\n" .
|
||||
' ' . &CONFIG_HELP_DESCRIPTION . " =>\n" .
|
||||
helpDataWriteFormatText($oManifest, $self->{oDocRender}, $$oCommandHash{&CONFIG_HELP_DESCRIPTION}, 16, 112) . ",\n" .
|
||||
"\n";
|
||||
|
||||
# Iterate options
|
||||
my $strOptionData;
|
||||
my $bExtraLinefeed = false;
|
||||
|
||||
if (defined($$oCommandHash{&CONFIG_HELP_OPTION}))
|
||||
{
|
||||
$strCommandData .=
|
||||
' ' . CONFIG_HELP_OPTION . " =>\n" .
|
||||
" {\n";
|
||||
|
||||
foreach my $strOption (sort(keys(%{$$oCommandHash{&CONFIG_HELP_OPTION}})))
|
||||
{
|
||||
my $oOptionHash = $$oCommandHash{&CONFIG_HELP_OPTION}{$strOption};
|
||||
|
||||
if (defined($strOptionData))
|
||||
{
|
||||
$strOptionData .= ",\n";
|
||||
}
|
||||
|
||||
# If option came from the command then output details
|
||||
if ($$oOptionHash{&CONFIG_HELP_SOURCE} eq CONFIG_HELP_SOURCE_COMMAND)
|
||||
{
|
||||
if (defined($strOptionData))
|
||||
{
|
||||
$strOptionData .= "\n";
|
||||
}
|
||||
|
||||
# Format the command option for output
|
||||
$strOptionData .=
|
||||
' # ' . uc($strOption) . " Option Help\n" .
|
||||
' #' . ('-' x 115) . "\n" .
|
||||
" '${strOption}' =>\n" .
|
||||
" {\n" .
|
||||
' ' . &CONFIG_HELP_SUMMARY . " =>\n" .
|
||||
helpDataWriteFormatText($oManifest, $self->{oDocRender},
|
||||
$$oOptionHash{&CONFIG_HELP_SUMMARY}, 24, 104) . ",\n" .
|
||||
' ' . &CONFIG_HELP_DESCRIPTION . " =>\n" .
|
||||
helpDataWriteFormatText($oManifest, $self->{oDocRender},
|
||||
$$oOptionHash{&CONFIG_HELP_DESCRIPTION}, 24, 104) . "\n" .
|
||||
" }";
|
||||
|
||||
$bExtraLinefeed = true;
|
||||
}
|
||||
# Else output a reference to indicate the option is in the global option list
|
||||
else
|
||||
{
|
||||
if ($bExtraLinefeed)
|
||||
{
|
||||
$strOptionData .= "\n";
|
||||
$bExtraLinefeed = false;
|
||||
}
|
||||
|
||||
$strOptionData .=
|
||||
" '${strOption}' => '" . $$oOptionHash{&CONFIG_HELP_SOURCE} . "'";
|
||||
}
|
||||
}
|
||||
|
||||
$strCommandData .=
|
||||
$strOptionData . "\n" .
|
||||
" }\n";
|
||||
}
|
||||
|
||||
$strCommandData .=
|
||||
" }";
|
||||
}
|
||||
|
||||
# Format the perl module
|
||||
my $strHelpData =
|
||||
('#' x 132) . "\n" .
|
||||
"# CONFIG HELP DATA MODULE\n" .
|
||||
"#\n" .
|
||||
"# This module is automatically generated by doc.pl and should never be manually edited.\n" .
|
||||
('#' x 132) . "\n" .
|
||||
"package pgBackRest::Config::ConfigHelpData;\n" .
|
||||
"\n" .
|
||||
"use strict;\n" .
|
||||
"use warnings FATAL => qw(all);\n" .
|
||||
"use Carp qw(confess);\n" .
|
||||
"\n" .
|
||||
"use Exporter qw(import);\n" .
|
||||
" our \@EXPORT = qw();\n" .
|
||||
"\n" .
|
||||
('#' x 132) . "\n" .
|
||||
"# Data used by the ConfigHelp module to generate command-line help\n" .
|
||||
('#' x 132) . "\n" .
|
||||
"my \$oConfigHelpData =\n".
|
||||
"{\n" .
|
||||
" # Option Help\n" .
|
||||
' #' . ('-' x 127) . "\n" .
|
||||
" " . CONFIG_HELP_OPTION . " =>\n" .
|
||||
" {\n" .
|
||||
$strOptionData . "\n" .
|
||||
" },\n" .
|
||||
"\n" .
|
||||
" # Command Help\n" .
|
||||
' #' . ('-' x 127) . "\n" .
|
||||
" " . CONFIG_HELP_COMMAND . " =>\n" .
|
||||
" {\n" .
|
||||
$strCommandData . "\n" .
|
||||
" }\n" .
|
||||
"};\n" .
|
||||
"\n" .
|
||||
('#' x 132) . "\n" .
|
||||
"# configHelpDataGet\n" .
|
||||
('#' x 132) . "\n" .
|
||||
"sub configHelpDataGet\n" .
|
||||
"{\n" .
|
||||
" return \$oConfigHelpData;\n" .
|
||||
"}\n" .
|
||||
"\n" .
|
||||
"push \@EXPORT, qw(configHelpDataGet);\n" .
|
||||
"\n" .
|
||||
"1;\n";
|
||||
|
||||
# Write the perl module into the lib path
|
||||
$oManifest->storage()->put(dirname(dirname($0)) . '/lib/pgBackRest/Config/ConfigHelpData.pm', $strHelpData);
|
||||
|
||||
# Return from function and log return values if any
|
||||
logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
# Helper function for helpDataWrite() used to format text by quoting it and splitting lines so it looks good in the module.
|
||||
sub helpDataWriteFormatText
|
||||
{
|
||||
my $oManifest = shift;
|
||||
my $oDocRender = shift;
|
||||
my $oText = shift;
|
||||
my $iIndent = shift;
|
||||
my $iLength = shift;
|
||||
|
||||
# Split the string into lines for processing
|
||||
my @stryText = split("\n", trim($oManifest->variableReplace($oDocRender->processText($oText))));
|
||||
my $strText;
|
||||
my $iIndex = 0;
|
||||
|
||||
foreach my $strLine (@stryText)
|
||||
{
|
||||
# Add a linefeed if this is not the first line
|
||||
if (defined($strText))
|
||||
{
|
||||
$strText .= " .\n";
|
||||
}
|
||||
|
||||
# Escape perl special characters
|
||||
$strLine =~ s/\@/\\@/g;
|
||||
$strLine =~ s/\$/\\\$/g;
|
||||
$strLine =~ s/\"/\\"/g;
|
||||
|
||||
my $strPart;
|
||||
my $bFirst = true;
|
||||
|
||||
# Split the line for output if it's too long
|
||||
do
|
||||
{
|
||||
($strPart, $strLine) = stringSplit($strLine, ' ', defined($strPart) ? $iLength - 4 : $iLength);
|
||||
|
||||
$strText .= ' ' x $iIndent;
|
||||
|
||||
if (!$bFirst)
|
||||
{
|
||||
$strText .= " ";
|
||||
}
|
||||
|
||||
$strText .= "\"${strPart}";
|
||||
|
||||
if (defined($strLine))
|
||||
{
|
||||
$strText .= "\" .\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$strText .= ($iIndex + 1 < @stryText ? '\n' : '') . '"';
|
||||
}
|
||||
|
||||
$bFirst = false;
|
||||
}
|
||||
while (defined($strLine));
|
||||
|
||||
$iIndex++;
|
||||
}
|
||||
|
||||
return $strText;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# manGet
|
||||
#
|
||||
|
@@ -851,6 +851,12 @@ sub processText
|
||||
# $strBuffer =~ s/\_/\\_/g;
|
||||
}
|
||||
|
||||
if ($strType eq 'text')
|
||||
{
|
||||
$strBuffer =~ s/\&mdash\;/--/g;
|
||||
$strBuffer =~ s/\<\;/\</g;
|
||||
}
|
||||
|
||||
$strBuffer = $self->variableReplace($strBuffer);
|
||||
|
||||
# Return from function and log return values if any
|
||||
|
@@ -267,11 +267,11 @@
|
||||
|
||||
<!-- CONFIG - REPO SECTION - REPO-PATH KEY -->
|
||||
<config-key id="repo-path" name="Repository Path">
|
||||
<summary>Repository path where WAL segments and backups stored.</summary>
|
||||
<summary>Path where backups and archive are stored.</summary>
|
||||
|
||||
<text>The repository is where <backrest/> stores backup and archives WAL segments.
|
||||
<text>The repository is where <backrest/> stores backups and archives WAL segments.
|
||||
|
||||
If you are new to backup then it will be difficult to estimate in advance how much space you'll need. The best thing to do is take some backups then record the size of different types of backups (full/incr/diff) and measure the amount of WAL generated per day. This will give you a general idea of how much space you'll need, though of course requirements will likely change over time as your database evolves.</text>
|
||||
It may be difficult to estimate in advance how much space you'll need. The best thing to do is take some backups then record the size of different types of backups (full/incr/diff) and measure the amount of WAL generated per day. This will give you a general idea of how much space you'll need, though of course requirements will likely change over time as your database evolves.</text>
|
||||
|
||||
<example>/backup/db/backrest</example>
|
||||
</config-key>
|
||||
@@ -405,7 +405,7 @@
|
||||
<config-key-list>
|
||||
<!-- CONFIG - BACKUP SECTION - ARCHIVE-CHECK -->
|
||||
<config-key id="archive-check" name="Check Archive">
|
||||
<summary>Check that WAL segments are present in the archive before backup completes.</summary>
|
||||
<summary>Check that WAL segments are in the archive before backup completes.</summary>
|
||||
|
||||
<text>Checks that all WAL segments required to make the backup consistent are present in the WAL archive. It's a good idea to leave this as the default unless you are using another method for archiving.
|
||||
|
||||
|
@@ -53,6 +53,10 @@
|
||||
</release-improvement-list>
|
||||
|
||||
<release-development-list>
|
||||
<release-item>
|
||||
<p>Implement <cmd>help</cmd> command in C.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Implement <cmd>version</cmd> command in C.</p>
|
||||
</release-item>
|
||||
|
@@ -160,6 +160,8 @@ use constant ERROR_MEMORY => ERROR_MIN
|
||||
push @EXPORT, qw(ERROR_MEMORY);
|
||||
use constant ERROR_CIPHER => ERROR_MINIMUM + 70;
|
||||
push @EXPORT, qw(ERROR_CIPHER);
|
||||
use constant ERROR_PARAM_INVALID => ERROR_MINIMUM + 71;
|
||||
push @EXPORT, qw(ERROR_PARAM_INVALID);
|
||||
|
||||
use constant ERROR_INVALID_VALUE => ERROR_MAXIMUM - 2;
|
||||
push @EXPORT, qw(ERROR_INVALID_VALUE);
|
||||
|
@@ -52,7 +52,6 @@ use constant CFGDEF_SECTION_STANZA => 'stanza';
|
||||
####################################################################################################################################
|
||||
my %oOption; # Option hash
|
||||
my $strCommand; # Command (backup, archive-get, ...)
|
||||
my $strCommandHelp; # The command that help is being generate for
|
||||
my $bInitLog = false; # Has logging been initialized yet?
|
||||
|
||||
####################################################################################################################################
|
||||
@@ -133,38 +132,13 @@ sub configLoad
|
||||
# Get command-line options
|
||||
my %oOptionTest;
|
||||
|
||||
# If nothing was passed on the command line then display help
|
||||
if (@ARGV == 0)
|
||||
# Parse command line options
|
||||
if (!GetOptions(\%oOptionTest, @stryOptionAllow))
|
||||
{
|
||||
cfgCommandSet(CFGCMD_HELP);
|
||||
confess &log(ASSERT, "error parsing command line");
|
||||
}
|
||||
# Else process command line options
|
||||
else
|
||||
{
|
||||
# Parse command line options
|
||||
if (!GetOptions(\%oOptionTest, @stryOptionAllow))
|
||||
{
|
||||
$strCommand = cfgCommandName(CFGCMD_HELP);
|
||||
return false;
|
||||
}
|
||||
|
||||
# Validate and store options
|
||||
my $bHelp = false;
|
||||
|
||||
if (defined($ARGV[0]) && $ARGV[0] eq cfgCommandName(CFGCMD_HELP) && defined($ARGV[1]))
|
||||
{
|
||||
$bHelp = true;
|
||||
$strCommandHelp = $ARGV[1];
|
||||
$ARGV[0] = $ARGV[1];
|
||||
}
|
||||
|
||||
optionValidate(\%oOptionTest, $bHelp);
|
||||
|
||||
if ($bHelp)
|
||||
{
|
||||
cfgCommandSet(CFGCMD_HELP);
|
||||
}
|
||||
}
|
||||
optionValidate(\%oOptionTest);
|
||||
|
||||
# If this is not the remote and logging is allowed (to not overwrite log levels for tests) then set the log level so that
|
||||
# INFO/WARN messages can be displayed (the user may still disable them). This should be run before any WARN logging is
|
||||
@@ -348,7 +322,6 @@ sub optionValueGet
|
||||
sub optionValidate
|
||||
{
|
||||
my $oOptionTest = shift;
|
||||
my $bHelp = shift;
|
||||
|
||||
# Check that the command is present and valid
|
||||
$strCommand = $ARGV[0];
|
||||
@@ -765,7 +738,7 @@ sub optionValidate
|
||||
$oOption{$strOption}{value} = $strDefault if !$oOption{$strOption}{negate};
|
||||
}
|
||||
# Else check required
|
||||
elsif (cfgDefOptionRequired($iCommandId, $iOptionId) && !$bHelp)
|
||||
elsif (cfgDefOptionRequired($iCommandId, $iOptionId))
|
||||
{
|
||||
confess &log(ERROR,
|
||||
"${strCommand} command requires option: ${strOption}" .
|
||||
@@ -943,12 +916,7 @@ sub cfgOptionValid
|
||||
# If defined then this is the command help is being generated for so all valid checks should be against that command
|
||||
my $iCommandId;
|
||||
|
||||
if (defined($strCommandHelp))
|
||||
{
|
||||
$iCommandId = cfgCommandId($strCommandHelp);
|
||||
}
|
||||
# Else try to use the normal command
|
||||
elsif (defined($strCommand))
|
||||
if (defined($strCommand))
|
||||
{
|
||||
$iCommandId = cfgCommandId($strCommand);
|
||||
}
|
||||
|
@@ -1,424 +0,0 @@
|
||||
####################################################################################################################################
|
||||
# CONFIG HELP MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRest::Config::ConfigHelp;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess);
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use File::Basename qw(dirname);
|
||||
|
||||
use pgBackRest::Common::Exception;
|
||||
use pgBackRest::Common::Ini;
|
||||
use pgBackRest::Common::Log;
|
||||
use pgBackRest::Common::String;
|
||||
use pgBackRest::Config::Config;
|
||||
use pgBackRest::Version;
|
||||
|
||||
####################################################################################################################################
|
||||
# Help types
|
||||
####################################################################################################################################
|
||||
use constant CONFIG_HELP_COMMAND => 'command';
|
||||
push @EXPORT, qw(CONFIG_HELP_COMMAND);
|
||||
use constant CONFIG_HELP_CURRENT => 'current';
|
||||
push @EXPORT, qw(CONFIG_HELP_CURRENT);
|
||||
use constant CONFIG_HELP_DEFAULT => 'default';
|
||||
push @EXPORT, qw(CONFIG_HELP_DEFAULT);
|
||||
use constant CONFIG_HELP_DESCRIPTION => 'description';
|
||||
push @EXPORT, qw(CONFIG_HELP_DESCRIPTION);
|
||||
use constant CONFIG_HELP_OPTION => 'option';
|
||||
push @EXPORT, qw(CONFIG_HELP_OPTION);
|
||||
use constant CONFIG_HELP_SECTION => 'section';
|
||||
push @EXPORT, qw(CONFIG_HELP_SECTION);
|
||||
use constant CONFIG_HELP_SUMMARY => 'summary';
|
||||
push @EXPORT, qw(CONFIG_HELP_SUMMARY);
|
||||
|
||||
use constant CONFIG_HELP_SOURCE => 'source';
|
||||
push @EXPORT, qw(CONFIG_HELP_SOURCE);
|
||||
use constant CONFIG_HELP_SOURCE_DEFAULT => 'default';
|
||||
push @EXPORT, qw(CONFIG_HELP_SOURCE_DEFAULT);
|
||||
use constant CONFIG_HELP_SOURCE_SECTION => CONFIG_HELP_SECTION;
|
||||
push @EXPORT, qw(CONFIG_HELP_SOURCE_SECTION);
|
||||
use constant CONFIG_HELP_SOURCE_COMMAND => CONFIG_HELP_COMMAND;
|
||||
push @EXPORT, qw(CONFIG_HELP_SOURCE_COMMAND);
|
||||
|
||||
####################################################################################################################################
|
||||
# Config Section Types
|
||||
####################################################################################################################################
|
||||
use constant CONFIG_SECTION_COMMAND => 'command';
|
||||
push @EXPORT, qw(CONFIG_SECTION_COMMAND);
|
||||
use constant CONFIG_SECTION_GENERAL => 'general';
|
||||
push @EXPORT, qw(CONFIG_SECTION_GENERAL);
|
||||
use constant CONFIG_SECTION_LOG => 'log';
|
||||
push @EXPORT, qw(CONFIG_SECTION_LOG);
|
||||
use constant CONFIG_SECTION_EXPIRE => 'expire';
|
||||
push @EXPORT, qw(CONFIG_SECTION_EXPIRE);
|
||||
use constant CONFIG_SECTION_REPOSITORY => 'repository';
|
||||
push @EXPORT, qw(CONFIG_SECTION_REPOSITORY);
|
||||
|
||||
####################################################################################################################################
|
||||
# configHelp
|
||||
#
|
||||
# Display command-line help.
|
||||
####################################################################################################################################
|
||||
sub configHelp
|
||||
{
|
||||
my $strCommand = shift;
|
||||
my $strOption = shift;
|
||||
my $bVersion = shift;
|
||||
my $bConfigResult = shift;
|
||||
|
||||
# Load module dynamically
|
||||
require pgBackRest::Config::ConfigHelpData;
|
||||
pgBackRest::Config::ConfigHelpData->import();
|
||||
|
||||
# Get config data
|
||||
my $oConfigHelpData = configHelpDataGet();
|
||||
|
||||
# Build version
|
||||
my $strVersion = (!$bConfigResult ? "\n" : '') . BACKREST_NAME . ' ' . BACKREST_VERSION;
|
||||
|
||||
# Display version
|
||||
if ($bVersion)
|
||||
{
|
||||
syswrite(*STDOUT, "${strVersion}\n");
|
||||
return;
|
||||
}
|
||||
|
||||
# Build the title
|
||||
my $strTitle;
|
||||
my $strHelp;
|
||||
|
||||
# Since there's no command this will be general help
|
||||
if (!defined($strCommand))
|
||||
{
|
||||
$strTitle = "General"
|
||||
}
|
||||
# Else check the command
|
||||
else
|
||||
{
|
||||
$strCommand = lc($strCommand);
|
||||
|
||||
if (defined($oConfigHelpData->{&CONFIG_HELP_COMMAND}{$strCommand}))
|
||||
{
|
||||
$strTitle = "'${strCommand}' command";
|
||||
|
||||
# And check the option
|
||||
if (defined($strOption))
|
||||
{
|
||||
$strOption = lc($strOption);
|
||||
|
||||
if (defined($$oConfigHelpData{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption}))
|
||||
{
|
||||
$strTitle .= " - '${strOption}' option";
|
||||
}
|
||||
else
|
||||
{
|
||||
$strHelp = "Invalid option '${strOption}' for command '${strCommand}'";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$strHelp = "Invalid command '${strCommand}'";
|
||||
}
|
||||
}
|
||||
|
||||
# Build the help
|
||||
my $strMore;
|
||||
|
||||
if (!defined($strHelp))
|
||||
{
|
||||
my $iScreenWidth = 80;
|
||||
|
||||
# General help
|
||||
if (!defined($strCommand))
|
||||
{
|
||||
$strHelp =
|
||||
"Usage:\n" .
|
||||
" " . BACKREST_EXE . " [options] [command]\n\n" .
|
||||
"Commands:\n";
|
||||
|
||||
# Find longest command length
|
||||
my $iCommandLength = 0;
|
||||
|
||||
foreach my $strCommand (sort(keys(%{$$oConfigHelpData{&CONFIG_HELP_COMMAND}})))
|
||||
{
|
||||
if (length($strCommand) > $iCommandLength)
|
||||
{
|
||||
$iCommandLength = length($strCommand);
|
||||
}
|
||||
}
|
||||
|
||||
# Output commands
|
||||
foreach my $strCommand (sort(keys(%{$$oConfigHelpData{&CONFIG_HELP_COMMAND}})))
|
||||
{
|
||||
my $oCommand = $$oConfigHelpData{&CONFIG_HELP_COMMAND}{$strCommand};
|
||||
|
||||
$strHelp .= " ${strCommand}" . (' ' x ($iCommandLength - length($strCommand)));
|
||||
$strHelp .=
|
||||
' ' .
|
||||
configHelpFormatText($$oCommand{&CONFIG_HELP_SUMMARY}, 4 + $iCommandLength + 2, false, $iScreenWidth + 1) .
|
||||
"\n";
|
||||
}
|
||||
|
||||
$strMore = '[command]';
|
||||
}
|
||||
# Else command help
|
||||
elsif (!defined($strOption))
|
||||
{
|
||||
my $oCommand = $$oConfigHelpData{&CONFIG_HELP_COMMAND}{$strCommand};
|
||||
|
||||
$strHelp =
|
||||
configHelpFormatText($$oCommand{&CONFIG_HELP_SUMMARY} . "\n\n" .
|
||||
$$oCommand{&CONFIG_HELP_DESCRIPTION}, 0, true, $iScreenWidth + 1);
|
||||
|
||||
# Find longest option length and unique list of sections
|
||||
my $iOptionLength = 0;
|
||||
my $oSection = {};
|
||||
|
||||
if (defined($$oCommand{&CONFIG_HELP_OPTION}))
|
||||
{
|
||||
foreach my $strOption (sort(keys(%{$$oCommand{&CONFIG_HELP_OPTION}})))
|
||||
{
|
||||
if (length($strOption) > $iOptionLength)
|
||||
{
|
||||
$iOptionLength = length($strOption);
|
||||
}
|
||||
|
||||
my ($oOption, $strSection) = configHelpOptionFind($oConfigHelpData, $strCommand, $strOption);
|
||||
|
||||
$$oSection{$strSection}{$strOption} = $oOption;
|
||||
}
|
||||
|
||||
# Iterate sections
|
||||
foreach my $strSection (sort(keys(%{$oSection})))
|
||||
{
|
||||
$strHelp .=
|
||||
"\n\n" . ucfirst($strSection) . " Options:\n";
|
||||
|
||||
# Iterate options
|
||||
foreach my $strOption (sort(keys(%{$$oSection{$strSection}})))
|
||||
{
|
||||
$strHelp .= "\n";
|
||||
|
||||
my $iIndent = 4 + $iOptionLength + 2;
|
||||
my $oOption = $$oSection{$strSection}{$strOption};
|
||||
|
||||
# Set current and default values
|
||||
my $strDefault = '';
|
||||
|
||||
if ($$oOption{&CONFIG_HELP_CURRENT} || $$oOption{&CONFIG_HELP_DEFAULT})
|
||||
{
|
||||
$strDefault = undef;
|
||||
|
||||
if ($$oOption{&CONFIG_HELP_CURRENT})
|
||||
{
|
||||
$strDefault .= 'current=' . $$oOption{&CONFIG_HELP_CURRENT};
|
||||
}
|
||||
|
||||
if ($$oOption{&CONFIG_HELP_DEFAULT})
|
||||
{
|
||||
if (defined($strDefault))
|
||||
{
|
||||
$strDefault .= ', ';
|
||||
}
|
||||
|
||||
$strDefault .= 'default=' . $$oOption{&CONFIG_HELP_DEFAULT};
|
||||
}
|
||||
|
||||
$strDefault = " [${strDefault}]";
|
||||
}
|
||||
|
||||
# Output help
|
||||
$strHelp .= " --${strOption}" . (' ' x ($iOptionLength - length($strOption)));
|
||||
$strHelp .= ' ' . configHelpFormatText(lcfirst(substr($$oOption{&CONFIG_HELP_SUMMARY}, 0,
|
||||
length($$oOption{&CONFIG_HELP_SUMMARY}) - 1)) .
|
||||
$strDefault, $iIndent, false, $iScreenWidth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
$strMore = "${strCommand} [option]";
|
||||
$strHelp .= "\n";
|
||||
}
|
||||
}
|
||||
# Else option help
|
||||
else
|
||||
{
|
||||
my ($oOption) = configHelpOptionFind($oConfigHelpData, $strCommand, $strOption);
|
||||
|
||||
# Set current and default values
|
||||
my $strDefault = '';
|
||||
|
||||
if ($$oOption{&CONFIG_HELP_CURRENT} || $$oOption{&CONFIG_HELP_DEFAULT})
|
||||
{
|
||||
$strDefault = undef;
|
||||
|
||||
if ($$oOption{&CONFIG_HELP_CURRENT})
|
||||
{
|
||||
$strDefault = 'current: ' . $$oOption{&CONFIG_HELP_CURRENT};
|
||||
}
|
||||
|
||||
if ($$oOption{&CONFIG_HELP_DEFAULT})
|
||||
{
|
||||
if (defined($strDefault))
|
||||
{
|
||||
$strDefault .= "\n";
|
||||
}
|
||||
|
||||
$strDefault .= 'default: ' . $$oOption{&CONFIG_HELP_DEFAULT};
|
||||
}
|
||||
|
||||
$strDefault = "\n\n${strDefault}";
|
||||
}
|
||||
|
||||
# Output help
|
||||
$strHelp =
|
||||
configHelpFormatText($$oOption{&CONFIG_HELP_SUMMARY} . "\n\n" . $$oOption{&CONFIG_HELP_DESCRIPTION} .
|
||||
$strDefault, 0, true, $iScreenWidth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
# Output help
|
||||
syswrite(*STDOUT, "${strVersion} -" . (defined($strTitle) ? " ${strTitle}" : '') . " help\n\n${strHelp}\n" .
|
||||
(defined($strMore) ? 'Use \'' . BACKREST_EXE . " help ${strMore}' for more information.\n" : ''));
|
||||
}
|
||||
|
||||
push @EXPORT, qw(configHelp);
|
||||
|
||||
# Helper function for configHelp() to make output look good on a console
|
||||
sub configHelpFormatText
|
||||
{
|
||||
my $strTextIn = shift;
|
||||
my $iIndent = shift;
|
||||
my $bIndentFirst = shift;
|
||||
my $iLength = shift;
|
||||
|
||||
my @stryText = split("\n", trim($strTextIn));
|
||||
my $strText;
|
||||
my $iIndex = 0;
|
||||
|
||||
foreach my $strLine (@stryText)
|
||||
{
|
||||
if (defined($strText))
|
||||
{
|
||||
$strText .= "\n";
|
||||
}
|
||||
|
||||
my $strPart;
|
||||
my $bFirst = true;
|
||||
|
||||
do
|
||||
{
|
||||
($strPart, $strLine) = stringSplit($strLine, ' ', $iLength - $iIndent);
|
||||
|
||||
if (!$bFirst || $bIndentFirst)
|
||||
{
|
||||
if (!$bFirst)
|
||||
{
|
||||
$strText .= "\n";
|
||||
}
|
||||
|
||||
$strText .= ' ' x $iIndent;
|
||||
}
|
||||
|
||||
$strText .= trim($strPart);
|
||||
|
||||
$bFirst = false;
|
||||
}
|
||||
while (defined($strLine));
|
||||
|
||||
$iIndex++;
|
||||
}
|
||||
|
||||
return $strText;
|
||||
}
|
||||
|
||||
# Helper function for configHelp() to find options. The option may be stored with the command or in the option list depending on
|
||||
# whether it's generic or command-specific
|
||||
sub configHelpOptionFind
|
||||
{
|
||||
my $oConfigHelpData = shift;
|
||||
my $strCommand = shift;
|
||||
my $strOption = shift;
|
||||
|
||||
my $strSection = CONFIG_HELP_COMMAND;
|
||||
my $oOption = $$oConfigHelpData{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption};
|
||||
my $iCommandId = cfgCommandId($strCommand);
|
||||
my $iOptionId = cfgOptionId($strOption);
|
||||
|
||||
# If not found then this is an indexed value
|
||||
if ($iOptionId eq -1)
|
||||
{
|
||||
my $strPrefix = substr($strOption, 0, index($strOption, '-'));
|
||||
$iOptionId = cfgOptionId("${strPrefix}1" . substr($strOption, index($strOption, '-')));
|
||||
|
||||
# If still not found then error
|
||||
if ($iOptionId eq -1)
|
||||
{
|
||||
confess &log(ASSERT, "option '${strOption}' not found in help");
|
||||
}
|
||||
}
|
||||
|
||||
if (ref(\$oOption) eq 'SCALAR')
|
||||
{
|
||||
$oOption = $$oConfigHelpData{&CONFIG_HELP_OPTION}{$strOption};
|
||||
|
||||
if (defined($$oOption{&CONFIG_HELP_SECTION}))
|
||||
{
|
||||
$strSection = $$oOption{&CONFIG_HELP_SECTION};
|
||||
|
||||
if ($strSection eq CONFIG_SECTION_COMMAND)
|
||||
{
|
||||
$strSection = CONFIG_SECTION_GENERAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$strSection = CONFIG_SECTION_GENERAL;
|
||||
}
|
||||
|
||||
if (($strSection ne CONFIG_SECTION_GENERAL && $strSection ne CONFIG_SECTION_LOG &&
|
||||
$strSection ne CONFIG_SECTION_REPOSITORY && $strSection ne CFGDEF_SECTION_STANZA &&
|
||||
$strSection ne CONFIG_SECTION_EXPIRE) ||
|
||||
$strSection eq $strCommand)
|
||||
{
|
||||
$strSection = CONFIG_HELP_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
# Check if the current set value is default (some defaults are set at runtime and are not in the defines)
|
||||
if (defined(cfgOption($iOptionId, false, false)) && cfgOptionSource($iOptionId, false) eq CONFIG_HELP_SOURCE_DEFAULT)
|
||||
{
|
||||
$oOption->{&CONFIG_HELP_DEFAULT} = cfgOption($iOptionId, true, false);
|
||||
}
|
||||
|
||||
# If no default is set see if there is a default in the defines
|
||||
if (!defined($oOption->{&CONFIG_HELP_DEFAULT}) && defined(cfgDefOptionDefault($iCommandId, $iOptionId)))
|
||||
{
|
||||
$oOption->{&CONFIG_HELP_DEFAULT} = cfgDefOptionDefault($iCommandId, $iOptionId);
|
||||
}
|
||||
|
||||
# Format the default properly if it is a boolean
|
||||
if (defined($oOption->{&CONFIG_HELP_DEFAULT}) && cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN)
|
||||
{
|
||||
$oOption->{&CONFIG_HELP_DEFAULT} = $oOption->{&CONFIG_HELP_DEFAULT} ? 'y' : 'n';
|
||||
}
|
||||
|
||||
if (defined(cfgOption($iOptionId, false, false)) && cfgOptionSource($iOptionId, false) ne CONFIG_HELP_SOURCE_DEFAULT)
|
||||
{
|
||||
$oOption->{&CONFIG_HELP_CURRENT} = cfgOption($iOptionId, true, false);
|
||||
|
||||
if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN)
|
||||
{
|
||||
$$oOption{&CONFIG_HELP_CURRENT} = $oOption->{&CONFIG_HELP_CURRENT} ? 'y' : 'n';
|
||||
}
|
||||
}
|
||||
|
||||
return $oOption, $strSection;
|
||||
}
|
||||
|
||||
1;
|
File diff suppressed because it is too large
Load Diff
@@ -41,19 +41,7 @@ sub main
|
||||
# Load command line parameters and config
|
||||
############################################################################################################################
|
||||
backrestBinSet($strBackRestBin);
|
||||
my $bConfigResult = configLoad();
|
||||
|
||||
# Display help and version
|
||||
if (cfgCommandTest(CFGCMD_HELP) || cfgCommandTest(CFGCMD_VERSION))
|
||||
{
|
||||
# Load module dynamically
|
||||
require pgBackRest::Config::ConfigHelp;
|
||||
pgBackRest::Config::ConfigHelp->import();
|
||||
|
||||
# Generate help and exit
|
||||
configHelp($ARGV[1], $ARGV[2], cfgCommandTest(CFGCMD_VERSION), $bConfigResult);
|
||||
exitSafe(0);
|
||||
}
|
||||
configLoad();
|
||||
|
||||
# Set test options
|
||||
if (cfgOptionTest(CFGOPT_TEST) && cfgOption(CFGOPT_TEST))
|
||||
|
@@ -4,6 +4,7 @@ DESTDIR=
|
||||
|
||||
pgbackrest: \
|
||||
command/archive/push/push.o \
|
||||
command/help/help.o \
|
||||
command/command.o \
|
||||
common/error.o \
|
||||
common/errorType.o \
|
||||
@@ -31,6 +32,7 @@ pgbackrest: \
|
||||
main.o
|
||||
$(CC) $(CFLAGS) -o pgbackrest \
|
||||
command/archive/push/push.o \
|
||||
command/help/help.o \
|
||||
command/command.o \
|
||||
common/error.o \
|
||||
common/errorType.o \
|
||||
|
321
src/command/help/help.c
Normal file
321
src/command/help/help.c
Normal file
@@ -0,0 +1,321 @@
|
||||
/***********************************************************************************************************************************
|
||||
Help Command
|
||||
***********************************************************************************************************************************/
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/memContext.h"
|
||||
#include "common/type.h"
|
||||
#include "config/config.h"
|
||||
#include "config/define.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define the console width - use a fixed with of 80 since this should be safe on virtually all consoles
|
||||
***********************************************************************************************************************************/
|
||||
#define CONSOLE_WIDTH 80
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Helper function for helpRender() to make output look good on a console
|
||||
***********************************************************************************************************************************/
|
||||
static String *
|
||||
helpRenderText(const String *text, int indent, bool indentFirst, int length)
|
||||
{
|
||||
String *result = strNew("");
|
||||
|
||||
// Split the text into paragraphs
|
||||
StringList *lineList = strLstNewSplitZ(text, "\n");
|
||||
|
||||
// Iterate through each paragraph and split the lines according to the line length
|
||||
for (unsigned int lineIdx = 0; lineIdx < strLstSize(lineList); lineIdx++)
|
||||
{
|
||||
// Add LF if there is already content
|
||||
if (strSize(result) != 0)
|
||||
strCat(result, "\n");
|
||||
|
||||
// Split the paragraph into lines that don't exceed the line length
|
||||
StringList *partList = strLstNewSplitSizeZ(strLstGet(lineList, lineIdx), " ", length - indent);
|
||||
|
||||
for (unsigned int partIdx = 0; partIdx < strLstSize(partList); partIdx++)
|
||||
{
|
||||
// Indent when required
|
||||
if (partIdx != 0 || indentFirst)
|
||||
{
|
||||
if (partIdx != 0)
|
||||
strCat(result, "\n");
|
||||
|
||||
if (strSize(strLstGet(partList, partIdx)))
|
||||
strCatFmt(result, "%*s", indent, "");
|
||||
}
|
||||
|
||||
// Add the line
|
||||
strCat(result, strPtr(strLstGet(partList, partIdx)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Helper function for helpRender() to output values as strings
|
||||
***********************************************************************************************************************************/
|
||||
static String *
|
||||
helpRenderValue(const Variant *value)
|
||||
{
|
||||
String *result = NULL;
|
||||
|
||||
if (value != NULL)
|
||||
{
|
||||
if (varType(value) == varTypeBool)
|
||||
{
|
||||
if (varBool(value))
|
||||
result = strNew("y");
|
||||
else
|
||||
result = strNew("n");
|
||||
}
|
||||
else
|
||||
result = varStrForce(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Render help to a string
|
||||
***********************************************************************************************************************************/
|
||||
static String *
|
||||
helpRender()
|
||||
{
|
||||
String *result = strNew(PGBACKREST_NAME " " PGBACKREST_VERSION);
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Message for more help when it is available
|
||||
String *more = NULL;
|
||||
|
||||
// Display general help
|
||||
if (cfgCommand() == cfgCmdHelp || cfgCommand() == cfgCmdNone)
|
||||
{
|
||||
strCat(
|
||||
result,
|
||||
" - General help\n"
|
||||
"\n"
|
||||
"Usage:\n"
|
||||
" " PGBACKREST_BIN " [options] [command]\n"
|
||||
"\n"
|
||||
"Commands:\n");
|
||||
|
||||
// Find size of longest command name
|
||||
size_t commandSizeMax = 0;
|
||||
|
||||
for (ConfigCommand commandId = 0; commandId < CFG_COMMAND_TOTAL; commandId++)
|
||||
{
|
||||
if (commandId == cfgCmdNone)
|
||||
continue;
|
||||
|
||||
if (strlen(cfgCommandName(commandId)) > commandSizeMax)
|
||||
commandSizeMax = strlen(cfgCommandName(commandId));
|
||||
}
|
||||
|
||||
// Output help for each command
|
||||
for (ConfigCommand commandId = 0; commandId < CFG_COMMAND_TOTAL; commandId++)
|
||||
{
|
||||
if (commandId == cfgCmdNone)
|
||||
continue;
|
||||
|
||||
const char *helpSummary = cfgDefCommandHelpSummary(cfgCommandDefIdFromId(commandId));
|
||||
|
||||
if (helpSummary != NULL)
|
||||
{
|
||||
strCatFmt(
|
||||
result, " %s%*s%s\n", cfgCommandName(commandId),
|
||||
(int)(commandSizeMax - strlen(cfgCommandName(commandId)) + 2), "",
|
||||
strPtr(helpRenderText(strNew(helpSummary), commandSizeMax + 6, false, CONSOLE_WIDTH)));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct message for more help
|
||||
more = strNew("[command]");
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfigCommand commandId = cfgCommand();
|
||||
ConfigDefineCommand commandDefId = cfgCommandDefIdFromId(commandId);
|
||||
const char *commandName = cfgCommandName(commandId);
|
||||
|
||||
// Output command part of title
|
||||
strCatFmt(result, " - '%s' command", commandName);
|
||||
|
||||
// If no additional params then this is command help
|
||||
if (strLstSize(cfgCommandParam()) == 0)
|
||||
{
|
||||
// Output command summary and description
|
||||
strCatFmt(
|
||||
result,
|
||||
" help\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"%s\n",
|
||||
strPtr(helpRenderText(strNew(cfgDefCommandHelpSummary(commandDefId)), 0, true, CONSOLE_WIDTH)),
|
||||
strPtr(helpRenderText(strNew(cfgDefCommandHelpDescription(commandDefId)), 0, true, CONSOLE_WIDTH)));
|
||||
|
||||
// Construct key/value of sections and options
|
||||
KeyValue *optionKv = kvNew();
|
||||
size_t optionSizeMax = 0;
|
||||
|
||||
for (unsigned int optionDefId = 0; optionDefId < cfgDefOptionTotal(); optionDefId++)
|
||||
{
|
||||
if (cfgDefOptionValid(commandDefId, optionDefId) && !cfgDefOptionInternal(optionDefId))
|
||||
{
|
||||
String *section = NULL;
|
||||
|
||||
if (cfgDefOptionHelpSection(optionDefId) != NULL)
|
||||
section = strNew(cfgDefOptionHelpSection(optionDefId));
|
||||
|
||||
if (section == NULL ||
|
||||
(!strEqZ(section, "general") && !strEqZ(section, "log") && !strEqZ(section, "repository") &&
|
||||
!strEqZ(section, "stanza") && !strEqZ(section, "expire")))
|
||||
{
|
||||
section = strNew("command");
|
||||
}
|
||||
|
||||
kvAdd(optionKv, varNewStr(section), varNewInt((int)optionDefId));
|
||||
|
||||
if (strlen(cfgDefOptionName(optionDefId)) > optionSizeMax)
|
||||
optionSizeMax = strlen(cfgDefOptionName(optionDefId));
|
||||
}
|
||||
}
|
||||
|
||||
// Output sections
|
||||
StringList *sectionList = strLstSort(strLstNewVarLst(kvKeyList(optionKv)), sortOrderAsc);
|
||||
|
||||
for (unsigned int sectionIdx = 0; sectionIdx < strLstSize(sectionList); sectionIdx++)
|
||||
{
|
||||
const String *section = strLstGet(sectionList, sectionIdx);
|
||||
|
||||
strCatFmt(result, "\n%s Options:\n\n", strPtr(strFirstUpper(strDup(section))));
|
||||
|
||||
// Output options
|
||||
VariantList *optionList = kvGetList(optionKv, varNewStr(section));
|
||||
|
||||
for (unsigned int optionIdx = 0; optionIdx < varLstSize(optionList); optionIdx++)
|
||||
{
|
||||
ConfigDefineOption optionDefId = varInt(varLstGet(optionList, optionIdx));
|
||||
ConfigOption optionId = cfgOptionIdFromDefId(optionDefId, 0);
|
||||
|
||||
// Get option summary
|
||||
String *summary = strFirstLower(strNewN(
|
||||
cfgDefOptionHelpSummary(commandDefId, optionDefId),
|
||||
strlen(cfgDefOptionHelpSummary(commandDefId, optionDefId)) - 1));
|
||||
|
||||
// Ouput current and default values if they exist
|
||||
String *defaultValue = helpRenderValue(cfgOptionDefault(optionId));
|
||||
String *value = NULL;
|
||||
|
||||
if (cfgOptionSource(optionId) != cfgSourceDefault)
|
||||
value = helpRenderValue(cfgOption(optionId));
|
||||
|
||||
if (value != NULL || defaultValue != NULL)
|
||||
{
|
||||
strCat(summary, " [");
|
||||
|
||||
if (value != NULL)
|
||||
strCatFmt(summary, "current=%s", strPtr(value));
|
||||
|
||||
if (defaultValue != NULL)
|
||||
{
|
||||
if (value != NULL)
|
||||
strCat(summary, ", ");
|
||||
|
||||
strCatFmt(summary, "default=%s", strPtr(defaultValue));
|
||||
}
|
||||
|
||||
strCat(summary, "]");
|
||||
}
|
||||
|
||||
// Output option help
|
||||
strCatFmt(
|
||||
result, " --%s%*s%s\n",
|
||||
cfgDefOptionName(optionDefId), (int)(optionSizeMax - strlen(cfgDefOptionName(optionDefId)) + 2), "",
|
||||
strPtr(helpRenderText(summary, optionSizeMax + 6, false, CONSOLE_WIDTH)));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct message for more help if there are options
|
||||
if (optionSizeMax > 0)
|
||||
more = strNewFmt("%s [option]", commandName);
|
||||
}
|
||||
// Else option help for the specified command
|
||||
else
|
||||
{
|
||||
// Make sure only one option was specified
|
||||
if (strLstSize(cfgCommandParam()) > 1)
|
||||
THROW(ParamInvalidError, "only one option allowed for option help");
|
||||
|
||||
// Ensure the option is valid
|
||||
const char *optionName = strPtr(strLstGet(cfgCommandParam(), 0));
|
||||
|
||||
if (cfgOptionId(optionName) == -1)
|
||||
THROW(OptionInvalidError, "option '%s' is not valid for command '%s'", optionName, commandName);
|
||||
|
||||
// Output option summary and description
|
||||
ConfigOption optionId = cfgOptionId(optionName);
|
||||
ConfigDefineOption optionDefId = cfgOptionDefIdFromId(optionId);
|
||||
|
||||
strCatFmt(
|
||||
result,
|
||||
" - '%s' option help\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"%s\n",
|
||||
optionName,
|
||||
strPtr(helpRenderText(strNew(cfgDefOptionHelpSummary(commandDefId, optionDefId)), 0, true, CONSOLE_WIDTH)),
|
||||
strPtr(helpRenderText(strNew(cfgDefOptionHelpDescription(commandDefId, optionDefId)), 0, true, CONSOLE_WIDTH)));
|
||||
|
||||
// Ouput current and default values if they exist
|
||||
String *defaultValue = helpRenderValue(cfgOptionDefault(optionId));
|
||||
String *value = NULL;
|
||||
|
||||
if (cfgOptionSource(optionId) != cfgSourceDefault)
|
||||
value = helpRenderValue(cfgOption(optionId));
|
||||
|
||||
if (value != NULL || defaultValue != NULL)
|
||||
{
|
||||
strCat(result, "\n");
|
||||
|
||||
if (value != NULL)
|
||||
strCatFmt(result, "current: %s\n", strPtr(value));
|
||||
|
||||
if (defaultValue != NULL)
|
||||
strCatFmt(result, "default: %s\n", strPtr(defaultValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is more help available output a message to let the user know
|
||||
if (more != NULL)
|
||||
strCatFmt(result, "\nUse '" PGBACKREST_BIN " help %s' for more information.\n", strPtr(more));
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Render help and output to stdout
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
cmdHelp()
|
||||
{
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
String *help = helpRender();
|
||||
|
||||
THROW_ON_SYS_ERROR(
|
||||
write(STDOUT_FILENO, strPtr(help), strSize(help)) != (int)strSize(help), FileWriteError,
|
||||
"unable to write help to stdout");
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
12
src/command/help/help.h
Normal file
12
src/command/help/help.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/***********************************************************************************************************************************
|
||||
Help Command
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef COMMAND_HELP_H
|
||||
#define COMMAND_HELP_H
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void cmdHelp();
|
||||
|
||||
#endif
|
@@ -45,6 +45,7 @@ ERROR_DEFINE(ERROR_CODE_MIN + 39, FileWriteError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 57, ArchiveTimeoutError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 69, MemoryError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 70, CipherError, FormatError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 71, ParamInvalidError, RuntimeError);
|
||||
|
||||
ERROR_DEFINE(ERROR_CODE_MAX, RuntimeError, RuntimeError);
|
||||
|
||||
@@ -70,6 +71,7 @@ static const ErrorType *errorTypeList[] =
|
||||
&ArchiveTimeoutError,
|
||||
&MemoryError,
|
||||
&CipherError,
|
||||
&ParamInvalidError,
|
||||
|
||||
&RuntimeError,
|
||||
|
||||
|
@@ -31,6 +31,7 @@ ERROR_DECLARE(FileWriteError);
|
||||
ERROR_DECLARE(ArchiveTimeoutError);
|
||||
ERROR_DECLARE(MemoryError);
|
||||
ERROR_DECLARE(CipherError);
|
||||
ERROR_DECLARE(ParamInvalidError);
|
||||
|
||||
ERROR_DECLARE(RuntimeError);
|
||||
|
||||
|
@@ -171,6 +171,6 @@ logInternal(LogLevel logLevel, const char *fileName, const char *functionName, i
|
||||
stream = logHandleStdErr;
|
||||
|
||||
THROW_ON_SYS_ERROR(
|
||||
write(stream, logBuffer, bufferPos) != bufferPos, FileWriteError, "unable to write log to stdout");
|
||||
write(stream, logBuffer, bufferPos) != bufferPos, FileWriteError, "unable to write log to console");
|
||||
}
|
||||
}
|
||||
|
@@ -90,6 +90,7 @@ typedef struct ConfigOptionValue
|
||||
unsigned int source:2;
|
||||
|
||||
Variant *value;
|
||||
Variant *defaultValue;
|
||||
} ConfigOptionValue;
|
||||
|
||||
ConfigOptionValue configOptionValue[CFG_OPTION_TOTAL];
|
||||
@@ -279,7 +280,86 @@ cfgOptionDefIdFromId(ConfigOption optionId)
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get total indexed values for option
|
||||
Get/set option default
|
||||
***********************************************************************************************************************************/
|
||||
const Variant *
|
||||
cfgOptionDefault(ConfigOption optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
if (configOptionValue[optionId].defaultValue == NULL)
|
||||
{
|
||||
ConfigDefineOption optionDefId = cfgOptionDefIdFromId(optionId);
|
||||
|
||||
if (cfgDefOptionDefault(cfgCommandDefIdFromId(cfgCommand()), optionDefId) != NULL)
|
||||
{
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
Variant *defaultValue = varNewStrZ(cfgDefOptionDefault(cfgCommandDefIdFromId(cfgCommand()), optionDefId));
|
||||
|
||||
MEM_CONTEXT_BEGIN(configMemContext)
|
||||
{
|
||||
switch (cfgDefOptionType(optionDefId))
|
||||
{
|
||||
case cfgDefOptTypeBoolean:
|
||||
{
|
||||
configOptionValue[optionId].defaultValue = varNewBool(varBoolForce(defaultValue));
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeFloat:
|
||||
{
|
||||
configOptionValue[optionId].defaultValue = varNewDbl(varDblForce(defaultValue));
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeInteger:
|
||||
{
|
||||
configOptionValue[optionId].defaultValue = varNewInt(varIntForce(defaultValue));
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeString:
|
||||
configOptionValue[optionId].defaultValue = varDup(defaultValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
THROW( // {uncoverable - others types do not have defaults yet}
|
||||
AssertError, "type for option '%s' does not support defaults", cfgOptionName(optionId));
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
}
|
||||
|
||||
return configOptionValue[optionId].defaultValue;
|
||||
}
|
||||
|
||||
void
|
||||
cfgOptionDefaultSet(ConfigOption optionId, const Variant *defaultValue)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(configMemContext)
|
||||
{
|
||||
if (configOptionValue[optionId].defaultValue != NULL)
|
||||
varFree(configOptionValue[optionId].defaultValue);
|
||||
|
||||
configOptionValue[optionId].defaultValue = varDup(defaultValue);
|
||||
|
||||
if (configOptionValue[optionId].source == cfgSourceDefault)
|
||||
{
|
||||
if (configOptionValue[optionId].value != NULL)
|
||||
varFree(configOptionValue[optionId].value);
|
||||
|
||||
configOptionValue[optionId].value = varDup(defaultValue);
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get index for option
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
cfgOptionIndex(ConfigOption optionId)
|
||||
|
@@ -40,6 +40,8 @@ void cfgExeSet(const String *exeParam);
|
||||
|
||||
const Variant *cfgOption(ConfigSource optionId);
|
||||
bool cfgOptionBool(ConfigSource optionId);
|
||||
const Variant *cfgOptionDefault(ConfigOption optionId);
|
||||
void cfgOptionDefaultSet(ConfigOption optionId, const Variant *defaultValue);
|
||||
ConfigDefineOption cfgOptionDefIdFromId(ConfigOption optionId);
|
||||
double cfgOptionDbl(ConfigSource optionId);
|
||||
int cfgOptionId(const char *optionName);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,9 @@ Map command names to ids and vice versa.
|
||||
typedef struct ConfigDefineCommandData
|
||||
{
|
||||
const char *name; // Command name
|
||||
|
||||
const char *helpSummary; // Brief summary of the command
|
||||
const char *helpDescription; // Full description of the command
|
||||
} ConfigDefineCommandData;
|
||||
|
||||
// Command macros are intended to make the command definitions easy to read and to produce good diffs.
|
||||
@@ -26,6 +29,11 @@ typedef struct ConfigDefineCommandData
|
||||
#define CFGDEFDATA_COMMAND_NAME(nameParam) \
|
||||
.name = nameParam,
|
||||
|
||||
#define CFGDEFDATA_COMMAND_HELP_SUMMARY(helpSummaryParam) \
|
||||
.helpSummary = helpSummaryParam,
|
||||
#define CFGDEFDATA_COMMAND_HELP_DESCRIPTION(helpDescriptionParam) \
|
||||
.helpDescription = helpDescriptionParam,
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define how an option is parsed and interacts with other options.
|
||||
***********************************************************************************************************************************/
|
||||
@@ -39,7 +47,12 @@ typedef struct ConfigDefineOptionData
|
||||
bool negate:1; // Can the option be negated?
|
||||
bool required:1; // Is the option required?
|
||||
bool secure:1; // Does the option need to be redacted on logs and cmd-line?
|
||||
unsigned int commandValid:15; // Bitmap for commands that is option is valid for
|
||||
unsigned int commandValid:15; // Bitmap for commands that the option is valid for
|
||||
|
||||
const char *helpSection; // Classify the option
|
||||
const char *helpSummary; // Brief summary of the option
|
||||
const char *helpDescription; // Full description of the option
|
||||
|
||||
const void **data; // Optional data and command overrides
|
||||
} ConfigDefineOptionData;
|
||||
|
||||
@@ -68,6 +81,13 @@ typedef struct ConfigDefineOptionData
|
||||
#define CFGDEFDATA_OPTION_TYPE(typeParam) \
|
||||
.type = typeParam,
|
||||
|
||||
#define CFGDEFDATA_OPTION_HELP_SECTION(helpSectionParam) \
|
||||
.helpSection = helpSectionParam,
|
||||
#define CFGDEFDATA_OPTION_HELP_SUMMARY(helpSummaryParam) \
|
||||
.helpSummary = helpSummaryParam,
|
||||
#define CFGDEFDATA_OPTION_HELP_DESCRIPTION(helpDescriptionParam) \
|
||||
.helpDescription = helpDescriptionParam,
|
||||
|
||||
// Define additional types of data that can be associated with an option. Because these types are rare they are not give dedicated
|
||||
// fields and are instead packed into an array which is read at runtime. This may seem inefficient but they are only accessed a
|
||||
// single time during parse so space efficiency is more important than performance.
|
||||
@@ -83,6 +103,8 @@ typedef enum
|
||||
configDefDataTypeNameAlt,
|
||||
configDefDataTypePrefix,
|
||||
configDefDataTypeRequired,
|
||||
configDefDataTypeHelpSummary,
|
||||
configDefDataTypeHelpDescription,
|
||||
} ConfigDefineDataType;
|
||||
|
||||
#define CFGDATA_OPTION_OPTIONAL_PUSH_LIST(type, size, data, ...) \
|
||||
@@ -109,7 +131,7 @@ typedef enum
|
||||
|
||||
#define CFGDEFDATA_OPTION_OPTIONAL_ALLOW_RANGE(rangeMinParam, rangeMaxParam) \
|
||||
CFGDATA_OPTION_OPTIONAL_PUSH_LIST( \
|
||||
configDefDataTypeAllowRange, 2, 0, (const void *)(intptr_t)(int32)(rangeMinParam * 100), \
|
||||
configDefDataTypeAllowRange, 2, 0, (const void *)(intptr_t)(int32)(rangeMinParam * 100), \
|
||||
(const void *)(intptr_t)(int32)(rangeMaxParam * 100)),
|
||||
|
||||
#define CFGDEFDATA_OPTION_OPTIONAL_NAME_ALT(nameAltParam) \
|
||||
@@ -134,6 +156,11 @@ typedef enum
|
||||
#define CFGDEFDATA_OPTION_OPTIONAL_REQUIRED(commandOptionRequired) \
|
||||
CFGDATA_OPTION_OPTIONAL_PUSH(configDefDataTypeRequired, 0, commandOptionRequired),
|
||||
|
||||
#define CFGDEFDATA_OPTION_OPTIONAL_HELP_SUMMARY(helpSummaryParam) \
|
||||
CFGDATA_OPTION_OPTIONAL_PUSH_LIST(configDefDataTypeHelpSummary, 1, 0, helpSummaryParam),
|
||||
#define CFGDEFDATA_OPTION_OPTIONAL_HELP_DESCRIPTION(helpDescriptionParam) \
|
||||
CFGDATA_OPTION_OPTIONAL_PUSH_LIST(configDefDataTypeHelpDescription, 1, 0, helpDescriptionParam),
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Include the automatically generated configuration data.
|
||||
***********************************************************************************************************************************/
|
||||
@@ -244,6 +271,26 @@ cfgDefCommandOptionCheck(ConfigDefineCommand commandDefId, ConfigDefineOption op
|
||||
cfgDefOptionCheck(optionDefId);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Command help description
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
cfgDefCommandHelpDescription(ConfigDefineCommand commandDefId)
|
||||
{
|
||||
cfgDefCommandCheck(commandDefId);
|
||||
return configDefineCommandData[commandDefId].helpDescription;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Command help summary
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
cfgDefCommandHelpSummary(ConfigDefineCommand commandDefId)
|
||||
{
|
||||
cfgDefCommandCheck(commandDefId);
|
||||
return configDefineCommandData[commandDefId].helpSummary;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option allow lists
|
||||
***********************************************************************************************************************************/
|
||||
@@ -403,6 +450,48 @@ cfgDefOptionDependValueValid(ConfigDefineCommand commandDefId, ConfigDefineOptio
|
||||
return false;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option help description
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
cfgDefOptionHelpDescription(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId)
|
||||
{
|
||||
cfgDefCommandOptionCheck(commandDefId, optionDefId);
|
||||
|
||||
CONFIG_DEFINE_DATA_FIND(commandDefId, optionDefId, configDefDataTypeHelpDescription);
|
||||
|
||||
if (dataDefFound)
|
||||
return (char *)dataDefList[0];
|
||||
|
||||
return configDefineOptionData[optionDefId].helpDescription;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option help section
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
cfgDefOptionHelpSection(ConfigDefineOption optionDefId)
|
||||
{
|
||||
cfgDefOptionCheck(optionDefId);
|
||||
return configDefineOptionData[optionDefId].helpSection;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option help summary
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
cfgDefOptionHelpSummary(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId)
|
||||
{
|
||||
cfgDefCommandOptionCheck(commandDefId, optionDefId);
|
||||
|
||||
CONFIG_DEFINE_DATA_FIND(commandDefId, optionDefId, configDefDataTypeHelpSummary);
|
||||
|
||||
if (dataDefFound)
|
||||
return (char *)dataDefList[0];
|
||||
|
||||
return configDefineOptionData[optionDefId].helpSummary;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get total indexed values for option
|
||||
***********************************************************************************************************************************/
|
||||
|
@@ -27,6 +27,8 @@ Functions
|
||||
***********************************************************************************************************************************/
|
||||
unsigned int cfgDefCommandTotal();
|
||||
void cfgDefCommandCheck(ConfigDefineCommand commandDefId);
|
||||
const char *cfgDefCommandHelpDescription(ConfigDefineCommand commandDefId);
|
||||
const char *cfgDefCommandHelpSummary(ConfigDefineCommand commandDefId);
|
||||
|
||||
bool cfgDefOptionAllowList(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId);
|
||||
int cfgDefOptionAllowListValueTotal(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId);
|
||||
@@ -42,6 +44,9 @@ ConfigDefineOption cfgDefOptionDependOption(ConfigDefineCommand commandDefId, Co
|
||||
int cfgDefOptionDependValueTotal(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId);
|
||||
bool cfgDefOptionDependValueValid(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId, const char *value);
|
||||
const char *cfgDefOptionDependValue(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId, int valueId);
|
||||
const char *cfgDefOptionHelpDescription(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId);
|
||||
const char *cfgDefOptionHelpSection(ConfigDefineOption optionDefId);
|
||||
const char *cfgDefOptionHelpSummary(ConfigDefineCommand commandDefId, ConfigDefineOption optionDefId);
|
||||
int cfgDefOptionIndexTotal(ConfigDefineOption optionDefId);
|
||||
bool cfgDefOptionInternal(ConfigDefineOption optionDefId);
|
||||
const char *cfgDefOptionName(ConfigDefineOption optionDefId);
|
||||
|
@@ -34,6 +34,22 @@ cfgLoad(int argListSize, const char *argList[])
|
||||
logTimestamp = cfgOptionBool(cfgOptLogTimestamp);
|
||||
|
||||
logInit(logLevelConsole, logLevelStdErr, logTimestamp);
|
||||
|
||||
// Set default for backup-cmd
|
||||
if (cfgOptionValid(cfgOptBackupHost) && cfgOption(cfgOptBackupHost) != NULL &&
|
||||
cfgOptionSource(cfgOptBackupCmd) == cfgSourceDefault)
|
||||
{
|
||||
cfgOptionDefaultSet(cfgOptBackupCmd, varNewStr(cfgExe()));
|
||||
}
|
||||
|
||||
if (cfgOptionValid(cfgOptDbCmd))
|
||||
{
|
||||
for (int optionIdx = 0; optionIdx <= cfgOptionIndexTotal(cfgOptDbHost); optionIdx++)
|
||||
{
|
||||
if (cfgOption(cfgOptDbHost + optionIdx) != NULL && cfgOptionSource(cfgOptDbCmd + optionIdx) == cfgSourceDefault)
|
||||
cfgOptionDefaultSet(cfgOptDbCmd + optionIdx, varNewStr(cfgExe()));
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
|
11
src/main.c
11
src/main.c
@@ -5,6 +5,7 @@ Main
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "command/archive/push/push.h"
|
||||
#include "command/help/help.h"
|
||||
#include "command/command.h"
|
||||
#include "common/error.h"
|
||||
#include "common/exit.h"
|
||||
@@ -21,9 +22,17 @@ int main(int argListSize, const char *argList[])
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
cfgLoad(argListSize, argList);
|
||||
|
||||
// Display help
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
if (cfgCommandHelp())
|
||||
{
|
||||
cmdHelp();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Display version
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
if (!cfgCommandHelp() && cfgCommand() == cfgCmdVersion)
|
||||
if (cfgCommand() == cfgCmdVersion)
|
||||
{
|
||||
printf(PGBACKREST_NAME " " PGBACKREST_VERSION "\n");
|
||||
fflush(stdout);
|
||||
|
@@ -9,6 +9,11 @@ Official name of the software, also used for Perl package name
|
||||
***********************************************************************************************************************************/
|
||||
#define PGBACKREST_NAME "pgBackRest"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Standard binary name
|
||||
***********************************************************************************************************************************/
|
||||
#define PGBACKREST_BIN "pgbackrest"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Version of the software. Currently this value is maintained in Version.pm and updated by test.pl.
|
||||
***********************************************************************************************************************************/
|
||||
|
@@ -93,8 +93,8 @@ Repository Options:
|
||||
[default=backrest]
|
||||
--repo-cipher-pass repository cipher passphrase
|
||||
--repo-cipher-type cipher used to encrypt the repository [default=none]
|
||||
--repo-path repository path where WAL segments and backups
|
||||
stored [default=/var/lib/pgbackrest]
|
||||
--repo-path path where backups and archive are stored
|
||||
[default=/var/lib/pgbackrest]
|
||||
--repo-s3-bucket s3 repository bucket
|
||||
--repo-s3-ca-file s3 SSL CA File
|
||||
--repo-s3-ca-path s3 SSL CA Path
|
||||
@@ -147,8 +147,8 @@ before running the check command.
|
||||
|
||||
Command Options:
|
||||
|
||||
--archive-check check that WAL segments are present in the archive
|
||||
before backup completes [default=y]
|
||||
--archive-check check that WAL segments are in the archive before
|
||||
backup completes [default=y]
|
||||
--archive-timeout archive timeout [default=60]
|
||||
--backup-standby backup from the standby cluster [default=n]
|
||||
--online check an online cluster [default=y]
|
||||
@@ -188,8 +188,8 @@ Repository Options:
|
||||
[default=backrest]
|
||||
--repo-cipher-pass repository cipher passphrase
|
||||
--repo-cipher-type cipher used to encrypt the repository [default=none]
|
||||
--repo-path repository path where WAL segments and backups
|
||||
stored [default=/var/lib/pgbackrest]
|
||||
--repo-path path where backups and archive are stored
|
||||
[default=/var/lib/pgbackrest]
|
||||
--repo-s3-bucket s3 repository bucket
|
||||
--repo-s3-ca-file s3 SSL CA File
|
||||
--repo-s3-ca-path s3 SSL CA Path
|
||||
|
@@ -400,6 +400,25 @@ my $oTestDef =
|
||||
},
|
||||
]
|
||||
},
|
||||
# Help tests
|
||||
{
|
||||
&TESTDEF_NAME => 'help',
|
||||
&TESTDEF_CONTAINER => true,
|
||||
|
||||
&TESTDEF_TEST =>
|
||||
[
|
||||
{
|
||||
&TESTDEF_NAME => 'help',
|
||||
&TESTDEF_TOTAL => 4,
|
||||
&TESTDEF_C => true,
|
||||
|
||||
&TESTDEF_COVERAGE =>
|
||||
{
|
||||
'command/help/help' => TESTDEF_COVERAGE_FULL,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
# Config tests
|
||||
{
|
||||
&TESTDEF_NAME => 'config',
|
||||
@@ -409,7 +428,7 @@ my $oTestDef =
|
||||
[
|
||||
{
|
||||
&TESTDEF_NAME => 'define',
|
||||
&TESTDEF_TOTAL => 1,
|
||||
&TESTDEF_TOTAL => 2,
|
||||
&TESTDEF_C => true,
|
||||
|
||||
&TESTDEF_COVERAGE =>
|
||||
@@ -420,7 +439,7 @@ my $oTestDef =
|
||||
},
|
||||
{
|
||||
&TESTDEF_NAME => 'config',
|
||||
&TESTDEF_TOTAL => 2,
|
||||
&TESTDEF_TOTAL => 3,
|
||||
&TESTDEF_C => true,
|
||||
|
||||
&TESTDEF_COVERAGE =>
|
||||
|
@@ -156,4 +156,29 @@ void testRun()
|
||||
TEST_RESULT_VOID(cfgInit(), "config init resets value");
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdNone, "command begins as none");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("cfgOptionDefault() and cfgOptionDefaultSet()"))
|
||||
{
|
||||
TEST_RESULT_VOID(cfgInit(), "config init");
|
||||
TEST_RESULT_VOID(cfgCommandSet(cfgCmdBackup), "backup command");
|
||||
|
||||
TEST_RESULT_STR(strPtr(varStr(cfgOptionDefault(cfgOptType))), "incr", "backup type default");
|
||||
TEST_RESULT_BOOL(varBool(cfgOptionDefault(cfgOptCompress)), "true", "backup compress default");
|
||||
TEST_RESULT_DOUBLE(varDbl(cfgOptionDefault(cfgOptProtocolTimeout)), 1830, "backup protocol-timeout default");
|
||||
TEST_RESULT_INT(varInt(cfgOptionDefault(cfgOptCompressLevel)), 6, "backup compress-level default");
|
||||
TEST_RESULT_PTR(cfgOptionDefault(cfgOptDbInclude), NULL, "backup db-include default is null");
|
||||
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptDbHost, cfgSourceParam, varNewStrZ("backup")), "backup host set");
|
||||
TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptDbHost, varNewStrZ("backup-default")), "backup host default");
|
||||
TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptDbHost, varNewStrZ("backup-default2")), "reset backup host default");
|
||||
TEST_RESULT_STR(strPtr(varStr(cfgOption(cfgOptDbHost))), "backup", "backup host value");
|
||||
TEST_RESULT_STR(strPtr(varStr(cfgOptionDefault(cfgOptDbHost))), "backup-default2", "backup host default");
|
||||
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptDbSocketPath, cfgSourceDefault, NULL), "backup db-socket-path set");
|
||||
TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptDbSocketPath, varNewStrZ("/to/socket")), "backup db-socket-path default");
|
||||
TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptDbSocketPath, varNewStrZ("/to/socket2")), "reset backup db-socket-path default");
|
||||
TEST_RESULT_STR(strPtr(varStr(cfgOption(cfgOptDbSocketPath))), "/to/socket2", "backup db-socket-path value");
|
||||
TEST_RESULT_STR(strPtr(varStr(cfgOptionDefault(cfgOptDbSocketPath))), "/to/socket2", "backup db-socket-path value default");
|
||||
}
|
||||
}
|
||||
|
@@ -125,4 +125,32 @@ void testRun()
|
||||
TEST_RESULT_BOOL(cfgDefOptionValid(cfgDefCmdBackup, cfgDefOptType), true, "option valid");
|
||||
TEST_RESULT_BOOL(cfgDefOptionValid(cfgDefCmdInfo, cfgDefOptType), false, "option not valid");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("cfgDefCommandHelp*() and cfgDefOptionHelp*()"))
|
||||
{
|
||||
TEST_RESULT_STR(cfgDefCommandHelpSummary(cfgDefCmdBackup), "Backup a database cluster.", "backup command help summary");
|
||||
TEST_RESULT_STR(
|
||||
cfgDefCommandHelpDescription(cfgDefCmdBackup),
|
||||
"pgBackRest does not have a built-in scheduler so it's best to run it from cron or some other scheduling mechanism.",
|
||||
"backup command help description");
|
||||
|
||||
TEST_RESULT_STR(cfgDefOptionHelpSection(cfgDefOptCompress), "general", "compress option help section");
|
||||
TEST_RESULT_STR(
|
||||
cfgDefOptionHelpSummary(cfgDefCmdBackup, cfgDefOptCompress), "Use gzip file compression.",
|
||||
"backup command, compress option help summary");
|
||||
TEST_RESULT_STR(
|
||||
cfgDefOptionHelpDescription(cfgDefCmdBackup, cfgDefOptCompress),
|
||||
"Backup files are compatible with command-line gzip tools.", "backup command, compress option help description");
|
||||
TEST_RESULT_STR(
|
||||
cfgDefOptionHelpSummary(cfgDefCmdBackup, cfgDefOptType), "Backup type.", "backup command, type option help summary");
|
||||
TEST_RESULT_STR(
|
||||
cfgDefOptionHelpDescription(cfgDefCmdBackup, cfgDefOptType),
|
||||
"The following backup types are supported:\n"
|
||||
"\n"
|
||||
"* full - all database cluster files will be copied and there will be no dependencies on previous backups.\n"
|
||||
"* incr - incremental from the last successful backup.\n"
|
||||
"* diff - like an incremental backup but always based on the last full backup.",
|
||||
"backup command, type option help description");
|
||||
}
|
||||
}
|
||||
|
@@ -49,5 +49,53 @@ void testRun()
|
||||
|
||||
TEST_RESULT_INT(logLevelStdOut, logLevelWarn, "console logging is off");
|
||||
TEST_RESULT_INT(logLevelStdErr, logLevelOff, "stderr logging is off");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew("pgbackrest"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("archive-push"));
|
||||
|
||||
TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load archive-push config");
|
||||
TEST_RESULT_PTR(cfgOptionDefault(cfgOptBackupCmd), NULL, " command archive-push, option backup-cmd default");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew("pgbackrest"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--backup-host=backup"));
|
||||
strLstAdd(argList, strNew("archive-push"));
|
||||
|
||||
TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load archive-push config");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(varStr(cfgOptionDefault(cfgOptBackupCmd))), strPtr(cfgExe()),
|
||||
" command archive-push, option backup-cmd default");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew("pgbackrest"));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("backup"));
|
||||
|
||||
TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load backup config");
|
||||
TEST_RESULT_PTR(cfgOptionDefault(cfgOptDbCmd), NULL, " command backup, option db1-cmd default");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew("pgbackrest"));
|
||||
strLstAdd(argList, strNew("--db-host=db"));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--db3-host=db"));
|
||||
strLstAdd(argList, strNew("--db3-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("backup"));
|
||||
|
||||
TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load backup config");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(varStr(cfgOptionDefault(cfgOptDbCmd))), strPtr(cfgExe()), " command backup, option db1-cmd default");
|
||||
TEST_RESULT_PTR(cfgOptionDefault(cfgOptDbCmd + 1), NULL, " command backup, option db2-cmd default");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(varStr(cfgOptionDefault(cfgOptDbCmd + 2))), strPtr(cfgExe()), " command backup, option db3-cmd default");
|
||||
}
|
||||
}
|
||||
|
271
test/src/module/help/helpTest.c
Normal file
271
test/src/module/help/helpTest.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Help Command
|
||||
***********************************************************************************************************************************/
|
||||
#include "config/parse.h"
|
||||
#include "storage/storage.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
void testRun()
|
||||
{
|
||||
// Program name a version are used multiple times
|
||||
const char *helpVersion = PGBACKREST_NAME " " PGBACKREST_VERSION;
|
||||
|
||||
// General help text is used in more than one test
|
||||
const char *generalHelp = strPtr(strNewFmt(
|
||||
"%s - General help\n"
|
||||
"\n"
|
||||
"Usage:\n"
|
||||
" pgbackrest [options] [command]\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" archive-get Get a WAL segment from the archive.\n"
|
||||
" archive-push Push a WAL segment to the archive.\n"
|
||||
" backup Backup a database cluster.\n"
|
||||
" check Check the configuration.\n"
|
||||
" expire Expire backups that exceed retention.\n"
|
||||
" help Get help.\n"
|
||||
" info Retrieve information about backups.\n"
|
||||
" restore Restore a database cluster.\n"
|
||||
" stanza-create Create the required stanza data.\n"
|
||||
" stanza-delete Delete a stanza.\n"
|
||||
" stanza-upgrade Upgrade a stanza.\n"
|
||||
" start Allow pgBackRest processes to run.\n"
|
||||
" stop Stop pgBackRest processes from running.\n"
|
||||
" version Get version.\n"
|
||||
"\n"
|
||||
"Use 'pgbackrest help [command]' for more information.\n",
|
||||
helpVersion));
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("helpRenderText()"))
|
||||
{
|
||||
TEST_RESULT_STR(
|
||||
strPtr(helpRenderText(strNew("this is a short sentence"), 0, false, 80)), "this is a short sentence", "one line");
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(helpRenderText(strNew("this is a short sentence"), 4, false, 14)),
|
||||
"this is a\n"
|
||||
" short\n"
|
||||
" sentence",
|
||||
"three lines, no indent first");
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(helpRenderText(strNew("This is a short paragraph.\n\nHere is another one."), 2, true, 16)),
|
||||
" This is a\n"
|
||||
" short\n"
|
||||
" paragraph.\n"
|
||||
"\n"
|
||||
" Here is\n"
|
||||
" another one.",
|
||||
"two paragraphs, indent first");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("helpRenderValue()"))
|
||||
{
|
||||
TEST_RESULT_STR(strPtr(helpRenderValue(varNewBool(true))), "y", "boolean y");
|
||||
TEST_RESULT_STR(strPtr(helpRenderValue(varNewBool(false))), "n", "boolean n");
|
||||
TEST_RESULT_STR(strPtr(helpRenderValue(varNewStrZ("test-string"))), "test-string", "string");
|
||||
TEST_RESULT_STR(strPtr(helpRenderValue(varNewDbl(1.234))), "1.234", "double");
|
||||
TEST_RESULT_STR(strPtr(helpRenderValue(varNewInt(1234))), "1234", "int");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("helpRender()"))
|
||||
{
|
||||
StringList *argList = NULL;
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "help from empty command line");
|
||||
TEST_RESULT_STR(strPtr(helpRender()), generalHelp, " check text");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
strLstAddZ(argList, "help");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "help from help command");
|
||||
TEST_RESULT_STR(strPtr(helpRender()), generalHelp, " check text");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
const char *commandHelp = strPtr(strNewFmt(
|
||||
"%s - 'archive-push' command help\n"
|
||||
"\n"
|
||||
"Push a WAL segment to the archive.\n"
|
||||
"\n"
|
||||
"The WAL segment may be pushed immediately to the archive or stored locally\n"
|
||||
"depending on the value of archive-async\n"
|
||||
"\n"
|
||||
"Command Options:\n"
|
||||
"\n"
|
||||
" --archive-async archive WAL segments asynchronously [default=n]\n"
|
||||
" --archive-queue-max limit size (in bytes) of the PostgreSQL archive\n"
|
||||
" queue\n"
|
||||
" --archive-timeout archive timeout [default=60]\n"
|
||||
"\n"
|
||||
"General Options:\n"
|
||||
"\n"
|
||||
" --buffer-size buffer size for file operations [current=32768,\n"
|
||||
" default=4194304]\n"
|
||||
" --cmd-ssh path to ssh client executable [default=ssh]\n"
|
||||
" --compress use gzip file compression [default=y]\n"
|
||||
" --compress-level compression level for stored files [default=6]\n"
|
||||
" --compress-level-network compression level for network transfer when\n"
|
||||
" compress=n [default=3]\n"
|
||||
" --config pgBackRest configuration file\n"
|
||||
" [default=/etc/pgbackrest.conf]\n"
|
||||
" --db-timeout database query timeout [default=1800]\n"
|
||||
" --lock-path path where lock files are stored\n"
|
||||
" [default=/tmp/pgbackrest]\n"
|
||||
" --neutral-umask use a neutral umask [default=y]\n"
|
||||
" --perl-bin path of Perl binary\n"
|
||||
" --process-max max processes to use for compress/transfer\n"
|
||||
" [default=1]\n"
|
||||
" --protocol-timeout protocol timeout [default=1830]\n"
|
||||
" --spool-path path where WAL segments are spooled during async\n"
|
||||
" archiving [default=/var/spool/pgbackrest]\n"
|
||||
" --stanza defines the stanza\n"
|
||||
"\n"
|
||||
"Log Options:\n"
|
||||
"\n"
|
||||
" --log-level-console level for console logging [default=warn]\n"
|
||||
" --log-level-file level for file logging [default=info]\n"
|
||||
" --log-level-stderr level for stderr logging [default=warn]\n"
|
||||
" --log-path path where log files are stored\n"
|
||||
" [default=/var/log/pgbackrest]\n"
|
||||
" --log-timestamp enable timestamp in logging [default=y]\n"
|
||||
"\n"
|
||||
"Repository Options:\n"
|
||||
"\n"
|
||||
" --backup-cmd pgBackRest exe path on the backup host\n"
|
||||
" --backup-config pgBackRest backup host configuration file\n"
|
||||
" [default=/etc/pgbackrest.conf]\n"
|
||||
" --backup-host backup host when operating remotely via SSH\n"
|
||||
" [current=backup.example.net]\n"
|
||||
" --backup-ssh-port backup server SSH port when backup-host is set\n"
|
||||
" --backup-user backup host user when backup-host is set\n"
|
||||
" [default=backrest]\n"
|
||||
" --repo-cipher-pass repository cipher passphrase\n"
|
||||
" --repo-cipher-type cipher used to encrypt the repository [default=none]\n"
|
||||
" --repo-path path where backups and archive are stored\n"
|
||||
" [default=/var/lib/pgbackrest]\n"
|
||||
" --repo-s3-bucket s3 repository bucket\n"
|
||||
" --repo-s3-ca-file s3 SSL CA File\n"
|
||||
" --repo-s3-ca-path s3 SSL CA Path\n"
|
||||
" --repo-s3-endpoint s3 repository endpoint\n"
|
||||
" --repo-s3-host s3 repository host\n"
|
||||
" --repo-s3-key s3 repository access key\n"
|
||||
" --repo-s3-key-secret s3 repository secret access key\n"
|
||||
" --repo-s3-region s3 repository region\n"
|
||||
" --repo-s3-verify-ssl verify S3 server certificate [default=y]\n"
|
||||
" --repo-type type of storage used for the repository\n"
|
||||
" [default=posix]\n"
|
||||
"\n"
|
||||
"Stanza Options:\n"
|
||||
"\n"
|
||||
" --db-host cluster host for operating remotely via SSH\n"
|
||||
" --db-path cluster data directory\n"
|
||||
" --db-ssh-port database server SSH port when db-host is set\n"
|
||||
"\n"
|
||||
"Use 'pgbackrest help archive-push [option]' for more information.\n",
|
||||
helpVersion));
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
strLstAddZ(argList, "help");
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, "--buffer-size=32768");
|
||||
strLstAddZ(argList, "--backup-host=backup.example.net");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "help for archive-push command");
|
||||
TEST_RESULT_STR(strPtr(helpRender()), commandHelp, " check text");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
strLstAddZ(argList, "help");
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, "buffer-size");
|
||||
strLstAddZ(argList, "buffer-size");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "parse too many options");
|
||||
TEST_ERROR(helpRender(), ParamInvalidError, "only one option allowed for option help");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
strLstAddZ(argList, "help");
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, BOGUS_STR);
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "parse bogus option");
|
||||
TEST_ERROR(helpRender(), OptionInvalidError, "option 'BOGUS' is not valid for command 'archive-push'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
const char *optionHelp = strPtr(strNewFmt(
|
||||
"%s - 'archive-push' command - 'buffer-size' option help\n"
|
||||
"\n"
|
||||
"Buffer size for file operations.\n"
|
||||
"\n"
|
||||
"Set the buffer size used for copy, compress, and uncompress functions. A\n"
|
||||
"maximum of 3 buffers will be in use at a time per process. An additional\n"
|
||||
"maximum of 256K per process may be used for zlib buffers.\n",
|
||||
helpVersion));
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
strLstAddZ(argList, "help");
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, "buffer-size");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "help for archive-push command, buffer-size option");
|
||||
TEST_RESULT_STR(strPtr(helpRender()), strPtr(strNewFmt("%s\ndefault: 4194304\n", optionHelp)), " check text");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
strLstAddZ(argList, "help");
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, "buffer-size");
|
||||
strLstAddZ(argList, "--buffer-size=32768");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "help for archive-push command, buffer-size option");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(helpRender()), strPtr(strNewFmt("%s\ncurrent: 32768\ndefault: 4194304\n", optionHelp)), " check text");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
optionHelp = strPtr(strNewFmt(
|
||||
"%s - 'archive-push' command - 'perl-bin' option help\n"
|
||||
"\n"
|
||||
"Path of Perl binary.\n"
|
||||
"\n"
|
||||
"Path of the Perl binary if /usr/bin/env perl won't work.\n",
|
||||
helpVersion));
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
strLstAddZ(argList, "help");
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, "perl-bin");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "help for archive-push command, perl-bin option");
|
||||
TEST_RESULT_STR(strPtr(helpRender()), optionHelp, " check text");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("cmdHelp()"))
|
||||
{
|
||||
StringList *argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "parse help from empty command line");
|
||||
|
||||
// Redirect stdout to a file
|
||||
int stdoutSave = dup(STDOUT_FILENO);
|
||||
String *stdoutFile = strNewFmt("%s/stdout.help", testPath());
|
||||
THROW_ON_SYS_ERROR(freopen(strPtr(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
||||
|
||||
// Not in a test wrapper to avoid writing to stdout
|
||||
cmdHelp();
|
||||
|
||||
// Restore normal stdout
|
||||
dup2(stdoutSave, STDOUT_FILENO);
|
||||
|
||||
Storage *storage = storageNew(strNew(testPath()), 0750, 65536, NULL);
|
||||
TEST_RESULT_STR(strPtr(strNewBuf(storageGet(storage, stdoutFile, false))), generalHelp, " check text");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user