From c6b448bed60ada06b9a341e59dac501b43ceafe1 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Wed, 20 Oct 2021 21:42:54 +0000 Subject: [PATCH v3 1/2] Move logic for archiving via shell to its own file. --- src/backend/access/transam/xlog.c | 2 +- src/backend/postmaster/Makefile | 1 + src/backend/postmaster/pgarch.c | 138 ++++----------------------------- src/backend/postmaster/shell_archive.c | 137 ++++++++++++++++++++++++++++++++ src/include/access/xlog.h | 11 ++- src/include/postmaster/pgarch.h | 6 ++ 6 files changed, 172 insertions(+), 123 deletions(-) create mode 100644 src/backend/postmaster/shell_archive.c diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 62862255fc..b0bb3d633e 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -8777,7 +8777,7 @@ ShutdownXLOG(int code, Datum arg) * process one more time at the end of shutdown). The checkpoint * record will go to the next XLOG file and won't be archived (yet). */ - if (XLogArchivingActive() && XLogArchiveCommandSet()) + if (XLogArchivingActive() && XLogArchivingConfigured()) RequestXLogSwitch(false); CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); diff --git a/src/backend/postmaster/Makefile b/src/backend/postmaster/Makefile index 787c6a2c3b..dbbeac5a82 100644 --- a/src/backend/postmaster/Makefile +++ b/src/backend/postmaster/Makefile @@ -23,6 +23,7 @@ OBJS = \ pgarch.o \ pgstat.o \ postmaster.o \ + shell_archive.o \ startup.o \ syslogger.o \ walwriter.o diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c index 74a7d7c4d0..69e23e286f 100644 --- a/src/backend/postmaster/pgarch.c +++ b/src/backend/postmaster/pgarch.c @@ -25,18 +25,12 @@ */ #include "postgres.h" -#include -#include -#include #include -#include -#include #include #include "access/xlog.h" #include "access/xlog_internal.h" #include "libpq/pqsignal.h" -#include "miscadmin.h" #include "pgstat.h" #include "postmaster/interrupt.h" #include "postmaster/pgarch.h" @@ -78,6 +72,12 @@ typedef struct PgArchData int pgprocno; /* pgprocno of archiver process */ } PgArchData; +/* + * PG_archive can be overwritten by modules to define custom archiving logic. + * By default, we use archive_command. + */ +PG_archive_t PG_archive = shell_archive; + /* ---------- * Local data @@ -358,11 +358,11 @@ pgarch_ArchiverCopyLoop(void) */ HandlePgArchInterrupts(); - /* can't do anything if no command ... */ - if (!XLogArchiveCommandSet()) + /* can't do anything if not configured ... */ + if (!XLogArchivingConfigured()) { ereport(WARNING, - (errmsg("archive_mode enabled, yet archive_command is not set"))); + (errmsg("archive_mode enabled, yet archiving is not configured"))); return; } @@ -443,136 +443,32 @@ pgarch_ArchiverCopyLoop(void) /* * pgarch_archiveXlog * - * Invokes system(3) to copy one archive file to wherever it should go + * Invokes PG_archive() to copy one archive file to wherever it should go * * Returns true if successful */ static bool pgarch_archiveXlog(char *xlog) { - 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); + bool ret; - /* - * construct the command to be executed - */ - dp = xlogarchcmd; - endp = xlogarchcmd + MAXPGPATH - 1; - *endp = '\0'; + Assert(PG_archive != NULL); - 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))); + snprintf(pathname, MAXPGPATH, XLOGDIR "/%s", xlog); /* Report archive activity in PS display */ snprintf(activitymsg, sizeof(activitymsg), "archiving %s", xlog); set_ps_display(activitymsg); - 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. Also die if the shell got a hard - * "command not found" type of error. If we overreact it's no big - * deal, the postmaster will just start the archiver again. - */ - int lev = wait_result_is_any_signal(rc, true) ? 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))); -#else - ereport(lev, - (errmsg("archive command was terminated by signal %d: %s", - WTERMSIG(rc), pg_strsignal(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))); - } - + if ((ret = (*PG_archive) (xlog, pathname))) + snprintf(activitymsg, sizeof(activitymsg), "last was %s", xlog); + else snprintf(activitymsg, sizeof(activitymsg), "failed on %s", xlog); - set_ps_display(activitymsg); - - return false; - } - elog(DEBUG1, "archived write-ahead log file \"%s\"", xlog); - - snprintf(activitymsg, sizeof(activitymsg), "last was %s", xlog); set_ps_display(activitymsg); - return true; + return ret; } /* diff --git a/src/backend/postmaster/shell_archive.c b/src/backend/postmaster/shell_archive.c new file mode 100644 index 0000000000..1ced9d11dc --- /dev/null +++ b/src/backend/postmaster/shell_archive.c @@ -0,0 +1,137 @@ +/*------------------------------------------------------------------------- + * + * shell_archive.c + * + * This archiving function uses a user-specified shell command (the + * archive_command GUC) to copy write-ahead log files. It is used as the + * default for PG_archive, but other modules may define their own custom + * archiving logic. + * + * Copyright (c) 2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/postmaster/shell_archive.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/xlog.h" +#include "postmaster/pgarch.h" + +bool +shell_archive(const char *file, const char *path) +{ + char xlogarchcmd[MAXPGPATH]; + char *dp; + char *endp; + const char *sp; + int rc; + + Assert(file != NULL); + Assert(path != NULL); + + /* + * 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, path, endp - dp); + make_native_path(dp); + dp += strlen(dp); + break; + case 'f': + /* %f: filename of source file */ + sp++; + strlcpy(dp, file, 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))); + + 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. Also die if the shell got a hard + * "command not found" type of error. If we overreact it's no big + * deal, the postmaster will just start the archiver again. + */ + int lev = wait_result_is_any_signal(rc, true) ? 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))); +#else + ereport(lev, + (errmsg("archive command was terminated by signal %d: %s", + WTERMSIG(rc), pg_strsignal(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))); + } + + return false; + } + + elog(DEBUG1, "archived write-ahead log file \"%s\"", file); + return true; +} diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 5e2c94a05f..f38e383600 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -18,6 +18,7 @@ #include "datatype/timestamp.h" #include "lib/stringinfo.h" #include "nodes/pg_list.h" +#include "postmaster/pgarch.h" #include "storage/fd.h" @@ -157,7 +158,15 @@ extern PGDLLIMPORT int wal_level; /* Is WAL archiving enabled always (even during recovery)? */ #define XLogArchivingAlways() \ (AssertMacro(XLogArchiveMode == ARCHIVE_MODE_OFF || wal_level >= WAL_LEVEL_REPLICA), XLogArchiveMode == ARCHIVE_MODE_ALWAYS) -#define XLogArchiveCommandSet() (XLogArchiveCommand[0] != '\0') + +/* + * Is WAL archiving configured? For consistency with previous releases, this + * checks that archive_command is set when archiving via shell is enabled. + * Otherwise, we just check that an archive function is set, and it is the + * responsibility of that archive function to ensure it is properly configured. + */ +#define XLogArchivingConfigured() \ + (PG_archive && (PG_archive != shell_archive || XLogArchiveCommand[0] != '\0')) /* * Is WAL-logging necessary for archival or log-shipping, or can we skip diff --git a/src/include/postmaster/pgarch.h b/src/include/postmaster/pgarch.h index 1e47a143e1..59fefb3458 100644 --- a/src/include/postmaster/pgarch.h +++ b/src/include/postmaster/pgarch.h @@ -32,4 +32,10 @@ extern bool PgArchCanRestart(void); extern void PgArchiverMain(void) pg_attribute_noreturn(); extern void PgArchWakeup(void); +typedef bool (*PG_archive_t) (const char *file, const char *path); +extern PGDLLIMPORT PG_archive_t PG_archive; + +/* in shell_archive.c */ +extern bool shell_archive(const char *file, const char *path); + #endif /* _PGARCH_H */ -- 2.16.6