Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pgarch.c
4 : *
5 : * PostgreSQL WAL archiver
6 : *
7 : * All functions relating to archiver are included here
8 : *
9 : * - All functions executed by archiver process
10 : *
11 : * - archiver is forked from postmaster, and the two
12 : * processes then communicate using signals. All functions
13 : * executed by postmaster are included in this file.
14 : *
15 : * Initial author: Simon Riggs simon@2ndquadrant.com
16 : *
17 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
18 : * Portions Copyright (c) 1994, Regents of the University of California
19 : *
20 : *
21 : * IDENTIFICATION
22 : * src/backend/postmaster/pgarch.c
23 : *
24 : *-------------------------------------------------------------------------
25 : */
26 : #include "postgres.h"
27 :
28 : #include <fcntl.h>
29 : #include <signal.h>
30 : #include <time.h>
31 : #include <sys/time.h>
32 : #include <sys/wait.h>
33 : #include <unistd.h>
34 :
35 : #include "access/xlog.h"
36 : #include "access/xlog_internal.h"
37 : #include "libpq/pqsignal.h"
38 : #include "miscadmin.h"
39 : #include "pgstat.h"
40 : #include "postmaster/fork_process.h"
41 : #include "postmaster/pgarch.h"
42 : #include "postmaster/postmaster.h"
43 : #include "storage/dsm.h"
44 : #include "storage/fd.h"
45 : #include "storage/ipc.h"
46 : #include "storage/latch.h"
47 : #include "storage/pg_shmem.h"
48 : #include "storage/pmsignal.h"
49 : #include "utils/guc.h"
50 : #include "utils/ps_status.h"
51 :
52 :
53 : /* ----------
54 : * Timer definitions.
55 : * ----------
56 : */
57 : #define PGARCH_AUTOWAKE_INTERVAL 60 /* How often to force a poll of the
58 : * archive status directory; in seconds. */
59 : #define PGARCH_RESTART_INTERVAL 10 /* How often to attempt to restart a
60 : * failed archiver; in seconds. */
61 :
62 : #define NUM_ARCHIVE_RETRIES 3
63 :
64 :
65 : /* ----------
66 : * Local data
67 : * ----------
68 : */
69 : static time_t last_pgarch_start_time;
70 : static time_t last_sigterm_time = 0;
71 :
72 : /*
73 : * Flags set by interrupt handlers for later service in the main loop.
74 : */
75 : static volatile sig_atomic_t got_SIGHUP = false;
76 : static volatile sig_atomic_t got_SIGTERM = false;
77 : static volatile sig_atomic_t wakened = false;
78 : static volatile sig_atomic_t ready_to_stop = false;
79 :
80 : /* ----------
81 : * Local function forward declarations
82 : * ----------
83 : */
84 : #ifdef EXEC_BACKEND
85 : static pid_t pgarch_forkexec(void);
86 : #endif
87 :
88 : NON_EXEC_STATIC void PgArchiverMain(int argc, char *argv[]) pg_attribute_noreturn();
89 : static void pgarch_exit(SIGNAL_ARGS);
90 : static void ArchSigHupHandler(SIGNAL_ARGS);
91 : static void ArchSigTermHandler(SIGNAL_ARGS);
92 : static void pgarch_waken(SIGNAL_ARGS);
93 : static void pgarch_waken_stop(SIGNAL_ARGS);
94 : static void pgarch_MainLoop(void);
95 : static void pgarch_ArchiverCopyLoop(void);
96 : static bool pgarch_archiveXlog(char *xlog);
97 : static bool pgarch_readyXlog(char *xlog);
98 : static void pgarch_archiveDone(char *xlog);
99 :
100 :
101 : /* ------------------------------------------------------------
102 : * Public functions called from postmaster follow
103 : * ------------------------------------------------------------
104 : */
105 :
106 : /*
107 : * pgarch_start
108 : *
109 : * Called from postmaster at startup or after an existing archiver
110 : * died. Attempt to fire up a fresh archiver process.
111 : *
112 : * Returns PID of child process, or 0 if fail.
113 : *
114 : * Note: if fail, we will be called again from the postmaster main loop.
115 : */
116 : int
117 0 : pgarch_start(void)
118 : {
119 : time_t curtime;
120 : pid_t pgArchPid;
121 :
122 : /*
123 : * Do nothing if no archiver needed
124 : */
125 0 : if (!XLogArchivingActive())
126 0 : return 0;
127 :
128 : /*
129 : * Do nothing if too soon since last archiver start. This is a safety
130 : * valve to protect against continuous respawn attempts if the archiver is
131 : * dying immediately at launch. Note that since we will be re-called from
132 : * the postmaster main loop, we will get another chance later.
133 : */
134 0 : curtime = time(NULL);
135 0 : if ((unsigned int) (curtime - last_pgarch_start_time) <
136 : (unsigned int) PGARCH_RESTART_INTERVAL)
137 0 : return 0;
138 0 : last_pgarch_start_time = curtime;
139 :
140 : #ifdef EXEC_BACKEND
141 : switch ((pgArchPid = pgarch_forkexec()))
142 : #else
143 0 : switch ((pgArchPid = fork_process()))
144 : #endif
145 : {
146 : case -1:
147 0 : ereport(LOG,
148 : (errmsg("could not fork archiver: %m")));
149 0 : return 0;
150 :
151 : #ifndef EXEC_BACKEND
152 : case 0:
153 : /* in postmaster child ... */
154 0 : InitPostmasterChild();
155 :
156 : /* Close the postmaster's sockets */
157 0 : ClosePostmasterPorts(false);
158 :
159 : /* Drop our connection to postmaster's shared memory, as well */
160 0 : dsm_detach_all();
161 0 : PGSharedMemoryDetach();
162 :
163 0 : PgArchiverMain(0, NULL);
164 : break;
165 : #endif
166 :
167 : default:
168 0 : return (int) pgArchPid;
169 : }
170 :
171 : /* shouldn't get here */
172 : return 0;
173 : }
174 :
175 : /* ------------------------------------------------------------
176 : * Local functions called by archiver follow
177 : * ------------------------------------------------------------
178 : */
179 :
180 :
181 : #ifdef EXEC_BACKEND
182 :
183 : /*
184 : * pgarch_forkexec() -
185 : *
186 : * Format up the arglist for, then fork and exec, archive process
187 : */
188 : static pid_t
189 : pgarch_forkexec(void)
190 : {
191 : char *av[10];
192 : int ac = 0;
193 :
194 : av[ac++] = "postgres";
195 :
196 : av[ac++] = "--forkarch";
197 :
198 : av[ac++] = NULL; /* filled in by postmaster_forkexec */
199 :
200 : av[ac] = NULL;
201 : Assert(ac < lengthof(av));
202 :
203 : return postmaster_forkexec(ac, av);
204 : }
205 : #endif /* EXEC_BACKEND */
206 :
207 :
208 : /*
209 : * PgArchiverMain
210 : *
211 : * The argc/argv parameters are valid only in EXEC_BACKEND case. However,
212 : * since we don't use 'em, it hardly matters...
213 : */
214 : NON_EXEC_STATIC void
215 0 : PgArchiverMain(int argc, char *argv[])
216 : {
217 : /*
218 : * Ignore all signals usually bound to some action in the postmaster,
219 : * except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT.
220 : */
221 0 : pqsignal(SIGHUP, ArchSigHupHandler);
222 0 : pqsignal(SIGINT, SIG_IGN);
223 0 : pqsignal(SIGTERM, ArchSigTermHandler);
224 0 : pqsignal(SIGQUIT, pgarch_exit);
225 0 : pqsignal(SIGALRM, SIG_IGN);
226 0 : pqsignal(SIGPIPE, SIG_IGN);
227 0 : pqsignal(SIGUSR1, pgarch_waken);
228 0 : pqsignal(SIGUSR2, pgarch_waken_stop);
229 0 : pqsignal(SIGCHLD, SIG_DFL);
230 0 : pqsignal(SIGTTIN, SIG_DFL);
231 0 : pqsignal(SIGTTOU, SIG_DFL);
232 0 : pqsignal(SIGCONT, SIG_DFL);
233 0 : pqsignal(SIGWINCH, SIG_DFL);
234 0 : PG_SETMASK(&UnBlockSig);
235 :
236 : /*
237 : * Identify myself via ps
238 : */
239 0 : init_ps_display("archiver process", "", "", "");
240 :
241 0 : pgarch_MainLoop();
242 :
243 0 : exit(0);
244 : }
245 :
246 : /* SIGQUIT signal handler for archiver process */
247 : static void
248 0 : pgarch_exit(SIGNAL_ARGS)
249 : {
250 : /* SIGQUIT means curl up and die ... */
251 0 : exit(1);
252 : }
253 :
254 : /* SIGHUP signal handler for archiver process */
255 : static void
256 0 : ArchSigHupHandler(SIGNAL_ARGS)
257 : {
258 0 : int save_errno = errno;
259 :
260 : /* set flag to re-read config file at next convenient time */
261 0 : got_SIGHUP = true;
262 0 : SetLatch(MyLatch);
263 :
264 0 : errno = save_errno;
265 0 : }
266 :
267 : /* SIGTERM signal handler for archiver process */
268 : static void
269 0 : ArchSigTermHandler(SIGNAL_ARGS)
270 : {
271 0 : int save_errno = errno;
272 :
273 : /*
274 : * The postmaster never sends us SIGTERM, so we assume that this means
275 : * that init is trying to shut down the whole system. If we hang around
276 : * too long we'll get SIGKILL'd. Set flag to prevent starting any more
277 : * archive commands.
278 : */
279 0 : got_SIGTERM = true;
280 0 : SetLatch(MyLatch);
281 :
282 0 : errno = save_errno;
283 0 : }
284 :
285 : /* SIGUSR1 signal handler for archiver process */
286 : static void
287 0 : pgarch_waken(SIGNAL_ARGS)
288 : {
289 0 : int save_errno = errno;
290 :
291 : /* set flag that there is work to be done */
292 0 : wakened = true;
293 0 : SetLatch(MyLatch);
294 :
295 0 : errno = save_errno;
296 0 : }
297 :
298 : /* SIGUSR2 signal handler for archiver process */
299 : static void
300 0 : pgarch_waken_stop(SIGNAL_ARGS)
301 : {
302 0 : int save_errno = errno;
303 :
304 : /* set flag to do a final cycle and shut down afterwards */
305 0 : ready_to_stop = true;
306 0 : SetLatch(MyLatch);
307 :
308 0 : errno = save_errno;
309 0 : }
310 :
311 : /*
312 : * pgarch_MainLoop
313 : *
314 : * Main loop for archiver
315 : */
316 : static void
317 0 : pgarch_MainLoop(void)
318 : {
319 0 : pg_time_t last_copy_time = 0;
320 : bool time_to_stop;
321 :
322 : /*
323 : * We run the copy loop immediately upon entry, in case there are
324 : * unarchived files left over from a previous database run (or maybe the
325 : * archiver died unexpectedly). After that we wait for a signal or
326 : * timeout before doing more.
327 : */
328 0 : wakened = true;
329 :
330 : /*
331 : * There shouldn't be anything for the archiver to do except to wait for a
332 : * signal ... however, the archiver exists to protect our data, so she
333 : * wakes up occasionally to allow herself to be proactive.
334 : */
335 : do
336 : {
337 0 : ResetLatch(MyLatch);
338 :
339 : /* When we get SIGUSR2, we do one more archive cycle, then exit */
340 0 : time_to_stop = ready_to_stop;
341 :
342 : /* Check for config update */
343 0 : if (got_SIGHUP)
344 : {
345 0 : got_SIGHUP = false;
346 0 : ProcessConfigFile(PGC_SIGHUP);
347 : }
348 :
349 : /*
350 : * If we've gotten SIGTERM, we normally just sit and do nothing until
351 : * SIGUSR2 arrives. However, that means a random SIGTERM would
352 : * disable archiving indefinitely, which doesn't seem like a good
353 : * idea. If more than 60 seconds pass since SIGTERM, exit anyway, so
354 : * that the postmaster can start a new archiver if needed.
355 : */
356 0 : if (got_SIGTERM)
357 : {
358 0 : time_t curtime = time(NULL);
359 :
360 0 : if (last_sigterm_time == 0)
361 0 : last_sigterm_time = curtime;
362 0 : else if ((unsigned int) (curtime - last_sigterm_time) >=
363 : (unsigned int) 60)
364 0 : break;
365 : }
366 :
367 : /* Do what we're here for */
368 0 : if (wakened || time_to_stop)
369 : {
370 0 : wakened = false;
371 0 : pgarch_ArchiverCopyLoop();
372 0 : last_copy_time = time(NULL);
373 : }
374 :
375 : /*
376 : * Sleep until a signal is received, or until a poll is forced by
377 : * PGARCH_AUTOWAKE_INTERVAL having passed since last_copy_time, or
378 : * until postmaster dies.
379 : */
380 0 : if (!time_to_stop) /* Don't wait during last iteration */
381 : {
382 0 : pg_time_t curtime = (pg_time_t) time(NULL);
383 : int timeout;
384 :
385 0 : timeout = PGARCH_AUTOWAKE_INTERVAL - (curtime - last_copy_time);
386 0 : if (timeout > 0)
387 : {
388 : int rc;
389 :
390 0 : rc = WaitLatch(MyLatch,
391 : WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
392 : timeout * 1000L,
393 : WAIT_EVENT_ARCHIVER_MAIN);
394 0 : if (rc & WL_TIMEOUT)
395 0 : wakened = true;
396 : }
397 : else
398 0 : wakened = true;
399 : }
400 :
401 : /*
402 : * The archiver quits either when the postmaster dies (not expected)
403 : * or after completing one more archiving cycle after receiving
404 : * SIGUSR2.
405 : */
406 0 : } while (PostmasterIsAlive() && !time_to_stop);
407 0 : }
408 :
409 : /*
410 : * pgarch_ArchiverCopyLoop
411 : *
412 : * Archives all outstanding xlogs then returns
413 : */
414 : static void
415 0 : pgarch_ArchiverCopyLoop(void)
416 : {
417 : char xlog[MAX_XFN_CHARS + 1];
418 :
419 : /*
420 : * loop through all xlogs with archive_status of .ready and archive
421 : * them...mostly we expect this to be a single file, though it is possible
422 : * some backend will add files onto the list of those that need archiving
423 : * while we are still copying earlier archives
424 : */
425 0 : while (pgarch_readyXlog(xlog))
426 : {
427 0 : int failures = 0;
428 :
429 : for (;;)
430 : {
431 : /*
432 : * Do not initiate any more archive commands after receiving
433 : * SIGTERM, nor after the postmaster has died unexpectedly. The
434 : * first condition is to try to keep from having init SIGKILL the
435 : * command, and the second is to avoid conflicts with another
436 : * archiver spawned by a newer postmaster.
437 : */
438 0 : if (got_SIGTERM || !PostmasterIsAlive())
439 0 : return;
440 :
441 : /*
442 : * Check for config update. This is so that we'll adopt a new
443 : * setting for archive_command as soon as possible, even if there
444 : * is a backlog of files to be archived.
445 : */
446 0 : if (got_SIGHUP)
447 : {
448 0 : got_SIGHUP = false;
449 0 : ProcessConfigFile(PGC_SIGHUP);
450 : }
451 :
452 : /* can't do anything if no command ... */
453 0 : if (!XLogArchiveCommandSet())
454 : {
455 0 : ereport(WARNING,
456 : (errmsg("archive_mode enabled, yet archive_command is not set")));
457 0 : return;
458 : }
459 :
460 0 : if (pgarch_archiveXlog(xlog))
461 : {
462 : /* successful */
463 0 : pgarch_archiveDone(xlog);
464 :
465 : /*
466 : * Tell the collector about the WAL file that we successfully
467 : * archived
468 : */
469 0 : pgstat_send_archiver(xlog, false);
470 :
471 0 : break; /* out of inner retry loop */
472 : }
473 : else
474 : {
475 : /*
476 : * Tell the collector about the WAL file that we failed to
477 : * archive
478 : */
479 0 : pgstat_send_archiver(xlog, true);
480 :
481 0 : if (++failures >= NUM_ARCHIVE_RETRIES)
482 : {
483 0 : ereport(WARNING,
484 : (errmsg("archiving write-ahead log file \"%s\" failed too many times, will try again later",
485 : xlog)));
486 0 : return; /* give up archiving for now */
487 : }
488 0 : pg_usleep(1000000L); /* wait a bit before retrying */
489 : }
490 0 : }
491 : }
492 : }
493 :
494 : /*
495 : * pgarch_archiveXlog
496 : *
497 : * Invokes system(3) to copy one archive file to wherever it should go
498 : *
499 : * Returns true if successful
500 : */
501 : static bool
502 0 : pgarch_archiveXlog(char *xlog)
503 : {
504 : char xlogarchcmd[MAXPGPATH];
505 : char pathname[MAXPGPATH];
506 : char activitymsg[MAXFNAMELEN + 16];
507 : char *dp;
508 : char *endp;
509 : const char *sp;
510 : int rc;
511 :
512 0 : snprintf(pathname, MAXPGPATH, XLOGDIR "/%s", xlog);
513 :
514 : /*
515 : * construct the command to be executed
516 : */
517 0 : dp = xlogarchcmd;
518 0 : endp = xlogarchcmd + MAXPGPATH - 1;
519 0 : *endp = '\0';
520 :
521 0 : for (sp = XLogArchiveCommand; *sp; sp++)
522 : {
523 0 : if (*sp == '%')
524 : {
525 0 : switch (sp[1])
526 : {
527 : case 'p':
528 : /* %p: relative path of source file */
529 0 : sp++;
530 0 : strlcpy(dp, pathname, endp - dp);
531 0 : make_native_path(dp);
532 0 : dp += strlen(dp);
533 0 : break;
534 : case 'f':
535 : /* %f: filename of source file */
536 0 : sp++;
537 0 : strlcpy(dp, xlog, endp - dp);
538 0 : dp += strlen(dp);
539 0 : break;
540 : case '%':
541 : /* convert %% to a single % */
542 0 : sp++;
543 0 : if (dp < endp)
544 0 : *dp++ = *sp;
545 0 : break;
546 : default:
547 : /* otherwise treat the % as not special */
548 0 : if (dp < endp)
549 0 : *dp++ = *sp;
550 0 : break;
551 : }
552 : }
553 : else
554 : {
555 0 : if (dp < endp)
556 0 : *dp++ = *sp;
557 : }
558 : }
559 0 : *dp = '\0';
560 :
561 0 : ereport(DEBUG3,
562 : (errmsg_internal("executing archive command \"%s\"",
563 : xlogarchcmd)));
564 :
565 : /* Report archive activity in PS display */
566 0 : snprintf(activitymsg, sizeof(activitymsg), "archiving %s", xlog);
567 0 : set_ps_display(activitymsg, false);
568 :
569 0 : rc = system(xlogarchcmd);
570 0 : if (rc != 0)
571 : {
572 : /*
573 : * If either the shell itself, or a called command, died on a signal,
574 : * abort the archiver. We do this because system() ignores SIGINT and
575 : * SIGQUIT while waiting; so a signal is very likely something that
576 : * should have interrupted us too. If we overreact it's no big deal,
577 : * the postmaster will just start the archiver again.
578 : *
579 : * Per the Single Unix Spec, shells report exit status > 128 when a
580 : * called command died on a signal.
581 : */
582 0 : int lev = (WIFSIGNALED(rc) || WEXITSTATUS(rc) > 128) ? FATAL : LOG;
583 :
584 0 : if (WIFEXITED(rc))
585 : {
586 0 : ereport(lev,
587 : (errmsg("archive command failed with exit code %d",
588 : WEXITSTATUS(rc)),
589 : errdetail("The failed archive command was: %s",
590 : xlogarchcmd)));
591 : }
592 0 : else if (WIFSIGNALED(rc))
593 : {
594 : #if defined(WIN32)
595 : ereport(lev,
596 : (errmsg("archive command was terminated by exception 0x%X",
597 : WTERMSIG(rc)),
598 : errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."),
599 : errdetail("The failed archive command was: %s",
600 : xlogarchcmd)));
601 : #elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
602 0 : ereport(lev,
603 : (errmsg("archive command was terminated by signal %d: %s",
604 : WTERMSIG(rc),
605 : WTERMSIG(rc) < NSIG ? sys_siglist[WTERMSIG(rc)] : "(unknown)"),
606 : errdetail("The failed archive command was: %s",
607 : xlogarchcmd)));
608 : #else
609 : ereport(lev,
610 : (errmsg("archive command was terminated by signal %d",
611 : WTERMSIG(rc)),
612 : errdetail("The failed archive command was: %s",
613 : xlogarchcmd)));
614 : #endif
615 : }
616 : else
617 : {
618 0 : ereport(lev,
619 : (errmsg("archive command exited with unrecognized status %d",
620 : rc),
621 : errdetail("The failed archive command was: %s",
622 : xlogarchcmd)));
623 : }
624 :
625 0 : snprintf(activitymsg, sizeof(activitymsg), "failed on %s", xlog);
626 0 : set_ps_display(activitymsg, false);
627 :
628 0 : return false;
629 : }
630 0 : elog(DEBUG1, "archived write-ahead log file \"%s\"", xlog);
631 :
632 0 : snprintf(activitymsg, sizeof(activitymsg), "last was %s", xlog);
633 0 : set_ps_display(activitymsg, false);
634 :
635 0 : return true;
636 : }
637 :
638 : /*
639 : * pgarch_readyXlog
640 : *
641 : * Return name of the oldest xlog file that has not yet been archived.
642 : * No notification is set that file archiving is now in progress, so
643 : * this would need to be extended if multiple concurrent archival
644 : * tasks were created. If a failure occurs, we will completely
645 : * re-copy the file at the next available opportunity.
646 : *
647 : * It is important that we return the oldest, so that we archive xlogs
648 : * in order that they were written, for two reasons:
649 : * 1) to maintain the sequential chain of xlogs required for recovery
650 : * 2) because the oldest ones will sooner become candidates for
651 : * recycling at time of checkpoint
652 : *
653 : * NOTE: the "oldest" comparison will presently consider all segments of
654 : * a timeline with a smaller ID to be older than all segments of a timeline
655 : * with a larger ID; the net result being that past timelines are given
656 : * higher priority for archiving. This seems okay, or at least not
657 : * obviously worth changing.
658 : */
659 : static bool
660 0 : pgarch_readyXlog(char *xlog)
661 : {
662 : /*
663 : * open xlog status directory and read through list of xlogs that have the
664 : * .ready suffix, looking for earliest file. It is possible to optimise
665 : * this code, though only a single file is expected on the vast majority
666 : * of calls, so....
667 : */
668 : char XLogArchiveStatusDir[MAXPGPATH];
669 : char newxlog[MAX_XFN_CHARS + 6 + 1];
670 : DIR *rldir;
671 : struct dirent *rlde;
672 0 : bool found = false;
673 :
674 0 : snprintf(XLogArchiveStatusDir, MAXPGPATH, XLOGDIR "/archive_status");
675 0 : rldir = AllocateDir(XLogArchiveStatusDir);
676 0 : if (rldir == NULL)
677 0 : ereport(ERROR,
678 : (errcode_for_file_access(),
679 : errmsg("could not open archive status directory \"%s\": %m",
680 : XLogArchiveStatusDir)));
681 :
682 0 : while ((rlde = ReadDir(rldir, XLogArchiveStatusDir)) != NULL)
683 : {
684 0 : int basenamelen = (int) strlen(rlde->d_name) - 6;
685 :
686 0 : if (basenamelen >= MIN_XFN_CHARS &&
687 0 : basenamelen <= MAX_XFN_CHARS &&
688 0 : strspn(rlde->d_name, VALID_XFN_CHARS) >= basenamelen &&
689 0 : strcmp(rlde->d_name + basenamelen, ".ready") == 0)
690 : {
691 0 : if (!found)
692 : {
693 0 : strcpy(newxlog, rlde->d_name);
694 0 : found = true;
695 : }
696 : else
697 : {
698 0 : if (strcmp(rlde->d_name, newxlog) < 0)
699 0 : strcpy(newxlog, rlde->d_name);
700 : }
701 : }
702 : }
703 0 : FreeDir(rldir);
704 :
705 0 : if (found)
706 : {
707 : /* truncate off the .ready */
708 0 : newxlog[strlen(newxlog) - 6] = '\0';
709 0 : strcpy(xlog, newxlog);
710 : }
711 0 : return found;
712 : }
713 :
714 : /*
715 : * pgarch_archiveDone
716 : *
717 : * Emit notification that an xlog file has been successfully archived.
718 : * We do this by renaming the status file from NNN.ready to NNN.done.
719 : * Eventually, a checkpoint process will notice this and delete both the
720 : * NNN.done file and the xlog file itself.
721 : */
722 : static void
723 0 : pgarch_archiveDone(char *xlog)
724 : {
725 : char rlogready[MAXPGPATH];
726 : char rlogdone[MAXPGPATH];
727 :
728 0 : StatusFilePath(rlogready, xlog, ".ready");
729 0 : StatusFilePath(rlogdone, xlog, ".done");
730 0 : (void) durable_rename(rlogready, rlogdone, WARNING);
731 0 : }
|