#include "postgres.h"
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <sys/wait.h>
#include <unistd.h>
#include "access/xlog.h"
#include "access/xlog_internal.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/fork_process.h"
#include "postmaster/pgarch.h"
#include "postmaster/postmaster.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
#include "utils/guc.h"
#include "utils/ps_status.h"
Go to the source code of this file.
Defines | |
#define | PGARCH_AUTOWAKE_INTERVAL 60 |
#define | PGARCH_RESTART_INTERVAL 10 |
#define | MIN_XFN_CHARS 16 |
#define | MAX_XFN_CHARS 40 |
#define | VALID_XFN_CHARS "0123456789ABCDEF.history.backup" |
#define | NUM_ARCHIVE_RETRIES 3 |
Functions | |
NON_EXEC_STATIC void | PgArchiverMain (int argc, char *argv[]) __attribute__((noreturn)) |
static void | pgarch_exit (SIGNAL_ARGS) |
static void | ArchSigHupHandler (SIGNAL_ARGS) |
static void | ArchSigTermHandler (SIGNAL_ARGS) |
static void | pgarch_waken (SIGNAL_ARGS) |
static void | pgarch_waken_stop (SIGNAL_ARGS) |
static void | pgarch_MainLoop (void) |
static void | pgarch_ArchiverCopyLoop (void) |
static bool | pgarch_archiveXlog (char *xlog) |
static bool | pgarch_readyXlog (char *xlog) |
static void | pgarch_archiveDone (char *xlog) |
int | pgarch_start (void) |
Variables | |
static time_t | last_pgarch_start_time |
static time_t | last_sigterm_time = 0 |
static volatile sig_atomic_t | got_SIGHUP = false |
static volatile sig_atomic_t | got_SIGTERM = false |
static volatile sig_atomic_t | wakened = false |
static volatile sig_atomic_t | ready_to_stop = false |
static Latch | mainloop_latch |
#define MAX_XFN_CHARS 40 |
Definition at line 71 of file pgarch.c.
Referenced by pgarch_ArchiverCopyLoop(), and pgarch_readyXlog().
#define MIN_XFN_CHARS 16 |
Definition at line 70 of file pgarch.c.
Referenced by pgarch_readyXlog().
#define NUM_ARCHIVE_RETRIES 3 |
Definition at line 74 of file pgarch.c.
Referenced by pgarch_ArchiverCopyLoop().
#define PGARCH_AUTOWAKE_INTERVAL 60 |
Definition at line 55 of file pgarch.c.
Referenced by pgarch_MainLoop().
#define PGARCH_RESTART_INTERVAL 10 |
Definition at line 58 of file pgarch.c.
Referenced by pgarch_start().
#define VALID_XFN_CHARS "0123456789ABCDEF.history.backup" |
Definition at line 72 of file pgarch.c.
Referenced by pgarch_readyXlog().
static void ArchSigHupHandler | ( | SIGNAL_ARGS | ) | [static] |
Definition at line 292 of file pgarch.c.
References got_SIGHUP, and SetLatch().
Referenced by PgArchiverMain().
{ int save_errno = errno; /* set flag to re-read config file at next convenient time */ got_SIGHUP = true; SetLatch(&mainloop_latch); errno = save_errno; }
static void ArchSigTermHandler | ( | SIGNAL_ARGS | ) | [static] |
Definition at line 305 of file pgarch.c.
References got_SIGTERM, and SetLatch().
Referenced by PgArchiverMain().
{ int save_errno = errno; /* * The postmaster never sends us SIGTERM, so we assume that this means * that init is trying to shut down the whole system. If we hang around * too long we'll get SIGKILL'd. Set flag to prevent starting any more * archive commands. */ got_SIGTERM = true; SetLatch(&mainloop_latch); errno = save_errno; }
static void pgarch_archiveDone | ( | char * | xlog | ) | [static] |
Definition at line 746 of file pgarch.c.
References ereport, errcode_for_file_access(), errmsg(), StatusFilePath, and WARNING.
Referenced by pgarch_ArchiverCopyLoop().
{ char rlogready[MAXPGPATH]; char rlogdone[MAXPGPATH]; StatusFilePath(rlogready, xlog, ".ready"); StatusFilePath(rlogdone, xlog, ".done"); if (rename(rlogready, rlogdone) < 0) ereport(WARNING, (errcode_for_file_access(), errmsg("could not rename file \"%s\" to \"%s\": %m", rlogready, rlogdone))); }
static void pgarch_ArchiverCopyLoop | ( | void | ) | [static] |
Definition at line 450 of file pgarch.c.
References ereport, errmsg(), got_SIGHUP, got_SIGTERM, MAX_XFN_CHARS, NUM_ARCHIVE_RETRIES, pg_usleep(), pgarch_archiveDone(), pgarch_archiveXlog(), pgarch_readyXlog(), PGC_SIGHUP, PostmasterIsAlive(), ProcessConfigFile(), WARNING, and XLogArchiveCommandSet.
Referenced by pgarch_MainLoop().
{ char xlog[MAX_XFN_CHARS + 1]; /* * loop through all xlogs with archive_status of .ready and archive * them...mostly we expect this to be a single file, though it is possible * some backend will add files onto the list of those that need archiving * while we are still copying earlier archives */ while (pgarch_readyXlog(xlog)) { int failures = 0; for (;;) { /* * Do not initiate any more archive commands after receiving * SIGTERM, nor after the postmaster has died unexpectedly. The * first condition is to try to keep from having init SIGKILL the * command, and the second is to avoid conflicts with another * archiver spawned by a newer postmaster. */ if (got_SIGTERM || !PostmasterIsAlive()) return; /* * Check for config update. This is so that we'll adopt a new * setting for archive_command as soon as possible, even if there * is a backlog of files to be archived. */ if (got_SIGHUP) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); } /* can't do anything if no command ... */ if (!XLogArchiveCommandSet()) { ereport(WARNING, (errmsg("archive_mode enabled, yet archive_command is not set"))); return; } if (pgarch_archiveXlog(xlog)) { /* successful */ pgarch_archiveDone(xlog); break; /* out of inner retry loop */ } else { if (++failures >= NUM_ARCHIVE_RETRIES) { ereport(WARNING, (errmsg("archiving transaction log file \"%s\" failed too many times, will try again later", xlog))); return; /* give up archiving for now */ } pg_usleep(1000000L); /* wait a bit before retrying */ } } } }
static bool pgarch_archiveXlog | ( | char * | xlog | ) | [static] |
Definition at line 524 of file pgarch.c.
References DEBUG1, DEBUG3, ereport, errdetail(), errhint(), errmsg(), errmsg_internal(), FATAL, LOG, make_native_path(), MAXFNAMELEN, MAXPGPATH, set_ps_display(), snprintf(), strlcpy(), system(), WEXITSTATUS, WIFEXITED, WIFSIGNALED, WTERMSIG, XLogArchiveCommand, and XLOGDIR.
Referenced by pgarch_ArchiverCopyLoop().
{ char xlogarchcmd[MAXPGPATH]; char pathname[MAXPGPATH]; char activitymsg[MAXFNAMELEN + 16]; char *dp; char *endp; const char *sp; int rc; snprintf(pathname, MAXPGPATH, XLOGDIR "/%s", xlog); /* * construct the command to be executed */ dp = xlogarchcmd; endp = xlogarchcmd + MAXPGPATH - 1; *endp = '\0'; for (sp = XLogArchiveCommand; *sp; sp++) { if (*sp == '%') { switch (sp[1]) { case 'p': /* %p: relative path of source file */ sp++; strlcpy(dp, pathname, endp - dp); make_native_path(dp); dp += strlen(dp); break; case 'f': /* %f: filename of source file */ sp++; strlcpy(dp, xlog, 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 archive command \"%s\"", xlogarchcmd))); /* Report archive activity in PS display */ snprintf(activitymsg, sizeof(activitymsg), "archiving %s", xlog); set_ps_display(activitymsg, false); rc = system(xlogarchcmd); if (rc != 0) { /* * If either the shell itself, or a called command, died on a signal, * abort the archiver. We do this because system() ignores SIGINT and * SIGQUIT while waiting; so a signal is very likely something that * should have interrupted us too. If we overreact it's no big deal, * the postmaster will just start the archiver again. * * Per the Single Unix Spec, shells report exit status > 128 when a * called command died on a signal. */ int lev = (WIFSIGNALED(rc) || WEXITSTATUS(rc) > 128) ? FATAL : LOG; if (WIFEXITED(rc)) { ereport(lev, (errmsg("archive command failed with exit code %d", WEXITSTATUS(rc)), errdetail("The failed archive command was: %s", xlogarchcmd))); } else if (WIFSIGNALED(rc)) { #if defined(WIN32) ereport(lev, (errmsg("archive command was terminated by exception 0x%X", WTERMSIG(rc)), errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."), errdetail("The failed archive command was: %s", xlogarchcmd))); #elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST ereport(lev, (errmsg("archive command was terminated by signal %d: %s", WTERMSIG(rc), WTERMSIG(rc) < NSIG ? sys_siglist[WTERMSIG(rc)] : "(unknown)"), errdetail("The failed archive command was: %s", xlogarchcmd))); #else ereport(lev, (errmsg("archive command was terminated by signal %d", WTERMSIG(rc)), errdetail("The failed archive command was: %s", xlogarchcmd))); #endif } else { ereport(lev, (errmsg("archive command exited with unrecognized status %d", rc), errdetail("The failed archive command was: %s", xlogarchcmd))); } snprintf(activitymsg, sizeof(activitymsg), "failed on %s", xlog); set_ps_display(activitymsg, false); return false; } ereport(DEBUG1, (errmsg("archived transaction log file \"%s\"", xlog))); snprintf(activitymsg, sizeof(activitymsg), "last was %s", xlog); set_ps_display(activitymsg, false); return true; }
static void pgarch_exit | ( | SIGNAL_ARGS | ) | [static] |
Definition at line 284 of file pgarch.c.
Referenced by PgArchiverMain().
{
/* SIGQUIT means curl up and die ... */
exit(1);
}
static void pgarch_MainLoop | ( | void | ) | [static] |
Definition at line 353 of file pgarch.c.
References got_SIGHUP, got_SIGTERM, last_sigterm_time, NULL, pgarch_ArchiverCopyLoop(), PGARCH_AUTOWAKE_INTERVAL, PGC_SIGHUP, PostmasterIsAlive(), ProcessConfigFile(), ready_to_stop, ResetLatch(), WaitLatch(), wakened, WL_LATCH_SET, WL_POSTMASTER_DEATH, and WL_TIMEOUT.
Referenced by PgArchiverMain().
{ pg_time_t last_copy_time = 0; bool time_to_stop; /* * We run the copy loop immediately upon entry, in case there are * unarchived files left over from a previous database run (or maybe the * archiver died unexpectedly). After that we wait for a signal or * timeout before doing more. */ wakened = true; /* * There shouldn't be anything for the archiver to do except to wait for a * signal ... however, the archiver exists to protect our data, so she * wakes up occasionally to allow herself to be proactive. */ do { ResetLatch(&mainloop_latch); /* When we get SIGUSR2, we do one more archive cycle, then exit */ time_to_stop = ready_to_stop; /* Check for config update */ if (got_SIGHUP) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); } /* * If we've gotten SIGTERM, we normally just sit and do nothing until * SIGUSR2 arrives. However, that means a random SIGTERM would * disable archiving indefinitely, which doesn't seem like a good * idea. If more than 60 seconds pass since SIGTERM, exit anyway, so * that the postmaster can start a new archiver if needed. */ if (got_SIGTERM) { time_t curtime = time(NULL); if (last_sigterm_time == 0) last_sigterm_time = curtime; else if ((unsigned int) (curtime - last_sigterm_time) >= (unsigned int) 60) break; } /* Do what we're here for */ if (wakened || time_to_stop) { wakened = false; pgarch_ArchiverCopyLoop(); last_copy_time = time(NULL); } /* * Sleep until a signal is received, or until a poll is forced by * PGARCH_AUTOWAKE_INTERVAL having passed since last_copy_time, or * until postmaster dies. */ if (!time_to_stop) /* Don't wait during last iteration */ { pg_time_t curtime = (pg_time_t) time(NULL); int timeout; timeout = PGARCH_AUTOWAKE_INTERVAL - (curtime - last_copy_time); if (timeout > 0) { int rc; rc = WaitLatch(&mainloop_latch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, timeout * 1000L); if (rc & WL_TIMEOUT) wakened = true; } else wakened = true; } /* * The archiver quits either when the postmaster dies (not expected) * or after completing one more archiving cycle after receiving * SIGUSR2. */ } while (PostmasterIsAlive() && !time_to_stop); }
static bool pgarch_readyXlog | ( | char * | xlog | ) | [static] |
Definition at line 683 of file pgarch.c.
References AllocateDir(), dirent::d_name, ereport, errcode_for_file_access(), errmsg(), ERROR, FreeDir(), MAX_XFN_CHARS, MAXPGPATH, MIN_XFN_CHARS, NULL, ReadDir(), snprintf(), VALID_XFN_CHARS, and XLOGDIR.
Referenced by pgarch_ArchiverCopyLoop().
{ /* * open xlog status directory and read through list of xlogs that have the * .ready suffix, looking for earliest file. It is possible to optimise * this code, though only a single file is expected on the vast majority * of calls, so.... */ char XLogArchiveStatusDir[MAXPGPATH]; char newxlog[MAX_XFN_CHARS + 6 + 1]; DIR *rldir; struct dirent *rlde; bool found = false; snprintf(XLogArchiveStatusDir, MAXPGPATH, XLOGDIR "/archive_status"); rldir = AllocateDir(XLogArchiveStatusDir); if (rldir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open archive status directory \"%s\": %m", XLogArchiveStatusDir))); while ((rlde = ReadDir(rldir, XLogArchiveStatusDir)) != NULL) { int basenamelen = (int) strlen(rlde->d_name) - 6; if (basenamelen >= MIN_XFN_CHARS && basenamelen <= MAX_XFN_CHARS && strspn(rlde->d_name, VALID_XFN_CHARS) >= basenamelen && strcmp(rlde->d_name + basenamelen, ".ready") == 0) { if (!found) { strcpy(newxlog, rlde->d_name); found = true; } else { if (strcmp(rlde->d_name, newxlog) < 0) strcpy(newxlog, rlde->d_name); } } } FreeDir(rldir); if (found) { /* truncate off the .ready */ newxlog[strlen(newxlog) - 6] = '\0'; strcpy(xlog, newxlog); } return found; }
int pgarch_start | ( | void | ) |
Definition at line 134 of file pgarch.c.
References ClosePostmasterPorts(), ereport, errmsg(), fork_process(), last_pgarch_start_time, LOG, NULL, on_exit_reset(), PGARCH_RESTART_INTERVAL, PgArchiverMain(), PGSharedMemoryDetach(), and XLogArchivingActive.
Referenced by reaper(), and ServerLoop().
{ time_t curtime; pid_t pgArchPid; /* * Do nothing if no archiver needed */ if (!XLogArchivingActive()) return 0; /* * Do nothing if too soon since last archiver start. This is a safety * valve to protect against continuous respawn attempts if the archiver is * dying immediately at launch. Note that since we will be re-called from * the postmaster main loop, we will get another chance later. */ curtime = time(NULL); if ((unsigned int) (curtime - last_pgarch_start_time) < (unsigned int) PGARCH_RESTART_INTERVAL) return 0; last_pgarch_start_time = curtime; #ifdef EXEC_BACKEND switch ((pgArchPid = pgarch_forkexec())) #else switch ((pgArchPid = fork_process())) #endif { case -1: ereport(LOG, (errmsg("could not fork archiver: %m"))); return 0; #ifndef EXEC_BACKEND case 0: /* in postmaster child ... */ /* Close the postmaster's sockets */ ClosePostmasterPorts(false); /* Lose the postmaster's on-exit routines */ on_exit_reset(); /* Drop our connection to postmaster's shared memory, as well */ PGSharedMemoryDetach(); PgArchiverMain(0, NULL); break; #endif default: return (int) pgArchPid; } /* shouldn't get here */ return 0; }
static void pgarch_waken | ( | SIGNAL_ARGS | ) | [static] |
Definition at line 323 of file pgarch.c.
References SetLatch(), and wakened.
Referenced by PgArchiverMain().
{ int save_errno = errno; /* set flag that there is work to be done */ wakened = true; SetLatch(&mainloop_latch); errno = save_errno; }
static void pgarch_waken_stop | ( | SIGNAL_ARGS | ) | [static] |
Definition at line 336 of file pgarch.c.
References ready_to_stop, and SetLatch().
Referenced by PgArchiverMain().
{ int save_errno = errno; /* set flag to do a final cycle and shut down afterwards */ ready_to_stop = true; SetLatch(&mainloop_latch); errno = save_errno; }
NON_EXEC_STATIC void PgArchiverMain | ( | int | argc, | |
char * | argv[] | |||
) |
Definition at line 232 of file pgarch.c.
References ArchSigHupHandler(), ArchSigTermHandler(), elog, FATAL, init_ps_display(), InitializeLatchSupport(), InitLatch(), IsUnderPostmaster, MyProcPid, MyStartTime, NULL, PG_SETMASK, pgarch_exit(), pgarch_MainLoop(), pgarch_waken(), pgarch_waken_stop(), pqsignal(), SIG_DFL, SIG_IGN, SIGALRM, SIGCHLD, SIGCONT, SIGHUP, SIGPIPE, SIGQUIT, SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2, SIGWINCH, and UnBlockSig.
Referenced by pgarch_start().
{ IsUnderPostmaster = true; /* we are a postmaster subprocess now */ MyProcPid = getpid(); /* reset MyProcPid */ MyStartTime = time(NULL); /* record Start Time for logging */ /* * If possible, make this process a group leader, so that the postmaster * can signal any child processes too. */ #ifdef HAVE_SETSID if (setsid() < 0) elog(FATAL, "setsid() failed: %m"); #endif InitializeLatchSupport(); /* needed for latch waits */ InitLatch(&mainloop_latch); /* initialize latch used in main loop */ /* * Ignore all signals usually bound to some action in the postmaster, * except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT. */ pqsignal(SIGHUP, ArchSigHupHandler); pqsignal(SIGINT, SIG_IGN); pqsignal(SIGTERM, ArchSigTermHandler); pqsignal(SIGQUIT, pgarch_exit); pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, pgarch_waken); pqsignal(SIGUSR2, pgarch_waken_stop); pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); PG_SETMASK(&UnBlockSig); /* * Identify myself via ps */ init_ps_display("archiver process", "", "", ""); pgarch_MainLoop(); exit(0); }
volatile sig_atomic_t got_SIGHUP = false [static] |
Definition at line 87 of file pgarch.c.
Referenced by ArchSigHupHandler(), pgarch_ArchiverCopyLoop(), and pgarch_MainLoop().
volatile sig_atomic_t got_SIGTERM = false [static] |
Definition at line 88 of file pgarch.c.
Referenced by ArchSigTermHandler(), pgarch_ArchiverCopyLoop(), and pgarch_MainLoop().
time_t last_pgarch_start_time [static] |
Definition at line 81 of file pgarch.c.
Referenced by pgarch_start().
time_t last_sigterm_time = 0 [static] |
Definition at line 82 of file pgarch.c.
Referenced by pgarch_MainLoop().
Latch mainloop_latch [static] |
volatile sig_atomic_t ready_to_stop = false [static] |
Definition at line 90 of file pgarch.c.
Referenced by pgarch_MainLoop(), and pgarch_waken_stop().
volatile sig_atomic_t wakened = false [static] |
Definition at line 89 of file pgarch.c.
Referenced by pgarch_MainLoop(), and pgarch_waken().