diff --git a/.gitignore b/.gitignore
index bb732d401..e6dfdf61e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ test/vm/.vagrant
test/nytprof.out
test/nytprof/*
doc/output/*
+doc/doc/output/*
test/vm/package/*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86b97e874..cf8d416a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
## v0.90: UNDER DEVELOPMENT
__No Release Date Set__
-*
+* Allow restores to be performed on a read-only repository by using `--no-lock` and `--log-level-file=off`. The `--no-lock` option can only be used with restores.
## v0.88: Documentation and Minor Bug Fixes
__Released November 22, 2015__
diff --git a/doc/doc.pl b/doc/doc.pl
index 37f43cfb7..be2d96a3b 100755
--- a/doc/doc.pl
+++ b/doc/doc.pl
@@ -26,6 +26,7 @@ use BackRestDoc::Common::DocManifest;
use BackRestDoc::Common::DocRender;
use BackRestDoc::Html::DocHtmlSite;
use BackRestDoc::Latex::DocLatex;
+use BackRestDoc::Markdown::DocMarkdown;
use lib dirname($0) . '/../lib';
use BackRest::Common::Log;
@@ -121,7 +122,13 @@ logLevelSet(undef, uc($strLogLevel));
# Get the base path
my $strBasePath = abs_path(dirname($0));
-my $strOutputPath = "${strBasePath}/output";
+
+if (!defined($strDocPath))
+{
+ $strDocPath = $strBasePath;
+}
+
+my $strOutputPath = "${strDocPath}/output";
sub docProcess
{
@@ -196,6 +203,19 @@ for my $strOutput (@stryOutput)
docProcess("${strBasePath}/xml/index.xml", "${strBasePath}/../README.md", $oManifest);
docProcess("${strBasePath}/xml/change-log.xml", "${strBasePath}/../CHANGELOG.md", $oManifest);
}
+ elsif ($strOutput eq 'markdown')
+ {
+ my $oMarkdown =
+ new BackRestDoc::Markdown::DocMarkdown
+ (
+ $oManifest,
+ "${strBasePath}/xml",
+ "${strOutputPath}/markdown",
+ !$bNoExe
+ );
+
+ $oMarkdown->process();
+ }
elsif ($strOutput eq 'help' && $oManifest->isBackRest())
{
# Generate the command-line help
diff --git a/doc/lib/BackRestDoc/Common/DocManifest.pm b/doc/lib/BackRestDoc/Common/DocManifest.pm
index f4bfb2692..f9edbaaea 100644
--- a/doc/lib/BackRestDoc/Common/DocManifest.pm
+++ b/doc/lib/BackRestDoc/Common/DocManifest.pm
@@ -45,6 +45,8 @@ use constant RENDER_FILE => 'file';
use constant RENDER_TYPE => 'type';
use constant RENDER_TYPE_HTML => 'html';
push @EXPORT, qw(RENDER_TYPE_HTML);
+use constant RENDER_TYPE_MARKDOWN => 'markdown';
+ push @EXPORT, qw(RENDER_TYPE_MARKDOWN);
use constant RENDER_TYPE_PDF => 'pdf';
push @EXPORT, qw(RENDER_TYPE_PDF);
@@ -144,12 +146,35 @@ sub new
$$oRenderOutHash{source} = $strSource;
- # Get the filename if this is a pdf
- $$oRenderOutHash{menu} = $oRenderOut->paramGet('menu', false);
-
- if (defined($$oRenderOutHash{menu}) && $strType ne RENDER_TYPE_HTML)
+ # Get the menu caption
+ if (defined($oRenderOut->paramGet('menu', false)) && $strType ne RENDER_TYPE_HTML)
{
- confess &log(ERROR, 'only the html render type can have menu set')
+ confess &log(ERROR, "menu is only valid with html render type");
+ }
+
+ # Get the filename
+ if (defined($oRenderOut->paramGet('file', false)))
+ {
+ if ($strType eq RENDER_TYPE_HTML || $strType eq RENDER_TYPE_MARKDOWN)
+ {
+ $$oRenderOutHash{file} = $oRenderOut->paramGet('file');
+ }
+ else
+ {
+ confess &log(ERROR, "file is only valid with html or markdown render types");
+ }
+ }
+
+ if (defined($oRenderOut->paramGet('menu', false)))
+ {
+ if ($strType eq RENDER_TYPE_HTML)
+ {
+ $$oRenderOutHash{menu} = $oRenderOut->paramGet('menu', false);
+ }
+ else
+ {
+ confess &log(ERROR, 'only the html render type can have menu set');
+ }
}
logDebugMisc
diff --git a/doc/lib/BackRestDoc/Common/DocRender.pm b/doc/lib/BackRestDoc/Common/DocRender.pm
index c4598d585..0c14b09fa 100644
--- a/doc/lib/BackRestDoc/Common/DocRender.pm
+++ b/doc/lib/BackRestDoc/Common/DocRender.pm
@@ -628,14 +628,29 @@ sub processTag
{
if (!defined($strUrl))
{
- $strUrl = $oTag->paramGet('page');
+ $strUrl = $oTag->paramGet('page', false);
+
+ if (!defined($strUrl))
+ {
+ $strUrl = '#' . substr($oTag->paramGet('section'), 1);
+ }
}
$strBuffer = '' . $oTag->valueGet() . '';
}
elsif ($strType eq 'latex')
{
- $strBuffer = $oTag->valueGet();
+ if (!defined($strUrl))
+ {
+ $strUrl = $oTag->paramGet('page', false);
+
+ if (!defined($strUrl))
+ {
+ $strUrl = $oTag->paramGet('section');
+ }
+ }
+
+ $strBuffer = "\\hyperref[$strUrl]{" . $oTag->valueGet() . "}";
}
else
{
diff --git a/doc/lib/BackRestDoc/Html/DocHtmlPage.pm b/doc/lib/BackRestDoc/Html/DocHtmlPage.pm
index 9eb66bc03..81bf7bf81 100644
--- a/doc/lib/BackRestDoc/Html/DocHtmlPage.pm
+++ b/doc/lib/BackRestDoc/Html/DocHtmlPage.pm
@@ -207,7 +207,7 @@ sub sectionProcess
}
# Working variables
- $strAnchor = (defined($strAnchor) ? "${strAnchor}-" : '') . $oSection->paramGet('id');
+ $strAnchor = (defined($strAnchor) ? "${strAnchor}/" : '') . $oSection->paramGet('id');
# Create the section toc element
my $oSectionTocElement = new BackRestDoc::Html::DocHtmlElement(HTML_DIV, "section${iDepth}-toc");
diff --git a/doc/lib/BackRestDoc/Latex/DocLatexSection.pm b/doc/lib/BackRestDoc/Latex/DocLatexSection.pm
index d71f536fd..dbc8588bf 100644
--- a/doc/lib/BackRestDoc/Latex/DocLatexSection.pm
+++ b/doc/lib/BackRestDoc/Latex/DocLatexSection.pm
@@ -149,7 +149,7 @@ sub sectionProcess
confess &log(ASSERT, "section depth of ${iDepth} exceeds maximum");
}
- $strLatex .= "\{${strSectionTitle}\}\n";
+ $strLatex .= "\{${strSectionTitle}\}\\label{" . $oSection->paramGet('path', false) . "}\n";
foreach my $oChild ($oSection->nodeList())
{
diff --git a/doc/lib/BackRestDoc/Markdown/DocMarkdown.pm b/doc/lib/BackRestDoc/Markdown/DocMarkdown.pm
new file mode 100644
index 000000000..07ab4dd6f
--- /dev/null
+++ b/doc/lib/BackRestDoc/Markdown/DocMarkdown.pm
@@ -0,0 +1,119 @@
+####################################################################################################################################
+# DOC MARKDOWN MODULE
+####################################################################################################################################
+package BackRestDoc::Markdown::DocMarkdown;
+
+use strict;
+use warnings FATAL => qw(all);
+use Carp qw(confess);
+
+use Data::Dumper;
+use Exporter qw(import);
+ our @EXPORT = qw();
+use File::Basename qw(dirname);
+use File::Copy;
+use POSIX qw(strftime);
+use Storable qw(dclone);
+
+use lib dirname($0) . '/../lib';
+use BackRest::Common::Log;
+use BackRest::Common::String;
+use BackRest::FileCommon;
+use BackRest::Version;
+
+use lib dirname($0) . '/../test/lib';
+use BackRestTest::Common::ExecuteTest;
+
+use BackRestDoc::Common::DocConfig;
+use BackRestDoc::Common::DocManifest;
+use BackRestDoc::Markdown::DocMarkdownRender;
+
+####################################################################################################################################
+# Operation constants
+####################################################################################################################################
+use constant OP_DOC_MARKDOWN => 'DocMarkdown';
+
+use constant OP_DOC_MARKDOWN_NEW => OP_DOC_MARKDOWN . '->new';
+use constant OP_DOC_MARKDOWN_PROCESS => OP_DOC_MARKDOWN . '->process';
+
+####################################################################################################################################
+# CONSTRUCTOR
+####################################################################################################################################
+sub new
+{
+ my $class = shift; # Class name
+
+ # Create the class hash
+ my $self = {};
+ bless $self, $class;
+
+ $self->{strClass} = $class;
+
+ # Assign function parameters, defaults, and log debug info
+ (
+ my $strOperation,
+ $self->{oManifest},
+ $self->{strXmlPath},
+ $self->{strMarkdownPath},
+ $self->{bExe}
+ ) =
+ logDebugParam
+ (
+ OP_DOC_MARKDOWN_NEW, \@_,
+ {name => 'oManifest'},
+ {name => 'strXmlPath'},
+ {name => 'strMarkdownPath'},
+ {name => 'bExe'}
+ );
+
+ # Remove the current html path if it exists
+ if (-e $self->{strMarkdownPath})
+ {
+ executeTest("rm -rf $self->{strMarkdownPath}/*");
+ }
+ # Else create the html path
+ else
+ {
+ mkdir($self->{strMarkdownPath})
+ or confess &log(ERROR, "unable to create path $self->{strMarkdownPath}");
+ }
+
+ # Return from function and log return values if any
+ return logDebugReturn
+ (
+ $strOperation,
+ {name => 'self', value => $self}
+ );
+}
+
+####################################################################################################################################
+# process
+#
+# Generate the site html
+####################################################################################################################################
+sub process
+{
+ my $self = shift;
+
+ # Assign function parameters, defaults, and log debug info
+ my $strOperation = logDebugParam(OP_DOC_MARKDOWN_PROCESS);
+
+ foreach my $strRenderOutId ($self->{oManifest}->renderOutList(RENDER_TYPE_MARKDOWN))
+ {
+ my $oRenderOut = $self->{oManifest}->renderOutGet(RENDER_TYPE_MARKDOWN, $strRenderOutId);
+ my $strFile = "$self->{strMarkdownPath}/" . (defined($$oRenderOut{file}) ? $$oRenderOut{file} : "${strRenderOutId}.md");
+
+ &log(INFO, " render out: ${strRenderOutId}");
+
+ # Save the html page
+ fileStringWrite($strFile,
+ $self->{oManifest}->variableReplace((new BackRestDoc::Markdown::DocMarkdownRender($self->{oManifest},
+ $strRenderOutId, $self->{bExe}))->process()),
+ false);
+ }
+
+ # Return from function and log return values if any
+ logDebugReturn($strOperation);
+}
+
+1;
diff --git a/doc/lib/BackRestDoc/Markdown/DocMarkdownRender.pm b/doc/lib/BackRestDoc/Markdown/DocMarkdownRender.pm
new file mode 100644
index 000000000..3d1f7231b
--- /dev/null
+++ b/doc/lib/BackRestDoc/Markdown/DocMarkdownRender.pm
@@ -0,0 +1,472 @@
+####################################################################################################################################
+# DOC MARKDOWN RENDER MODULE
+####################################################################################################################################
+package BackRestDoc::Markdown::DocMarkdownRender;
+use parent 'BackRestDoc::Common::DocExecute';
+
+use strict;
+use warnings FATAL => qw(all);
+use Carp qw(confess);
+
+use Data::Dumper;
+use Exporter qw(import);
+ our @EXPORT = qw();
+use File::Basename qw(dirname);
+use File::Copy;
+use Storable qw(dclone);
+
+use lib dirname($0) . '/../lib';
+use BackRest::Common::Log;
+use BackRest::Common::String;
+use BackRest::Config::ConfigHelp;
+
+use BackRestDoc::Common::DocManifest;
+
+####################################################################################################################################
+# Operation constants
+####################################################################################################################################
+use constant OP_DOC_MARKDOWN_RENDER => 'DocMarkdownRender';
+
+use constant OP_DOC_MARKDOWN_RENDER_BACKREST_CONFIG_PROCESS => OP_DOC_MARKDOWN_RENDER . '->backrestConfigProcess';
+use constant OP_DOC_MARKDOWN_RENDER_NEW => OP_DOC_MARKDOWN_RENDER . '->new';
+use constant OP_DOC_MARKDOWN_RENDER_POSTGRES_CONFIG_PROCESS => OP_DOC_MARKDOWN_RENDER . '->postgresConfigProcess';
+use constant OP_DOC_MARKDOWN_RENDER_PROCESS => OP_DOC_MARKDOWN_RENDER . '->process';
+use constant OP_DOC_MARKDOWN_RENDER_SECTION_PROCESS => OP_DOC_MARKDOWN_RENDER . '->sectionProcess';
+
+####################################################################################################################################
+# CONSTRUCTOR
+####################################################################################################################################
+sub new
+{
+ my $class = shift; # Class name
+
+ # Assign function parameters, defaults, and log debug info
+ my
+ (
+ $strOperation,
+ $oManifest,
+ $strRenderOutKey,
+ $bExe
+ ) =
+ logDebugParam
+ (
+ OP_DOC_MARKDOWN_RENDER_NEW, \@_,
+ {name => 'oManifest'},
+ {name => 'strRenderOutKey'},
+ {name => 'bExe'}
+ );
+
+ # Create the class hash
+ my $self = $class->SUPER::new(RENDER_TYPE_MARKDOWN, $oManifest, $strRenderOutKey, $bExe);
+ bless $self, $class;
+
+ # Return from function and log return values if any
+ return logDebugReturn
+ (
+ $strOperation,
+ {name => 'self', value => $self}
+ );
+}
+
+####################################################################################################################################
+# process
+#
+# Generate the site html
+####################################################################################################################################
+sub process
+{
+ my $self = shift;
+
+ # Assign function parameters, defaults, and log debug info
+ my $strOperation = logDebugParam(OP_DOC_MARKDOWN_RENDER_PROCESS);
+
+ # Working variables
+ my $oPage = $self->{oDoc};
+
+ # Initialize page
+ my $strMarkdown = "# {[project]}" .
+ (defined($oPage->paramGet('title', false)) ? ' ' . $oPage->paramGet('title') : '');
+
+ if (defined($oPage->paramGet('subtitle', false)))
+ {
+ $strMarkdown .= ' - ' . $oPage->paramGet('subtitle');
+ }
+
+ # my $oHtmlBuilder = new BackRestDoc::Html::DocHtmlBuilder("{[project]} - Reliable PostgreSQL Backup",
+ # $strTitle . (defined($strSubTitle) ? " - ${strSubTitle}" : ''),
+ # $self->{bPretty});
+ #
+ # # Generate header
+ # my $oPageHeader = $oHtmlBuilder->bodyGet()->addNew(HTML_DIV, 'page-header');
+ #
+ # $oPageHeader->
+ # addNew(HTML_DIV, 'page-header-title',
+ # {strContent => $strTitle});
+ #
+ # if (defined($strSubTitle))
+ # {
+ # $oPageHeader->
+ # addNew(HTML_DIV, 'page-header-subtitle',
+ # {strContent => $strSubTitle});
+ # }
+ #
+ # # Generate menu
+ # my $oMenuBody = $oHtmlBuilder->bodyGet()->addNew(HTML_DIV, 'page-menu')->addNew(HTML_DIV, 'menu-body');
+ #
+ # if ($self->{strRenderOutKey} ne 'index')
+ # {
+ # my $oRenderOut = $self->{oManifest}->renderOutGet(RENDER_TYPE_HTML, 'index');
+ #
+ # $oMenuBody->
+ # addNew(HTML_DIV, 'menu')->
+ # addNew(HTML_A, 'menu-link', {strContent => $$oRenderOut{menu}, strRef => '{[project-url-root]}'});
+ # }
+ #
+ # foreach my $strRenderOutKey ($self->{oManifest}->renderOutList(RENDER_TYPE_HTML))
+ # {
+ # if ($strRenderOutKey ne $self->{strRenderOutKey} && $strRenderOutKey ne 'index')
+ # {
+ # my $oRenderOut = $self->{oManifest}->renderOutGet(RENDER_TYPE_HTML, $strRenderOutKey);
+ #
+ # $oMenuBody->
+ # addNew(HTML_DIV, 'menu')->
+ # addNew(HTML_A, 'menu-link', {strContent => $$oRenderOut{menu}, strRef => "${strRenderOutKey}.html"});
+ # }
+ # }
+ #
+ # # Generate table of contents
+ # my $oPageTocBody;
+ #
+ # if (!defined($oPage->paramGet('toc', false)) || $oPage->paramGet('toc') eq 'y')
+ # {
+ # my $oPageToc = $oHtmlBuilder->bodyGet()->addNew(HTML_DIV, 'page-toc');
+ #
+ # $oPageToc->
+ # addNew(HTML_DIV, 'page-toc-title',
+ # {strContent => "Table of Contents"});
+ #
+ # $oPageTocBody = $oPageToc->
+ # addNew(HTML_DIV, 'page-toc-body');
+ # }
+ #
+ # # Generate body
+ # my $oPageBody = $oHtmlBuilder->bodyGet()->addNew(HTML_DIV, 'page-body');
+
+ # Render sections
+ foreach my $oSection ($oPage->nodeList('section'))
+ {
+ $strMarkdown .= "\n\n" . $self->sectionProcess($oSection, 1);
+ }
+
+ $strMarkdown .= "\n";
+
+ # Return from function and log return values if any
+ return logDebugReturn
+ (
+ $strOperation,
+ {name => 'strMarkdown', value => $strMarkdown, trace => true}
+ );
+}
+
+####################################################################################################################################
+# sectionProcess
+####################################################################################################################################
+sub sectionProcess
+{
+ my $self = shift;
+
+ # Assign function parameters, defaults, and log debug info
+ my
+ (
+ $strOperation,
+ $oSection,
+ $iDepth
+ ) =
+ logDebugParam
+ (
+ OP_DOC_MARKDOWN_RENDER_SECTION_PROCESS, \@_,
+ {name => 'oSection'},
+ {name => 'iDepth'}
+ );
+
+ &log($iDepth == 1 ? INFO : DEBUG, (' ' x ($iDepth + 1)) . 'process section: ' . $oSection->paramGet('id'));
+
+ if ($iDepth > 3)
+ {
+ confess &log(ASSERT, "section depth of ${iDepth} exceeds maximum");
+ }
+
+ my $strMarkdown = '#' . ('#' x $iDepth) . ' ' . $self->processText($oSection->nodeGet('title')->textGet());
+
+ foreach my $oChild ($oSection->nodeList())
+ {
+ &log(DEBUG, (' ' x ($iDepth + 2)) . 'process child ' . $oChild->nameGet());
+
+ # Execute a command
+ if ($oChild->nameGet() eq 'execute-list')
+ {
+ # my $oSectionBodyExecute = $oSectionBodyElement->addNew(HTML_DIV, "execute");
+ # my $bFirst = true;
+ # my $strHostName = $self->{oManifest}->variableReplace($oChild->paramGet('host'));
+ #
+ # $oSectionBodyExecute->
+ # addNew(HTML_DIV, "execute-title",
+ # {strContent => "${strHostName}⇒ " .
+ # $self->processText($oChild->nodeGet('title')->textGet())});
+ #
+ # my $oExecuteBodyElement = $oSectionBodyExecute->addNew(HTML_DIV, "execute-body");
+ #
+ # foreach my $oExecute ($oChild->nodeList('execute'))
+ # {
+ # my $bExeShow = !$oExecute->paramTest('show', 'n');
+ # my $bExeExpectedError = defined($oExecute->paramGet('err-expect', false));
+ #
+ # my ($strCommand, $strOutput) = $self->execute($oSection, $strHostName, $oExecute, $iDepth + 3);
+ #
+ # if ($bExeShow)
+ # {
+ # # Add continuation chars and proper spacing
+ # $strCommand =~ s/\n/\n /smg;
+ #
+ # $oExecuteBodyElement->
+ # addNew(HTML_PRE, "execute-body-cmd",
+ # {strContent => $strCommand, bPre => true});
+ #
+ # my $strHighLight = $self->{oManifest}->variableReplace($oExecute->fieldGet('exe-highlight', false));
+ # my $bHighLightFound = false;
+ #
+ # if (defined($strOutput))
+ # {
+ # my $bHighLightOld;
+ # my $strHighLightOutput;
+ #
+ # if ($oExecute->fieldTest('exe-highlight-type', 'error'))
+ # {
+ # $bExeExpectedError = true;
+ # }
+ #
+ # foreach my $strLine (split("\n", $strOutput))
+ # {
+ # my $bHighLight = defined($strHighLight) && $strLine =~ /$strHighLight/;
+ #
+ # if (defined($bHighLightOld) && $bHighLight != $bHighLightOld)
+ # {
+ # $oExecuteBodyElement->
+ # addNew(HTML_PRE, 'execute-body-output' .
+ # ($bHighLightOld ? '-highlight' . ($bExeExpectedError ? '-error' : '') : ''),
+ # {strContent => $strHighLightOutput, bPre => true});
+ #
+ # undef($strHighLightOutput);
+ # }
+ #
+ # $strHighLightOutput .= (defined($strHighLightOutput) ? "\n" : '') . $strLine;
+ # $bHighLightOld = $bHighLight;
+ #
+ # $bHighLightFound = $bHighLightFound ? true : $bHighLight ? true : false;
+ # }
+ #
+ # if (defined($bHighLightOld))
+ # {
+ # $oExecuteBodyElement->
+ # addNew(HTML_PRE, 'execute-body-output' .
+ # ($bHighLightOld ? '-highlight' . ($bExeExpectedError ? '-error' : '') : ''),
+ # {strContent => $strHighLightOutput, bPre => true});
+ # }
+ #
+ # $bFirst = true;
+ # }
+ #
+ # if ($self->{bExe} && $self->isRequired($oSection) && defined($strHighLight) && !$bHighLightFound)
+ # {
+ # confess &log(ERROR, "unable to find a match for highlight: ${strHighLight}");
+ # }
+ # }
+ #
+ # $bFirst = false;
+ # }
+ }
+ # Add code block
+ elsif ($oChild->nameGet() eq 'code-block')
+ {
+ # $oSectionBodyElement->
+ # addNew(HTML_DIV, 'code-block',
+ # {strContent => $oChild->valueGet()});
+ }
+ # Add descriptive text
+ elsif ($oChild->nameGet() eq 'p')
+ {
+ $strMarkdown .= "\n\n" . $self->processText($oChild->textGet());
+ }
+ # Add option descriptive text
+ elsif ($oChild->nameGet() eq 'option-description')
+ {
+ # my $strOption = $oChild->paramGet("key");
+ # my $oDescription = ${$self->{oReference}->{oConfigHash}}{&CONFIG_HELP_OPTION}{$strOption}{&CONFIG_HELP_DESCRIPTION};
+ #
+ # if (!defined($oDescription))
+ # {
+ # confess &log(ERROR, "unable to find ${strOption} option in sections - try adding command?");
+ # }
+ #
+ # $oSectionBodyElement->
+ # addNew(HTML_DIV, 'section-body-text',
+ # {strContent => $self->processText($oDescription)});
+ }
+ # Add/remove backrest config options
+ elsif ($oChild->nameGet() eq 'backrest-config')
+ {
+ # my $oConfigElement = $self->backrestConfigProcess($oSection, $oChild, $iDepth + 3);
+ #
+ # if (defined($oConfigElement))
+ # {
+ # $oSectionBodyElement->add($oConfigElement);
+ # }
+ }
+ # Add/remove postgres config options
+ elsif ($oChild->nameGet() eq 'postgres-config')
+ {
+ # my $oConfigElement = $self->postgresConfigProcess($oSection, $oChild, $iDepth + 3);
+ #
+ # if (defined($oConfigElement))
+ # {
+ # $oSectionBodyElement->add($oConfigElement);
+ # }
+ }
+ # Add a subsection
+ elsif ($oChild->nameGet() eq 'section')
+ {
+ $strMarkdown .= "\n\n" . $self->sectionProcess($oChild, $iDepth + 1);
+ }
+ # Check if the child can be processed by a parent
+ else
+ {
+ # $self->sectionChildProcess($oSection, $oChild, $iDepth + 1);
+ }
+ }
+
+ # Return from function and log return values if any
+ return logDebugReturn
+ (
+ $strOperation,
+ {name => 'strMarkdown', value => $strMarkdown, trace => true}
+ );
+}
+
+####################################################################################################################################
+# backrestConfigProcess
+####################################################################################################################################
+sub backrestConfigProcess
+{
+ my $self = shift;
+
+ # Assign function parameters, defaults, and log debug info
+ my
+ (
+ $strOperation,
+ $oSection,
+ $oConfig,
+ $iDepth
+ ) =
+ logDebugParam
+ (
+ OP_DOC_MARKDOWN_RENDER_BACKREST_CONFIG_PROCESS, \@_,
+ {name => 'oSection'},
+ {name => 'oConfig'},
+ {name => 'iDepth'}
+ );
+
+ # # Generate the config
+ # my $oConfigElement;
+ # my ($strFile, $strConfig, $bShow) = $self->backrestConfig($oSection, $oConfig, $iDepth);
+ #
+ # if ($bShow)
+ # {
+ # my $strHostName = $self->{oManifest}->variableReplace($oConfig->paramGet('host'));
+ #
+ # # Render the config
+ # $oConfigElement = new BackRestDoc::Html::DocHtmlElement(HTML_DIV, "config");
+ #
+ # $oConfigElement->
+ # addNew(HTML_DIV, "config-title",
+ # {strContent => "${strHostName}:${strFile}" .
+ # " ⇒ " . $self->processText($oConfig->nodeGet('title')->textGet())});
+ #
+ # my $oConfigBodyElement = $oConfigElement->addNew(HTML_DIV, "config-body");
+ # #
+ # # $oConfigBodyElement->
+ # # addNew(HTML_DIV, "config-body-title",
+ # # {strContent => "${strFile}:"});
+ #
+ # $oConfigBodyElement->
+ # addNew(HTML_DIV, "config-body-output",
+ # {strContent => $strConfig});
+ # }
+ #
+ # # Return from function and log return values if any
+ # return logDebugReturn
+ # (
+ # $strOperation,
+ # {name => 'oConfigElement', value => $oConfigElement, trace => true}
+ # );
+}
+
+####################################################################################################################################
+# postgresConfigProcess
+####################################################################################################################################
+sub postgresConfigProcess
+{
+ my $self = shift;
+
+ # # Assign function parameters, defaults, and log debug info
+ # my
+ # (
+ # $strOperation,
+ # $oSection,
+ # $oConfig,
+ # $iDepth
+ # ) =
+ # logDebugParam
+ # (
+ # OP_DOC_MARKDOWN_RENDER_POSTGRES_CONFIG_PROCESS, \@_,
+ # {name => 'oSection'},
+ # {name => 'oConfig'},
+ # {name => 'iDepth'}
+ # );
+ #
+ # # Generate the config
+ # my $oConfigElement;
+ # my ($strFile, $strConfig, $bShow) = $self->postgresConfig($oSection, $oConfig, $iDepth);
+ #
+ # if ($bShow)
+ # {
+ # # Render the config
+ # my $strHostName = $self->{oManifest}->variableReplace($oConfig->paramGet('host'));
+ # $oConfigElement = new BackRestDoc::Html::DocHtmlElement(HTML_DIV, "config");
+ #
+ # $oConfigElement->
+ # addNew(HTML_DIV, "config-title",
+ # {strContent => "${strHostName}:${strFile}" .
+ # " ⇒ " . $self->processText($oConfig->nodeGet('title')->textGet())});
+ #
+ # my $oConfigBodyElement = $oConfigElement->addNew(HTML_DIV, "config-body");
+ #
+ # # $oConfigBodyElement->
+ # # addNew(HTML_DIV, "config-body-title",
+ # # {strContent => "append to ${strFile}:"});
+ #
+ # $oConfigBodyElement->
+ # addNew(HTML_DIV, "config-body-output",
+ # {strContent => defined($strConfig) ? $strConfig : ''});
+ #
+ # $oConfig->fieldSet('actual-config', $strConfig);
+ # }
+ #
+ # # Return from function and log return values if any
+ # return logDebugReturn
+ # (
+ # $strOperation,
+ # {name => 'oConfigElement', value => $oConfigElement, trace => true}
+ # );
+}
+
+1;
diff --git a/doc/resource/latex/preamble.tex b/doc/resource/latex/preamble.tex
index fb9816848..fc177c35b 100644
--- a/doc/resource/latex/preamble.tex
+++ b/doc/resource/latex/preamble.tex
@@ -20,6 +20,17 @@
% ----------------------------------------------------------------------------------------------------------------------------------
\usepackage[table]{xcolor}
\definecolor{ltgray}{HTML}{E8E8E8}
+\definecolor{dkblue}{HTML}{396A93}
+
+% Styling for hyperlinks
+% ----------------------------------------------------------------------------------------------------------------------------------
+\hypersetup{frenchlinks=true}
+% {
+% colorlinks,
+% linkcolor={dkblue},
+% citecolor={dkblue},
+% urlcolor={dkblue}
+%}
% Use listings package instead of verbatim for displaying code
% ----------------------------------------------------------------------------------------------------------------------------------
@@ -54,6 +65,11 @@
{\normalfont\normalsize\bfseries}{\theparagraph}{1em}{}
\titlespacing*{\paragraph}{0pt}{3.25ex plus 1ex minus .2ex}{1.5ex plus .2ex}
+% Section styling
+% ----------------------------------------------------------------------------------------------------------------------------------
+\usepackage{sectsty}
+\allsectionsfont{\color{dkblue}}
+
% Define source code highlighting
% ----------------------------------------------------------------------------------------------------------------------------------
\newcommand{\Hilight}{\makebox[0pt][l]{\color{cyan}\rule[-4pt]{0.65\linewidth}{14pt}}}
@@ -106,6 +122,8 @@
% Create the title page
% ----------------------------------------------------------------------------------------------------------------------------------
+\hypersetup{pageanchor=false}
+
\makeatletter
\begin{titlepage}
\begin{center}
@@ -122,6 +140,8 @@
\thispagestyle{empty}
\newpage
+\hypersetup{pageanchor=true}
+
% Generate TOC
% ----------------------------------------------------------------------------------------------------------------------------------
\setcounter{tocdepth}{3}
diff --git a/doc/xml/dtd/doc.dtd b/doc/xml/dtd/doc.dtd
index a44cb3fc8..71bc3f1ba 100644
--- a/doc/xml/dtd/doc.dtd
+++ b/doc/xml/dtd/doc.dtd
@@ -195,3 +195,4 @@
+
diff --git a/doc/xml/dtd/manifest.dtd b/doc/xml/dtd/manifest.dtd
index e1d739899..0c1041604 100644
--- a/doc/xml/dtd/manifest.dtd
+++ b/doc/xml/dtd/manifest.dtd
@@ -18,4 +18,5 @@
+
diff --git a/doc/xml/reference.xml b/doc/xml/reference.xml
index 16c87c3aa..95659c610 100644
--- a/doc/xml/reference.xml
+++ b/doc/xml/reference.xml
@@ -600,7 +600,7 @@
Locking during restores is enabled by default but can be disabled using --no-lock. Be very careful when disabling this option because simultaneous restores to a single path might result in a corrupt cluster.
- --no-lock
+ n
diff --git a/doc/xml/user-guide.xml b/doc/xml/user-guide.xml
index 831cc870e..8616b2ca4 100644
--- a/doc/xml/user-guide.xml
+++ b/doc/xml/user-guide.xml
@@ -80,7 +80,7 @@
Introduction
-
This user guide is intended to be followed sequentially from beginning to end — each section depends on the last. For example the Backup section relies on setup that is performed in the Quick Start section. Once you have up and running then skipping around is possible but it is recommended to follow the user guide in order the first time through.
+
This user guide is intended to be followed sequentially from beginning to end — each section depends on the last. For example the Backup section relies on setup that is performed in the Quick Start section. Once you have up and running then skipping around is possible but it is recommended to follow the user guide in order the first time through.
Although the examples are targeted at Ubuntu and 9.4 they will also work fine on Debian and it should be fairly easy to apply this guide to any Unix distribution and version. The only OS-specific commands are those to create, start, stop, and drop clusters. The commands will be the same on any Unix system though the locations to install Perl libraries and executables may vary. Configuring archiving is different on versions <= 8.4 and configuration information can be found in the documentation.
@@ -119,11 +119,11 @@
Write Ahead Log (WAL)
-
WAL is the mechanism by which ensures that no committed changes are lost. Transactions are written sequentially to the WAL and a transaction is considered to be committed when those writes are flushed to disk. Afterwards, a background process writes the changes into the main database cluster files (also known as the heap). In the event of a crash, the WAL is replayed to make the database consistent.
+
WAL is the mechanism that uses to ensure that no committed changes are lost. Transactions are written sequentially to the WAL and a transaction is considered to be committed when those writes are flushed to disk. Afterwards, a background process writes the changes into the main database cluster files (also known as the heap). In the event of a crash, the WAL is replayed to make the database consistent.
WAL is conceptually infinite but in practice is broken up into individual 16MB files called segments. WAL segments follow the naming convention 0000000100000A1E000000FE where the first 8 hexadecimal digits represent the timeline and the next 16 digits are the logical sequence number (LSN).
-
A valid backup will always include at least one WAL segment even if no writes were made to the database between backups.
+
A valid backup will always include at least one WAL segment even if explicit writes were made to the database between backups.
@@ -406,7 +406,7 @@
The oldest and newest backups are shown in the info output. The oldest backup will always be a full backup (indicated by an F at the end of the label) but the newest backup can be full, differential (ends with D), or incremental (ends with I).
-
More information about the backup command can be found in the Backup section.
+
More information about the backup command can be found in the Backup section.
@@ -476,7 +476,7 @@
This time the cluster started successfully since the restore replaced the missing pg_control file.
-
More information about the restore command can be found in the Restore section.
+
More information about the restore command can be found in the Restore section.
@@ -589,7 +589,7 @@
Retention
-
Generally it is best to retain as many backups as possible to provide a greater window for Point-in-Time Recovery, but practical concerns such as disk space must also be considered. Retention options remove older backups once they are no longer needed.
+
Generally it is best to retain as many backups as possible to provide a greater window for Point-in-Time Recovery, but practical concerns such as disk space must also be considered. Retention options remove older backups once they are no longer needed.
@@ -688,7 +688,7 @@
Delta Option
-
Restore a Backup in Quick Start required the database cluster directory to be cleaned before the restore could be performed. The delta allows to automatically determine which files in the database cluster directory can be preserved and which ones need to be restored from the backup. This is accomplished by calculating a SHA-1 cryptographic hash for each file in the database cluster directory. If the SHA-1 hash does not match the hash stored in the backup then that file will be restored. This operation is very efficient when combined with the thread-max option. Since the process is shut down during the restore, a larger number of threads can be used than might be desirable during a backup when the process is running.
+
Restore a Backup in Quick Start required the database cluster directory to be cleaned before the restore could be performed. The delta allows to automatically determine which files in the database cluster directory can be preserved and which ones need to be restored from the backup. This is accomplished by calculating a SHA-1 cryptographic hash for each file in the database cluster directory. If the SHA-1 hash does not match the hash stored in the backup then that file will be restored. This operation is very efficient when combined with the thread-max option. Since the process is shut down during the restore, a larger number of threads can be used than might be desirable during a backup when the process is running.
Restore a Backup in Quick Start performed default recovery, which is to play all the way to the end of the WAL stream. In the case of a hardware failure this is probably the most appropriate action but for data corruption scenarios (whether machine or human in origin) there is a better alternative called Point-in-Time Recovery (PITR).
+
Restore a Backup in Quick Start performed default recovery, which is to play all the way to the end of the WAL stream. In the case of a hardware failure this is probably the most appropriate action but for data corruption scenarios (whether machine or human in origin) there is a better alternative called Point-in-Time Recovery (PITR).
-
Point-in-Time Recovery (PITR) allows the WAL to be played from the last backup to a specified time, transaction id, or recovery point. For common recovery scenarios time-based recovery is arguably the most useful. A common recovery scenario is to restore a table that was accidentally dropped or data that was accidentally deleted. Recovering a dropped table is more dramatic so that's the example given here but deleted data would be recovered in exactly the same way.
+
Point-in-Time Recovery (PITR) allows the WAL to be played from the last backup to a specified time, transaction id, or recovery point. For common recovery scenarios time-based recovery is arguably the most useful. A typical recovery scenario is to restore a table that was accidentally dropped or data that was accidentally deleted. Recovering a dropped table is more dramatic so that's the example given here but deleted data would be recovered in exactly the same way.
Backup the {[postgres-cluster-demo]} cluster and create a table with very important data
@@ -925,7 +925,7 @@
Dedicated Backup Host
-
The configuration described in Quickstart is suitable for simple installations but for enterprise configurations it is more typical to have a dedicated backup host. This separates the backups and WAL archive from the database server so database host failures have less impact. It is still a good idea to employ traditional backup software to backup the backup host.
+
The configuration described in Quickstart is suitable for simple installations but for enterprise configurations it is more typical to have a dedicated backup host. This separates the backups and WAL archive from the database server so database host failures have less impact. It is still a good idea to employ traditional backup software to backup the backup host.
@@ -955,7 +955,7 @@
{[backrest-repo-path]}
-
For this example a new host named backup has been created to store the cluster backups. Follow the instructions in Installation to install and Create the Repository to create the repository. The backup host must also be configured with the database host/user and database path.
+
For this example a new host named backup has been created to store the cluster backups. Follow the instructions in Installation to install and Create the Repository to create the repository. The backup host must also be configured with the database host/user and database path.
Configure db-host/db-user and db-path
@@ -1125,7 +1125,7 @@
A hot standby performs replication using the WAL archive and allows read-only queries.
-
A new host named db-standby will be created to run the standby. Follow the instructions in Installation to install , Setup Demo Cluster to setup the demo cluster, and Create the Repository to create the repository on the db-standby host.
+
A new host named db-standby will be created to run the standby. Follow the instructions in Installation to install , Setup Demo Cluster to setup the demo cluster, and Create the Repository to create the repository on the db-standby host.
diff --git a/lib/BackRest/Common/Lock.pm b/lib/BackRest/Common/Lock.pm
index 2996bb36c..a4904939a 100644
--- a/lib/BackRest/Common/Lock.pm
+++ b/lib/BackRest/Common/Lock.pm
@@ -325,7 +325,7 @@ push @EXPORT, qw(lockStop);
####################################################################################################################################
# lockStopTest
#
-# Test if a stop file exists for the current stanza or all stanza.
+# Test if a stop file exists for the current stanza or all stanzas.
####################################################################################################################################
sub lockStopTest
{
@@ -370,7 +370,7 @@ push @EXPORT, qw(lockStopTest);
####################################################################################################################################
# lockStart
#
-# Remove the stop file so processes can run
+# Remove the stop file so processes can run.
####################################################################################################################################
sub lockStart
{
diff --git a/lib/BackRest/Config/Config.pm b/lib/BackRest/Config/Config.pm
index 9e56d8d0e..112c59915 100644
--- a/lib/BackRest/Config/Config.pm
+++ b/lib/BackRest/Config/Config.pm
@@ -180,7 +180,7 @@ 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_RESTORE_RECOVERY_OPTION => 'restore:recovery-option';
+use constant CONFIG_SECTION_RESTORE_RECOVERY_OPTION => 'restore:recovery-option';
push @EXPORT, qw(CONFIG_SECTION_RESTORE_RECOVERY_OPTION);
use constant CONFIG_SECTION_RESTORE_TABLESPACE_MAP => 'restore:tablespace-map';
push @EXPORT, qw(CONFIG_SECTION_RESTORE_TABLESPACE_MAP);
diff --git a/test/vm/Vagrantfile b/test/vm/Vagrantfile
index 773a7862f..130f1aa4e 100644
--- a/test/vm/Vagrantfile
+++ b/test/vm/Vagrantfile
@@ -116,7 +116,7 @@ Vagrant.configure(2) do |config|
echo 'export PATH' >> /etc/profile
/usr/local/texlive/2015/bin/x86_64-linux/tlmgr install caption xcolor listings parskip helvetic ltablex titlesec \
- epstopdf courier
+ epstopdf courier sectsty pgf ms
# Build images
docker build -f /backrest/test/vm/docker/u14-base -t vagrant/u14-base /backrest/test/vm