diff options
Diffstat (limited to '')
-rw-r--r-- | src/backend/postmaster/shell_archive.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/backend/postmaster/shell_archive.c b/src/backend/postmaster/shell_archive.c new file mode 100644 index 0000000..6306f5b --- /dev/null +++ b/src/backend/postmaster/shell_archive.c @@ -0,0 +1,165 @@ +/*------------------------------------------------------------------------- + * + * 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, but other modules may define their own custom archiving logic. + * + * Copyright (c) 2022, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/postmaster/shell_archive.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include <sys/wait.h> + +#include "access/xlog.h" +#include "pgstat.h" +#include "postmaster/pgarch.h" + +static bool shell_archive_configured(void); +static bool shell_archive_file(const char *file, const char *path); +static void shell_archive_shutdown(void); + +void +shell_archive_init(ArchiveModuleCallbacks *cb) +{ + AssertVariableIsOfType(&shell_archive_init, ArchiveModuleInit); + + cb->check_configured_cb = shell_archive_configured; + cb->archive_file_cb = shell_archive_file; + cb->shutdown_cb = shell_archive_shutdown; +} + +static bool +shell_archive_configured(void) +{ + return XLogArchiveCommand[0] != '\0'; +} + +static bool +shell_archive_file(const char *file, const char *path) +{ + char xlogarchcmd[MAXPGPATH]; + char *dp; + char *endp; + const char *sp; + int rc; + + /* + * 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))); + + pgstat_report_wait_start(WAIT_EVENT_ARCHIVE_COMMAND); + rc = system(xlogarchcmd); + pgstat_report_wait_end(); + + 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; +} + +static void +shell_archive_shutdown(void) +{ + elog(DEBUG1, "archiver process shutting down"); +} |