1/*-------------------------------------------------------------------------
2 *
3 * xlogarchive.c
4 * Functions for archiving WAL files and restoring from the archive.
5 *
6 *
7 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * src/backend/access/transam/xlogarchive.c
11 *
12 *-------------------------------------------------------------------------
13 */
14
15#include "postgres.h"
16
17#include <sys/stat.h>
18#include <sys/wait.h>
19#include <signal.h>
20#include <unistd.h>
21
22#include "access/xlog.h"
23#include "access/xlog_internal.h"
24#include "miscadmin.h"
25#include "postmaster/startup.h"
26#include "replication/walsender.h"
27#include "storage/fd.h"
28#include "storage/ipc.h"
29#include "storage/lwlock.h"
30#include "storage/pmsignal.h"
31
32/*
33 * Attempt to retrieve the specified file from off-line archival storage.
34 * If successful, fill "path" with its complete path (note that this will be
35 * a temp file name that doesn't follow the normal naming convention), and
36 * return true.
37 *
38 * If not successful, fill "path" with the name of the normal on-line file
39 * (which may or may not actually exist, but we'll try to use it), and return
40 * false.
41 *
42 * For fixed-size files, the caller may pass the expected size as an
43 * additional crosscheck on successful recovery. If the file size is not
44 * known, set expectedSize = 0.
45 *
46 * When 'cleanupEnabled' is false, refrain from deleting any old WAL segments
47 * in the archive. This is used when fetching the initial checkpoint record,
48 * when we are not yet sure how far back we need the WAL.
49 */
50bool
51RestoreArchivedFile(char *path, const char *xlogfname,
52 const char *recovername, off_t expectedSize,
53 bool cleanupEnabled)
54{
55 char xlogpath[MAXPGPATH];
56 char xlogRestoreCmd[MAXPGPATH];
57 char lastRestartPointFname[MAXPGPATH];
58 char *dp;
59 char *endp;
60 const char *sp;
61 int rc;
62 struct stat stat_buf;
63 XLogSegNo restartSegNo;
64 XLogRecPtr restartRedoPtr;
65 TimeLineID restartTli;
66
67 /* In standby mode, restore_command might not be supplied */
68 if (recoveryRestoreCommand == NULL || strcmp(recoveryRestoreCommand, "") == 0)
69 goto not_available;
70
71 /*
72 * When doing archive recovery, we always prefer an archived log file even
73 * if a file of the same name exists in XLOGDIR. The reason is that the
74 * file in XLOGDIR could be an old, un-filled or partly-filled version
75 * that was copied and restored as part of backing up $PGDATA.
76 *
77 * We could try to optimize this slightly by checking the local copy
78 * lastchange timestamp against the archived copy, but we have no API to
79 * do this, nor can we guarantee that the lastchange timestamp was
80 * preserved correctly when we copied to archive. Our aim is robustness,
81 * so we elect not to do this.
82 *
83 * If we cannot obtain the log file from the archive, however, we will try
84 * to use the XLOGDIR file if it exists. This is so that we can make use
85 * of log segments that weren't yet transferred to the archive.
86 *
87 * Notice that we don't actually overwrite any files when we copy back
88 * from archive because the restore_command may inadvertently restore
89 * inappropriate xlogs, or they may be corrupt, so we may wish to fallback
90 * to the segments remaining in current XLOGDIR later. The
91 * copy-from-archive filename is always the same, ensuring that we don't
92 * run out of disk space on long recoveries.
93 */
94 snprintf(xlogpath, MAXPGPATH, XLOGDIR "/%s", recovername);
95
96 /*
97 * Make sure there is no existing file named recovername.
98 */
99 if (stat(xlogpath, &stat_buf) != 0)
100 {
101 if (errno != ENOENT)
102 ereport(FATAL,
103 (errcode_for_file_access(),
104 errmsg("could not stat file \"%s\": %m",
105 xlogpath)));
106 }
107 else
108 {
109 if (unlink(xlogpath) != 0)
110 ereport(FATAL,
111 (errcode_for_file_access(),
112 errmsg("could not remove file \"%s\": %m",
113 xlogpath)));
114 }
115
116 /*
117 * Calculate the archive file cutoff point for use during log shipping
118 * replication. All files earlier than this point can be deleted from the
119 * archive, though there is no requirement to do so.
120 *
121 * If cleanup is not enabled, initialise this with the filename of
122 * InvalidXLogRecPtr, which will prevent the deletion of any WAL files
123 * from the archive because of the alphabetic sorting property of WAL
124 * filenames.
125 *
126 * Once we have successfully located the redo pointer of the checkpoint
127 * from which we start recovery we never request a file prior to the redo
128 * pointer of the last restartpoint. When redo begins we know that we have
129 * successfully located it, so there is no need for additional status
130 * flags to signify the point when we can begin deleting WAL files from
131 * the archive.
132 */
133 if (cleanupEnabled)
134 {
135 GetOldestRestartPoint(&restartRedoPtr, &restartTli);
136 XLByteToSeg(restartRedoPtr, restartSegNo, wal_segment_size);
137 XLogFileName(lastRestartPointFname, restartTli, restartSegNo,
138 wal_segment_size);
139 /* we shouldn't need anything earlier than last restart point */
140 Assert(strcmp(lastRestartPointFname, xlogfname) <= 0);
141 }
142 else
143 XLogFileName(lastRestartPointFname, 0, 0L, wal_segment_size);
144
145 /*
146 * construct the command to be executed
147 */
148 dp = xlogRestoreCmd;
149 endp = xlogRestoreCmd + MAXPGPATH - 1;
150 *endp = '\0';
151
152 for (sp = recoveryRestoreCommand; *sp; sp++)
153 {
154 if (*sp == '%')
155 {
156 switch (sp[1])
157 {
158 case 'p':
159 /* %p: relative path of target file */
160 sp++;
161 StrNCpy(dp, xlogpath, endp - dp);
162 make_native_path(dp);
163 dp += strlen(dp);
164 break;
165 case 'f':
166 /* %f: filename of desired file */
167 sp++;
168 StrNCpy(dp, xlogfname, endp - dp);
169 dp += strlen(dp);
170 break;
171 case 'r':
172 /* %r: filename of last restartpoint */
173 sp++;
174 StrNCpy(dp, lastRestartPointFname, endp - dp);
175 dp += strlen(dp);
176 break;
177 case '%':
178 /* convert %% to a single % */
179 sp++;
180 if (dp < endp)
181 *dp++ = *sp;
182 break;
183 default:
184 /* otherwise treat the % as not special */
185 if (dp < endp)
186 *dp++ = *sp;
187 break;
188 }
189 }
190 else
191 {
192 if (dp < endp)
193 *dp++ = *sp;
194 }
195 }
196 *dp = '\0';
197
198 ereport(DEBUG3,
199 (errmsg_internal("executing restore command \"%s\"",
200 xlogRestoreCmd)));
201
202 /*
203 * Check signals before restore command and reset afterwards.
204 */
205 PreRestoreCommand();
206
207 /*
208 * Copy xlog from archival storage to XLOGDIR
209 */
210 rc = system(xlogRestoreCmd);
211
212 PostRestoreCommand();
213
214 if (rc == 0)
215 {
216 /*
217 * command apparently succeeded, but let's make sure the file is
218 * really there now and has the correct size.
219 */
220 if (stat(xlogpath, &stat_buf) == 0)
221 {
222 if (expectedSize > 0 && stat_buf.st_size != expectedSize)
223 {
224 int elevel;
225
226 /*
227 * If we find a partial file in standby mode, we assume it's
228 * because it's just being copied to the archive, and keep
229 * trying.
230 *
231 * Otherwise treat a wrong-sized file as FATAL to ensure the
232 * DBA would notice it, but is that too strong? We could try
233 * to plow ahead with a local copy of the file ... but the
234 * problem is that there probably isn't one, and we'd
235 * incorrectly conclude we've reached the end of WAL and we're
236 * done recovering ...
237 */
238 if (StandbyMode && stat_buf.st_size < expectedSize)
239 elevel = DEBUG1;
240 else
241 elevel = FATAL;
242 ereport(elevel,
243 (errmsg("archive file \"%s\" has wrong size: %lu instead of %lu",
244 xlogfname,
245 (unsigned long) stat_buf.st_size,
246 (unsigned long) expectedSize)));
247 return false;
248 }
249 else
250 {
251 ereport(LOG,
252 (errmsg("restored log file \"%s\" from archive",
253 xlogfname)));
254 strcpy(path, xlogpath);
255 return true;
256 }
257 }
258 else
259 {
260 /* stat failed */
261 if (errno != ENOENT)
262 ereport(FATAL,
263 (errcode_for_file_access(),
264 errmsg("could not stat file \"%s\": %m",
265 xlogpath)));
266 }
267 }
268
269 /*
270 * Remember, we rollforward UNTIL the restore fails so failure here is
271 * just part of the process... that makes it difficult to determine
272 * whether the restore failed because there isn't an archive to restore,
273 * or because the administrator has specified the restore program
274 * incorrectly. We have to assume the former.
275 *
276 * However, if the failure was due to any sort of signal, it's best to
277 * punt and abort recovery. (If we "return false" here, upper levels will
278 * assume that recovery is complete and start up the database!) It's
279 * essential to abort on child SIGINT and SIGQUIT, because per spec
280 * system() ignores SIGINT and SIGQUIT while waiting; if we see one of
281 * those it's a good bet we should have gotten it too.
282 *
283 * On SIGTERM, assume we have received a fast shutdown request, and exit
284 * cleanly. It's pure chance whether we receive the SIGTERM first, or the
285 * child process. If we receive it first, the signal handler will call
286 * proc_exit, otherwise we do it here. If we or the child process received
287 * SIGTERM for any other reason than a fast shutdown request, postmaster
288 * will perform an immediate shutdown when it sees us exiting
289 * unexpectedly.
290 *
291 * We treat hard shell errors such as "command not found" as fatal, too.
292 */
293 if (wait_result_is_signal(rc, SIGTERM))
294 proc_exit(1);
295
296 ereport(wait_result_is_any_signal(rc, true) ? FATAL : DEBUG2,
297 (errmsg("could not restore file \"%s\" from archive: %s",
298 xlogfname, wait_result_to_str(rc))));
299
300not_available:
301
302 /*
303 * if an archived file is not available, there might still be a version of
304 * this file in XLOGDIR, so return that as the filename to open.
305 *
306 * In many recovery scenarios we expect this to fail also, but if so that
307 * just means we've reached the end of WAL.
308 */
309 snprintf(path, MAXPGPATH, XLOGDIR "/%s", xlogfname);
310 return false;
311}
312
313/*
314 * Attempt to execute an external shell command during recovery.
315 *
316 * 'command' is the shell command to be executed, 'commandName' is a
317 * human-readable name describing the command emitted in the logs. If
318 * 'failOnSignal' is true and the command is killed by a signal, a FATAL
319 * error is thrown. Otherwise a WARNING is emitted.
320 *
321 * This is currently used for recovery_end_command and archive_cleanup_command.
322 */
323void
324ExecuteRecoveryCommand(const char *command, const char *commandName, bool failOnSignal)
325{
326 char xlogRecoveryCmd[MAXPGPATH];
327 char lastRestartPointFname[MAXPGPATH];
328 char *dp;
329 char *endp;
330 const char *sp;
331 int rc;
332 XLogSegNo restartSegNo;
333 XLogRecPtr restartRedoPtr;
334 TimeLineID restartTli;
335
336 Assert(command && commandName);
337
338 /*
339 * Calculate the archive file cutoff point for use during log shipping
340 * replication. All files earlier than this point can be deleted from the
341 * archive, though there is no requirement to do so.
342 */
343 GetOldestRestartPoint(&restartRedoPtr, &restartTli);
344 XLByteToSeg(restartRedoPtr, restartSegNo, wal_segment_size);
345 XLogFileName(lastRestartPointFname, restartTli, restartSegNo,
346 wal_segment_size);
347
348 /*
349 * construct the command to be executed
350 */
351 dp = xlogRecoveryCmd;
352 endp = xlogRecoveryCmd + MAXPGPATH - 1;
353 *endp = '\0';
354
355 for (sp = command; *sp; sp++)
356 {
357 if (*sp == '%')
358 {
359 switch (sp[1])
360 {
361 case 'r':
362 /* %r: filename of last restartpoint */
363 sp++;
364 StrNCpy(dp, lastRestartPointFname, endp - dp);
365 dp += strlen(dp);
366 break;
367 case '%':
368 /* convert %% to a single % */
369 sp++;
370 if (dp < endp)
371 *dp++ = *sp;
372 break;
373 default:
374 /* otherwise treat the % as not special */
375 if (dp < endp)
376 *dp++ = *sp;
377 break;
378 }
379 }
380 else
381 {
382 if (dp < endp)
383 *dp++ = *sp;
384 }
385 }
386 *dp = '\0';
387
388 ereport(DEBUG3,
389 (errmsg_internal("executing %s \"%s\"", commandName, command)));
390
391 /*
392 * execute the constructed command
393 */
394 rc = system(xlogRecoveryCmd);
395 if (rc != 0)
396 {
397 /*
398 * If the failure was due to any sort of signal, it's best to punt and
399 * abort recovery. See comments in RestoreArchivedFile().
400 */
401 ereport((failOnSignal && wait_result_is_any_signal(rc, true)) ? FATAL : WARNING,
402 /*------
403 translator: First %s represents a postgresql.conf parameter name like
404 "recovery_end_command", the 2nd is the value of that parameter, the
405 third an already translated error message. */
406 (errmsg("%s \"%s\": %s", commandName,
407 command, wait_result_to_str(rc))));
408 }
409}
410
411
412/*
413 * A file was restored from the archive under a temporary filename (path),
414 * and now we want to keep it. Rename it under the permanent filename in
415 * pg_wal (xlogfname), replacing any existing file with the same name.
416 */
417void
418KeepFileRestoredFromArchive(const char *path, const char *xlogfname)
419{
420 char xlogfpath[MAXPGPATH];
421 bool reload = false;
422 struct stat statbuf;
423
424 snprintf(xlogfpath, MAXPGPATH, XLOGDIR "/%s", xlogfname);
425
426 if (stat(xlogfpath, &statbuf) == 0)
427 {
428 char oldpath[MAXPGPATH];
429
430#ifdef WIN32
431 static unsigned int deletedcounter = 1;
432
433 /*
434 * On Windows, if another process (e.g a walsender process) holds the
435 * file open in FILE_SHARE_DELETE mode, unlink will succeed, but the
436 * file will still show up in directory listing until the last handle
437 * is closed, and we cannot rename the new file in its place until
438 * that. To avoid that problem, rename the old file to a temporary
439 * name first. Use a counter to create a unique filename, because the
440 * same file might be restored from the archive multiple times, and a
441 * walsender could still be holding onto an old deleted version of it.
442 */
443 snprintf(oldpath, MAXPGPATH, "%s.deleted%u",
444 xlogfpath, deletedcounter++);
445 if (rename(xlogfpath, oldpath) != 0)
446 {
447 ereport(ERROR,
448 (errcode_for_file_access(),
449 errmsg("could not rename file \"%s\" to \"%s\": %m",
450 xlogfpath, oldpath)));
451 }
452#else
453 /* same-size buffers, so this never truncates */
454 strlcpy(oldpath, xlogfpath, MAXPGPATH);
455#endif
456 if (unlink(oldpath) != 0)
457 ereport(FATAL,
458 (errcode_for_file_access(),
459 errmsg("could not remove file \"%s\": %m",
460 xlogfpath)));
461 reload = true;
462 }
463
464 durable_rename(path, xlogfpath, ERROR);
465
466 /*
467 * Create .done file forcibly to prevent the restored segment from being
468 * archived again later.
469 */
470 if (XLogArchiveMode != ARCHIVE_MODE_ALWAYS)
471 XLogArchiveForceDone(xlogfname);
472 else
473 XLogArchiveNotify(xlogfname);
474
475 /*
476 * If the existing file was replaced, since walsenders might have it open,
477 * request them to reload a currently-open segment. This is only required
478 * for WAL segments, walsenders don't hold other files open, but there's
479 * no harm in doing this too often, and we don't know what kind of a file
480 * we're dealing with here.
481 */
482 if (reload)
483 WalSndRqstFileReload();
484
485 /*
486 * Signal walsender that new WAL has arrived. Again, this isn't necessary
487 * if we restored something other than a WAL segment, but it does no harm
488 * either.
489 */
490 WalSndWakeup();
491}
492
493/*
494 * XLogArchiveNotify
495 *
496 * Create an archive notification file
497 *
498 * The name of the notification file is the message that will be picked up
499 * by the archiver, e.g. we write 0000000100000001000000C6.ready
500 * and the archiver then knows to archive XLOGDIR/0000000100000001000000C6,
501 * then when complete, rename it to 0000000100000001000000C6.done
502 */
503void
504XLogArchiveNotify(const char *xlog)
505{
506 char archiveStatusPath[MAXPGPATH];
507 FILE *fd;
508
509 /* insert an otherwise empty file called <XLOG>.ready */
510 StatusFilePath(archiveStatusPath, xlog, ".ready");
511 fd = AllocateFile(archiveStatusPath, "w");
512 if (fd == NULL)
513 {
514 ereport(LOG,
515 (errcode_for_file_access(),
516 errmsg("could not create archive status file \"%s\": %m",
517 archiveStatusPath)));
518 return;
519 }
520 if (FreeFile(fd))
521 {
522 ereport(LOG,
523 (errcode_for_file_access(),
524 errmsg("could not write archive status file \"%s\": %m",
525 archiveStatusPath)));
526 return;
527 }
528
529 /* Notify archiver that it's got something to do */
530 if (IsUnderPostmaster)
531 SendPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER);
532}
533
534/*
535 * Convenience routine to notify using segment number representation of filename
536 */
537void
538XLogArchiveNotifySeg(XLogSegNo segno)
539{
540 char xlog[MAXFNAMELEN];
541
542 XLogFileName(xlog, ThisTimeLineID, segno, wal_segment_size);
543 XLogArchiveNotify(xlog);
544}
545
546/*
547 * XLogArchiveForceDone
548 *
549 * Emit notification forcibly that an XLOG segment file has been successfully
550 * archived, by creating <XLOG>.done regardless of whether <XLOG>.ready
551 * exists or not.
552 */
553void
554XLogArchiveForceDone(const char *xlog)
555{
556 char archiveReady[MAXPGPATH];
557 char archiveDone[MAXPGPATH];
558 struct stat stat_buf;
559 FILE *fd;
560
561 /* Exit if already known done */
562 StatusFilePath(archiveDone, xlog, ".done");
563 if (stat(archiveDone, &stat_buf) == 0)
564 return;
565
566 /* If .ready exists, rename it to .done */
567 StatusFilePath(archiveReady, xlog, ".ready");
568 if (stat(archiveReady, &stat_buf) == 0)
569 {
570 (void) durable_rename(archiveReady, archiveDone, WARNING);
571 return;
572 }
573
574 /* insert an otherwise empty file called <XLOG>.done */
575 fd = AllocateFile(archiveDone, "w");
576 if (fd == NULL)
577 {
578 ereport(LOG,
579 (errcode_for_file_access(),
580 errmsg("could not create archive status file \"%s\": %m",
581 archiveDone)));
582 return;
583 }
584 if (FreeFile(fd))
585 {
586 ereport(LOG,
587 (errcode_for_file_access(),
588 errmsg("could not write archive status file \"%s\": %m",
589 archiveDone)));
590 return;
591 }
592}
593
594/*
595 * XLogArchiveCheckDone
596 *
597 * This is called when we are ready to delete or recycle an old XLOG segment
598 * file or backup history file. If it is okay to delete it then return true.
599 * If it is not time to delete it, make sure a .ready file exists, and return
600 * false.
601 *
602 * If <XLOG>.done exists, then return true; else if <XLOG>.ready exists,
603 * then return false; else create <XLOG>.ready and return false.
604 *
605 * The reason we do things this way is so that if the original attempt to
606 * create <XLOG>.ready fails, we'll retry during subsequent checkpoints.
607 */
608bool
609XLogArchiveCheckDone(const char *xlog)
610{
611 char archiveStatusPath[MAXPGPATH];
612 struct stat stat_buf;
613 bool inRecovery = RecoveryInProgress();
614
615 /*
616 * The file is always deletable if archive_mode is "off". On standbys
617 * archiving is disabled if archive_mode is "on", and enabled with
618 * "always". On a primary, archiving is enabled if archive_mode is "on"
619 * or "always".
620 */
621 if (!((XLogArchivingActive() && !inRecovery) ||
622 (XLogArchivingAlways() && inRecovery)))
623 return true;
624
625 /* First check for .done --- this means archiver is done with it */
626 StatusFilePath(archiveStatusPath, xlog, ".done");
627 if (stat(archiveStatusPath, &stat_buf) == 0)
628 return true;
629
630 /* check for .ready --- this means archiver is still busy with it */
631 StatusFilePath(archiveStatusPath, xlog, ".ready");
632 if (stat(archiveStatusPath, &stat_buf) == 0)
633 return false;
634
635 /* Race condition --- maybe archiver just finished, so recheck */
636 StatusFilePath(archiveStatusPath, xlog, ".done");
637 if (stat(archiveStatusPath, &stat_buf) == 0)
638 return true;
639
640 /* Retry creation of the .ready file */
641 XLogArchiveNotify(xlog);
642 return false;
643}
644
645/*
646 * XLogArchiveIsBusy
647 *
648 * Check to see if an XLOG segment file is still unarchived.
649 * This is almost but not quite the inverse of XLogArchiveCheckDone: in
650 * the first place we aren't chartered to recreate the .ready file, and
651 * in the second place we should consider that if the file is already gone
652 * then it's not busy. (This check is needed to handle the race condition
653 * that a checkpoint already deleted the no-longer-needed file.)
654 */
655bool
656XLogArchiveIsBusy(const char *xlog)
657{
658 char archiveStatusPath[MAXPGPATH];
659 struct stat stat_buf;
660
661 /* First check for .done --- this means archiver is done with it */
662 StatusFilePath(archiveStatusPath, xlog, ".done");
663 if (stat(archiveStatusPath, &stat_buf) == 0)
664 return false;
665
666 /* check for .ready --- this means archiver is still busy with it */
667 StatusFilePath(archiveStatusPath, xlog, ".ready");
668 if (stat(archiveStatusPath, &stat_buf) == 0)
669 return true;
670
671 /* Race condition --- maybe archiver just finished, so recheck */
672 StatusFilePath(archiveStatusPath, xlog, ".done");
673 if (stat(archiveStatusPath, &stat_buf) == 0)
674 return false;
675
676 /*
677 * Check to see if the WAL file has been removed by checkpoint, which
678 * implies it has already been archived, and explains why we can't see a
679 * status file for it.
680 */
681 snprintf(archiveStatusPath, MAXPGPATH, XLOGDIR "/%s", xlog);
682 if (stat(archiveStatusPath, &stat_buf) != 0 &&
683 errno == ENOENT)
684 return false;
685
686 return true;
687}
688
689/*
690 * XLogArchiveIsReadyOrDone
691 *
692 * Check to see if an XLOG segment file has a .ready or .done file.
693 * This is similar to XLogArchiveIsBusy(), but returns true if the file
694 * is already archived or is about to be archived.
695 *
696 * This is currently only used at recovery. During normal operation this
697 * would be racy: the file might get removed or marked with .ready as we're
698 * checking it, or immediately after we return.
699 */
700bool
701XLogArchiveIsReadyOrDone(const char *xlog)
702{
703 char archiveStatusPath[MAXPGPATH];
704 struct stat stat_buf;
705
706 /* First check for .done --- this means archiver is done with it */
707 StatusFilePath(archiveStatusPath, xlog, ".done");
708 if (stat(archiveStatusPath, &stat_buf) == 0)
709 return true;
710
711 /* check for .ready --- this means archiver is still busy with it */
712 StatusFilePath(archiveStatusPath, xlog, ".ready");
713 if (stat(archiveStatusPath, &stat_buf) == 0)
714 return true;
715
716 /* Race condition --- maybe archiver just finished, so recheck */
717 StatusFilePath(archiveStatusPath, xlog, ".done");
718 if (stat(archiveStatusPath, &stat_buf) == 0)
719 return true;
720
721 return false;
722}
723
724/*
725 * XLogArchiveIsReady
726 *
727 * Check to see if an XLOG segment file has an archive notification (.ready)
728 * file.
729 */
730bool
731XLogArchiveIsReady(const char *xlog)
732{
733 char archiveStatusPath[MAXPGPATH];
734 struct stat stat_buf;
735
736 StatusFilePath(archiveStatusPath, xlog, ".ready");
737 if (stat(archiveStatusPath, &stat_buf) == 0)
738 return true;
739
740 return false;
741}
742
743/*
744 * XLogArchiveCleanup
745 *
746 * Cleanup archive notification file(s) for a particular xlog segment
747 */
748void
749XLogArchiveCleanup(const char *xlog)
750{
751 char archiveStatusPath[MAXPGPATH];
752
753 /* Remove the .done file */
754 StatusFilePath(archiveStatusPath, xlog, ".done");
755 unlink(archiveStatusPath);
756 /* should we complain about failure? */
757
758 /* Remove the .ready file if present --- normally it shouldn't be */
759 StatusFilePath(archiveStatusPath, xlog, ".ready");
760 unlink(archiveStatusPath);
761 /* should we complain about failure? */
762}
763