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