#include "postgres.h"#include <sys/types.h>#include <sys/stat.h>#include <sys/wait.h>#include <signal.h>#include <unistd.h>#include "access/xlog.h"#include "access/xlog_internal.h"#include "miscadmin.h"#include "postmaster/startup.h"#include "replication/walsender.h"#include "storage/fd.h"#include "storage/ipc.h"#include "storage/lwlock.h"#include "storage/pmsignal.h"
Go to the source code of this file.
Functions | |
| bool | RestoreArchivedFile (char *path, const char *xlogfname, const char *recovername, off_t expectedSize, bool cleanupEnabled) |
| void | ExecuteRecoveryCommand (char *command, char *commandName, bool failOnSignal) |
| void | KeepFileRestoredFromArchive (char *path, char *xlogfname) |
| void | XLogArchiveNotify (const char *xlog) |
| void | XLogArchiveNotifySeg (XLogSegNo segno) |
| void | XLogArchiveForceDone (const char *xlog) |
| bool | XLogArchiveCheckDone (const char *xlog) |
| bool | XLogArchiveIsBusy (const char *xlog) |
| void | XLogArchiveCleanup (const char *xlog) |
| void ExecuteRecoveryCommand | ( | char * | command, | |
| char * | commandName, | |||
| bool | failOnSignal | |||
| ) |
Definition at line 330 of file xlogarchive.c.
References Assert, DEBUG3, ereport, errmsg(), errmsg_internal(), FATAL, GetOldestRestartPoint(), MAXPGPATH, signaled, StrNCpy, system(), WARNING, WEXITSTATUS, WIFSIGNALED, XLByteToSeg, and XLogFileName.
Referenced by CreateRestartPoint(), and StartupXLOG().
{
char xlogRecoveryCmd[MAXPGPATH];
char lastRestartPointFname[MAXPGPATH];
char *dp;
char *endp;
const char *sp;
int rc;
bool signaled;
XLogSegNo restartSegNo;
XLogRecPtr restartRedoPtr;
TimeLineID restartTli;
Assert(command && commandName);
/*
* Calculate the archive file cutoff point for use during log shipping
* replication. All files earlier than this point can be deleted from the
* archive, though there is no requirement to do so.
*/
GetOldestRestartPoint(&restartRedoPtr, &restartTli);
XLByteToSeg(restartRedoPtr, restartSegNo);
XLogFileName(lastRestartPointFname, restartTli, restartSegNo);
/*
* construct the command to be executed
*/
dp = xlogRecoveryCmd;
endp = xlogRecoveryCmd + MAXPGPATH - 1;
*endp = '\0';
for (sp = command; *sp; sp++)
{
if (*sp == '%')
{
switch (sp[1])
{
case 'r':
/* %r: filename of last restartpoint */
sp++;
StrNCpy(dp, lastRestartPointFname, endp - dp);
dp += strlen(dp);
break;
case '%':
/* convert %% to a single % */
sp++;
if (dp < endp)
*dp++ = *sp;
break;
default:
/* otherwise treat the % as not special */
if (dp < endp)
*dp++ = *sp;
break;
}
}
else
{
if (dp < endp)
*dp++ = *sp;
}
}
*dp = '\0';
ereport(DEBUG3,
(errmsg_internal("executing %s \"%s\"", commandName, command)));
/*
* execute the constructed command
*/
rc = system(xlogRecoveryCmd);
if (rc != 0)
{
/*
* If the failure was due to any sort of signal, it's best to punt and
* abort recovery. See also detailed comments on signals in
* RestoreArchivedFile().
*/
signaled = WIFSIGNALED(rc) || WEXITSTATUS(rc) > 125;
ereport((signaled && failOnSignal) ? FATAL : WARNING,
/*------
translator: First %s represents a recovery.conf parameter name like
"recovery_end_command", and the 2nd is the value of that parameter. */
(errmsg("%s \"%s\": return code %d", commandName,
command, rc)));
}
}
| void KeepFileRestoredFromArchive | ( | char * | path, | |
| char * | xlogfname | |||
| ) |
Definition at line 426 of file xlogarchive.c.
References ereport, errcode_for_file_access(), errmsg(), ERROR, FATAL, MAXPGPATH, snprintf(), unlink(), WalSndRqstFileReload(), WalSndWakeup(), XLogArchiveForceDone(), and XLOGDIR.
Referenced by readTimeLineHistory(), restoreTimeLineHistoryFiles(), and XLogFileRead().
{
char xlogfpath[MAXPGPATH];
bool reload = false;
struct stat statbuf;
snprintf(xlogfpath, MAXPGPATH, XLOGDIR "/%s", xlogfname);
if (stat(xlogfpath, &statbuf) == 0)
{
char oldpath[MAXPGPATH];
#ifdef WIN32
static unsigned int deletedcounter = 1;
/*
* On Windows, if another process (e.g a walsender process) holds
* the file open in FILE_SHARE_DELETE mode, unlink will succeed,
* but the file will still show up in directory listing until the
* last handle is closed, and we cannot rename the new file in its
* place until that. To avoid that problem, rename the old file to
* a temporary name first. Use a counter to create a unique
* filename, because the same file might be restored from the
* archive multiple times, and a walsender could still be holding
* onto an old deleted version of it.
*/
snprintf(oldpath, MAXPGPATH, "%s.deleted%u",
xlogfpath, deletedcounter++);
if (rename(xlogfpath, oldpath) != 0)
{
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not rename file \"%s\" to \"%s\": %m",
xlogfpath, oldpath)));
}
#else
strncpy(oldpath, xlogfpath, MAXPGPATH);
#endif
if (unlink(oldpath) != 0)
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not remove file \"%s\": %m",
xlogfpath)));
reload = true;
}
if (rename(path, xlogfpath) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not rename file \"%s\" to \"%s\": %m",
path, xlogfpath)));
/*
* Create .done file forcibly to prevent the restored segment from
* being archived again later.
*/
XLogArchiveForceDone(xlogfname);
/*
* If the existing file was replaced, since walsenders might have it
* open, request them to reload a currently-open segment. This is only
* required for WAL segments, walsenders don't hold other files open, but
* there's no harm in doing this too often, and we don't know what kind
* of a file we're dealing with here.
*/
if (reload)
WalSndRqstFileReload();
/*
* Signal walsender that new WAL has arrived. Again, this isn't necessary
* if we restored something other than a WAL segment, but it does no harm
* either.
*/
WalSndWakeup();
}
| bool RestoreArchivedFile | ( | char * | path, | |
| const char * | xlogfname, | |||
| const char * | recovername, | |||
| off_t | expectedSize, | |||
| bool | cleanupEnabled | |||
| ) |
Definition at line 52 of file xlogarchive.c.
References Assert, DEBUG2, DEBUG3, elevel, ereport, errcode_for_file_access(), errmsg(), errmsg_internal(), FATAL, GetOldestRestartPoint(), LOG, make_native_path(), MAXPGPATH, NULL, PostRestoreCommand(), PreRestoreCommand(), proc_exit(), recoveryRestoreCommand, signaled, snprintf(), StandbyMode, StrNCpy, system(), unlink(), WEXITSTATUS, WIFSIGNALED, WTERMSIG, XLByteToSeg, XLOGDIR, and XLogFileName.
Referenced by existsTimeLineHistory(), readTimeLineHistory(), restoreTimeLineHistoryFiles(), writeTimeLineHistory(), and XLogFileRead().
{
char xlogpath[MAXPGPATH];
char xlogRestoreCmd[MAXPGPATH];
char lastRestartPointFname[MAXPGPATH];
char *dp;
char *endp;
const char *sp;
int rc;
bool signaled;
struct stat stat_buf;
XLogSegNo restartSegNo;
XLogRecPtr restartRedoPtr;
TimeLineID restartTli;
/* In standby mode, restore_command might not be supplied */
if (recoveryRestoreCommand == NULL)
goto not_available;
/*
* When doing archive recovery, we always prefer an archived log file even
* if a file of the same name exists in XLOGDIR. The reason is that the
* file in XLOGDIR could be an old, un-filled or partly-filled version
* that was copied and restored as part of backing up $PGDATA.
*
* We could try to optimize this slightly by checking the local copy
* lastchange timestamp against the archived copy, but we have no API to
* do this, nor can we guarantee that the lastchange timestamp was
* preserved correctly when we copied to archive. Our aim is robustness,
* so we elect not to do this.
*
* If we cannot obtain the log file from the archive, however, we will try
* to use the XLOGDIR file if it exists. This is so that we can make use
* of log segments that weren't yet transferred to the archive.
*
* Notice that we don't actually overwrite any files when we copy back
* from archive because the restore_command may inadvertently
* restore inappropriate xlogs, or they may be corrupt, so we may wish to
* fallback to the segments remaining in current XLOGDIR later. The
* copy-from-archive filename is always the same, ensuring that we don't
* run out of disk space on long recoveries.
*/
snprintf(xlogpath, MAXPGPATH, XLOGDIR "/%s", recovername);
/*
* Make sure there is no existing file named recovername.
*/
if (stat(xlogpath, &stat_buf) != 0)
{
if (errno != ENOENT)
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
xlogpath)));
}
else
{
if (unlink(xlogpath) != 0)
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not remove file \"%s\": %m",
xlogpath)));
}
/*
* Calculate the archive file cutoff point for use during log shipping
* replication. All files earlier than this point can be deleted from the
* archive, though there is no requirement to do so.
*
* If cleanup is not enabled, initialise this with the filename of
* InvalidXLogRecPtr, which will prevent the deletion of any WAL files
* from the archive because of the alphabetic sorting property of WAL
* filenames.
*
* Once we have successfully located the redo pointer of the checkpoint
* from which we start recovery we never request a file prior to the redo
* pointer of the last restartpoint. When redo begins we know that we have
* successfully located it, so there is no need for additional status
* flags to signify the point when we can begin deleting WAL files from
* the archive.
*/
if (cleanupEnabled)
{
GetOldestRestartPoint(&restartRedoPtr, &restartTli);
XLByteToSeg(restartRedoPtr, restartSegNo);
XLogFileName(lastRestartPointFname, restartTli, restartSegNo);
/* we shouldn't need anything earlier than last restart point */
Assert(strcmp(lastRestartPointFname, xlogfname) <= 0);
}
else
XLogFileName(lastRestartPointFname, 0, 0L);
/*
* construct the command to be executed
*/
dp = xlogRestoreCmd;
endp = xlogRestoreCmd + MAXPGPATH - 1;
*endp = '\0';
for (sp = recoveryRestoreCommand; *sp; sp++)
{
if (*sp == '%')
{
switch (sp[1])
{
case 'p':
/* %p: relative path of target file */
sp++;
StrNCpy(dp, xlogpath, endp - dp);
make_native_path(dp);
dp += strlen(dp);
break;
case 'f':
/* %f: filename of desired file */
sp++;
StrNCpy(dp, xlogfname, endp - dp);
dp += strlen(dp);
break;
case 'r':
/* %r: filename of last restartpoint */
sp++;
StrNCpy(dp, lastRestartPointFname, endp - dp);
dp += strlen(dp);
break;
case '%':
/* convert %% to a single % */
sp++;
if (dp < endp)
*dp++ = *sp;
break;
default:
/* otherwise treat the % as not special */
if (dp < endp)
*dp++ = *sp;
break;
}
}
else
{
if (dp < endp)
*dp++ = *sp;
}
}
*dp = '\0';
ereport(DEBUG3,
(errmsg_internal("executing restore command \"%s\"",
xlogRestoreCmd)));
/*
* Check signals before restore command and reset afterwards.
*/
PreRestoreCommand();
/*
* Copy xlog from archival storage to XLOGDIR
*/
rc = system(xlogRestoreCmd);
PostRestoreCommand();
if (rc == 0)
{
/*
* command apparently succeeded, but let's make sure the file is
* really there now and has the correct size.
*/
if (stat(xlogpath, &stat_buf) == 0)
{
if (expectedSize > 0 && stat_buf.st_size != expectedSize)
{
int elevel;
/*
* If we find a partial file in standby mode, we assume it's
* because it's just being copied to the archive, and keep
* trying.
*
* Otherwise treat a wrong-sized file as FATAL to ensure the
* DBA would notice it, but is that too strong? We could try
* to plow ahead with a local copy of the file ... but the
* problem is that there probably isn't one, and we'd
* incorrectly conclude we've reached the end of WAL and we're
* done recovering ...
*/
if (StandbyMode && stat_buf.st_size < expectedSize)
elevel = DEBUG1;
else
elevel = FATAL;
ereport(elevel,
(errmsg("archive file \"%s\" has wrong size: %lu instead of %lu",
xlogfname,
(unsigned long) stat_buf.st_size,
(unsigned long) expectedSize)));
return false;
}
else
{
ereport(LOG,
(errmsg("restored log file \"%s\" from archive",
xlogfname)));
strcpy(path, xlogpath);
return true;
}
}
else
{
/* stat failed */
if (errno != ENOENT)
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
xlogpath)));
}
}
/*
* Remember, we rollforward UNTIL the restore fails so failure here is
* just part of the process... that makes it difficult to determine
* whether the restore failed because there isn't an archive to restore,
* or because the administrator has specified the restore program
* incorrectly. We have to assume the former.
*
* However, if the failure was due to any sort of signal, it's best to
* punt and abort recovery. (If we "return false" here, upper levels will
* assume that recovery is complete and start up the database!) It's
* essential to abort on child SIGINT and SIGQUIT, because per spec
* system() ignores SIGINT and SIGQUIT while waiting; if we see one of
* those it's a good bet we should have gotten it too.
*
* On SIGTERM, assume we have received a fast shutdown request, and exit
* cleanly. It's pure chance whether we receive the SIGTERM first, or the
* child process. If we receive it first, the signal handler will call
* proc_exit, otherwise we do it here. If we or the child process received
* SIGTERM for any other reason than a fast shutdown request, postmaster
* will perform an immediate shutdown when it sees us exiting
* unexpectedly.
*
* Per the Single Unix Spec, shells report exit status > 128 when a called
* command died on a signal. Also, 126 and 127 are used to report
* problems such as an unfindable command; treat those as fatal errors
* too.
*/
if (WIFSIGNALED(rc) && WTERMSIG(rc) == SIGTERM)
proc_exit(1);
signaled = WIFSIGNALED(rc) || WEXITSTATUS(rc) > 125;
ereport(signaled ? FATAL : DEBUG2,
(errmsg("could not restore file \"%s\" from archive: return code %d",
xlogfname, rc)));
not_available:
/*
* if an archived file is not available, there might still be a version of
* this file in XLOGDIR, so return that as the filename to open.
*
* In many recovery scenarios we expect this to fail also, but if so that
* just means we've reached the end of WAL.
*/
snprintf(path, MAXPGPATH, XLOGDIR "/%s", xlogfname);
return false;
}
| bool XLogArchiveCheckDone | ( | const char * | xlog | ) |
Definition at line 621 of file xlogarchive.c.
References StatusFilePath, XLogArchiveNotify(), and XLogArchivingActive.
Referenced by CleanupBackupHistory(), and RemoveOldXlogFiles().
{
char archiveStatusPath[MAXPGPATH];
struct stat stat_buf;
/* Always deletable if archiving is off */
if (!XLogArchivingActive())
return true;
/* First check for .done --- this means archiver is done with it */
StatusFilePath(archiveStatusPath, xlog, ".done");
if (stat(archiveStatusPath, &stat_buf) == 0)
return true;
/* check for .ready --- this means archiver is still busy with it */
StatusFilePath(archiveStatusPath, xlog, ".ready");
if (stat(archiveStatusPath, &stat_buf) == 0)
return false;
/* Race condition --- maybe archiver just finished, so recheck */
StatusFilePath(archiveStatusPath, xlog, ".done");
if (stat(archiveStatusPath, &stat_buf) == 0)
return true;
/* Retry creation of the .ready file */
XLogArchiveNotify(xlog);
return false;
}
| void XLogArchiveCleanup | ( | const char * | xlog | ) |
Definition at line 700 of file xlogarchive.c.
References StatusFilePath, and unlink().
Referenced by CleanupBackupHistory(), exitArchiveRecovery(), and RemoveOldXlogFiles().
{
char archiveStatusPath[MAXPGPATH];
/* Remove the .done file */
StatusFilePath(archiveStatusPath, xlog, ".done");
unlink(archiveStatusPath);
/* should we complain about failure? */
/* Remove the .ready file if present --- normally it shouldn't be */
StatusFilePath(archiveStatusPath, xlog, ".ready");
unlink(archiveStatusPath);
/* should we complain about failure? */
}
| void XLogArchiveForceDone | ( | const char * | xlog | ) |
Definition at line 561 of file xlogarchive.c.
References AllocateFile(), ereport, errcode_for_file_access(), errmsg(), FreeFile(), LOG, NULL, StatusFilePath, and WARNING.
Referenced by KeepFileRestoredFromArchive(), WalReceiverMain(), and XLogWalRcvWrite().
{
char archiveReady[MAXPGPATH];
char archiveDone[MAXPGPATH];
struct stat stat_buf;
FILE *fd;
/* Exit if already known done */
StatusFilePath(archiveDone, xlog, ".done");
if (stat(archiveDone, &stat_buf) == 0)
return;
/* If .ready exists, rename it to .done */
StatusFilePath(archiveReady, xlog, ".ready");
if (stat(archiveReady, &stat_buf) == 0)
{
if (rename(archiveReady, archiveDone) < 0)
ereport(WARNING,
(errcode_for_file_access(),
errmsg("could not rename file \"%s\" to \"%s\": %m",
archiveReady, archiveDone)));
return;
}
/* insert an otherwise empty file called <XLOG>.done */
fd = AllocateFile(archiveDone, "w");
if (fd == NULL)
{
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not create archive status file \"%s\": %m",
archiveDone)));
return;
}
if (FreeFile(fd))
{
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not write archive status file \"%s\": %m",
archiveDone)));
return;
}
}
| bool XLogArchiveIsBusy | ( | const char * | xlog | ) |
Definition at line 661 of file xlogarchive.c.
References MAXPGPATH, snprintf(), StatusFilePath, and XLOGDIR.
Referenced by do_pg_stop_backup().
{
char archiveStatusPath[MAXPGPATH];
struct stat stat_buf;
/* First check for .done --- this means archiver is done with it */
StatusFilePath(archiveStatusPath, xlog, ".done");
if (stat(archiveStatusPath, &stat_buf) == 0)
return false;
/* check for .ready --- this means archiver is still busy with it */
StatusFilePath(archiveStatusPath, xlog, ".ready");
if (stat(archiveStatusPath, &stat_buf) == 0)
return true;
/* Race condition --- maybe archiver just finished, so recheck */
StatusFilePath(archiveStatusPath, xlog, ".done");
if (stat(archiveStatusPath, &stat_buf) == 0)
return false;
/*
* Check to see if the WAL file has been removed by checkpoint, which
* implies it has already been archived, and explains why we can't see a
* status file for it.
*/
snprintf(archiveStatusPath, MAXPGPATH, XLOGDIR "/%s", xlog);
if (stat(archiveStatusPath, &stat_buf) != 0 &&
errno == ENOENT)
return false;
return true;
}
| void XLogArchiveNotify | ( | const char * | xlog | ) |
Definition at line 511 of file xlogarchive.c.
References AllocateFile(), ereport, errcode_for_file_access(), errmsg(), FreeFile(), IsUnderPostmaster, LOG, NULL, PMSIGNAL_WAKEN_ARCHIVER, SendPostmasterSignal(), and StatusFilePath.
Referenced by exitArchiveRecovery(), writeTimeLineHistory(), XLogArchiveCheckDone(), and XLogArchiveNotifySeg().
{
char archiveStatusPath[MAXPGPATH];
FILE *fd;
/* insert an otherwise empty file called <XLOG>.ready */
StatusFilePath(archiveStatusPath, xlog, ".ready");
fd = AllocateFile(archiveStatusPath, "w");
if (fd == NULL)
{
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not create archive status file \"%s\": %m",
archiveStatusPath)));
return;
}
if (FreeFile(fd))
{
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not write archive status file \"%s\": %m",
archiveStatusPath)));
return;
}
/* Notify archiver that it's got something to do */
if (IsUnderPostmaster)
SendPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER);
}
| void XLogArchiveNotifySeg | ( | XLogSegNo | segno | ) |
Definition at line 545 of file xlogarchive.c.
References ThisTimeLineID, XLogArchiveNotify(), and XLogFileName.
Referenced by XLogWrite().
{
char xlog[MAXFNAMELEN];
XLogFileName(xlog, ThisTimeLineID, segno);
XLogArchiveNotify(xlog);
}
1.7.1