mirror of https://github.com/postgres/postgres
parent
25fee5cfbd
commit
ca65f2190a
@ -0,0 +1,18 @@ |
|||||||
|
# $PostgreSQL: pgsql/contrib/pg_archivecleanup/Makefile,v 1.1 2010/06/14 16:19:24 sriggs Exp $
|
||||||
|
|
||||||
|
PGFILEDESC = "pg_archivecleanup - cleans archive when used with streaming replication"
|
||||||
|
PGAPPICON=win32
|
||||||
|
|
||||||
|
PROGRAM = pg_archivecleanup
|
||||||
|
OBJS = pg_archivecleanup.o
|
||||||
|
|
||||||
|
ifdef USE_PGXS |
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS) |
||||||
|
else |
||||||
|
subdir = contrib/pg_archivecleanup
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global |
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk |
||||||
|
endif |
||||||
@ -0,0 +1,318 @@ |
|||||||
|
/*
|
||||||
|
* $PostgreSQL: pgsql/contrib/pg_archivecleanup/pg_archivecleanup.c,v 1.1 2010/06/14 16:19:24 sriggs Exp $ |
||||||
|
* |
||||||
|
* pg_archivecleanup.c |
||||||
|
* |
||||||
|
* Production-ready example of an archive_cleanup_command |
||||||
|
* used to clean an archive when using standby_mode = on in 9.0 |
||||||
|
* or for standalone use for any version of PostgreSQL 8.0+. |
||||||
|
* |
||||||
|
* Original author: Simon Riggs simon@2ndquadrant.com |
||||||
|
* Current maintainer: Simon Riggs |
||||||
|
*/ |
||||||
|
#include "postgres_fe.h" |
||||||
|
|
||||||
|
#include <ctype.h> |
||||||
|
#include <dirent.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <signal.h> |
||||||
|
|
||||||
|
#ifdef WIN32 |
||||||
|
int getopt(int argc, char *const argv[], const char *optstring); |
||||||
|
#else |
||||||
|
#include <sys/time.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#ifdef HAVE_GETOPT_H |
||||||
|
#include <getopt.h> |
||||||
|
#endif |
||||||
|
#endif /* ! WIN32 */ |
||||||
|
|
||||||
|
extern char *optarg; |
||||||
|
extern int optind; |
||||||
|
|
||||||
|
const char *progname; |
||||||
|
|
||||||
|
/* Options and defaults */ |
||||||
|
bool debug = false; /* are we debugging? */ |
||||||
|
|
||||||
|
char *archiveLocation; /* where to find the archive? */ |
||||||
|
char *restartWALFileName; /* the file from which we can restart restore */ |
||||||
|
char WALFilePath[MAXPGPATH]; /* the file path including archive */ |
||||||
|
char exclusiveCleanupFileName[MAXPGPATH]; /* the oldest file we want to
|
||||||
|
* remain in archive */ |
||||||
|
|
||||||
|
struct stat stat_buf; |
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
* |
||||||
|
* Customizable section |
||||||
|
* |
||||||
|
* ===================================================================== |
||||||
|
* |
||||||
|
* Currently, this section assumes that the Archive is a locally |
||||||
|
* accessible directory. If you want to make other assumptions, |
||||||
|
* such as using a vendor-specific archive and access API, these |
||||||
|
* routines are the ones you'll need to change. You're |
||||||
|
* enouraged to submit any changes to pgsql-hackers@postgresql.org |
||||||
|
* or personally to the current maintainer. Those changes may be |
||||||
|
* folded in to later versions of this program. |
||||||
|
*/ |
||||||
|
|
||||||
|
#define XLOG_DATA_FNAME_LEN 24 |
||||||
|
/* Reworked from access/xlog_internal.h */ |
||||||
|
#define XLogFileName(fname, tli, log, seg) \ |
||||||
|
snprintf(fname, XLOG_DATA_FNAME_LEN + 1, "%08X%08X%08X", tli, log, seg) |
||||||
|
#define XLOG_BACKUP_FNAME_LEN 40 |
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize allows customized commands into the archive cleanup program. |
||||||
|
* |
||||||
|
* You may wish to add code to check for tape libraries, etc.. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
Initialize(void) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* This code assumes that archiveLocation is a directory, so we use |
||||||
|
* stat to test if it's accessible. |
||||||
|
*/ |
||||||
|
if (stat(archiveLocation, &stat_buf) != 0) |
||||||
|
{ |
||||||
|
fprintf(stderr, "%s: archiveLocation \"%s\" does not exist\n", progname, archiveLocation); |
||||||
|
fflush(stderr); |
||||||
|
exit(2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
CleanupPriorWALFiles(void) |
||||||
|
{ |
||||||
|
int rc; |
||||||
|
DIR *xldir; |
||||||
|
struct dirent *xlde; |
||||||
|
|
||||||
|
if ((xldir = opendir(archiveLocation)) != NULL) |
||||||
|
{ |
||||||
|
while ((xlde = readdir(xldir)) != NULL) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* We ignore the timeline part of the XLOG segment identifiers |
||||||
|
* in deciding whether a segment is still needed. This |
||||||
|
* ensures that we won't prematurely remove a segment from a |
||||||
|
* parent timeline. We could probably be a little more |
||||||
|
* proactive about removing segments of non-parent timelines, |
||||||
|
* but that would be a whole lot more complicated. |
||||||
|
* |
||||||
|
* We use the alphanumeric sorting property of the filenames |
||||||
|
* to decide which ones are earlier than the |
||||||
|
* exclusiveCleanupFileName file. Note that this means files |
||||||
|
* are not removed in the order they were originally written, |
||||||
|
* in case this worries you. |
||||||
|
*/ |
||||||
|
if (strlen(xlde->d_name) == XLOG_DATA_FNAME_LEN && |
||||||
|
strspn(xlde->d_name, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN && |
||||||
|
strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0) |
||||||
|
{ |
||||||
|
#ifdef WIN32 |
||||||
|
snprintf(WALFilePath, MAXPGPATH, "%s\\%s", archiveLocation, xlde->d_name); |
||||||
|
#else |
||||||
|
snprintf(WALFilePath, MAXPGPATH, "%s/%s", archiveLocation, xlde->d_name); |
||||||
|
#endif |
||||||
|
|
||||||
|
if (debug) |
||||||
|
fprintf(stderr, "\n%s: removing \"%s\"", progname, WALFilePath); |
||||||
|
|
||||||
|
rc = unlink(WALFilePath); |
||||||
|
if (rc != 0) |
||||||
|
{ |
||||||
|
fprintf(stderr, "\n%s: ERROR failed to remove \"%s\": %s", |
||||||
|
progname, WALFilePath, strerror(errno)); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (debug) |
||||||
|
fprintf(stderr, "\n"); |
||||||
|
} |
||||||
|
else |
||||||
|
fprintf(stderr, "%s: archiveLocation \"%s\" open error\n", progname, archiveLocation); |
||||||
|
|
||||||
|
closedir(xldir); |
||||||
|
fflush(stderr); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* SetWALFileNameForCleanup() |
||||||
|
* |
||||||
|
* Set the earliest WAL filename that we want to keep on the archive |
||||||
|
* and decide whether we need_cleanup |
||||||
|
*/ |
||||||
|
static void |
||||||
|
SetWALFileNameForCleanup(void) |
||||||
|
{ |
||||||
|
bool fnameOK = false; |
||||||
|
|
||||||
|
/*
|
||||||
|
* If restartWALFileName is a WAL file name then just use it directly. |
||||||
|
* If restartWALFileName is a .backup filename, make sure we use |
||||||
|
* the prefix of the filename, otherwise we will remove wrong files |
||||||
|
* since 000000010000000000000010.00000020.backup is after
|
||||||
|
* 000000010000000000000010. |
||||||
|
*/ |
||||||
|
if (strlen(restartWALFileName) == XLOG_DATA_FNAME_LEN && |
||||||
|
strspn(restartWALFileName, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN) |
||||||
|
{ |
||||||
|
strcpy(exclusiveCleanupFileName, restartWALFileName); |
||||||
|
fnameOK = true; |
||||||
|
} |
||||||
|
else if (strlen(restartWALFileName) == XLOG_BACKUP_FNAME_LEN) |
||||||
|
{ |
||||||
|
int args; |
||||||
|
uint32 tli = 1, |
||||||
|
log = 0, |
||||||
|
seg = 0, |
||||||
|
offset = 0; |
||||||
|
args = sscanf(restartWALFileName, "%08X%08X%08X.%08X.backup", &tli, &log, &seg, &offset); |
||||||
|
if (args == 4) |
||||||
|
{ |
||||||
|
fnameOK = true; |
||||||
|
/*
|
||||||
|
* Use just the prefix of the filename, ignore everything after first period |
||||||
|
*/ |
||||||
|
XLogFileName(exclusiveCleanupFileName, tli, log, seg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!fnameOK) |
||||||
|
{ |
||||||
|
fprintf(stderr, "%s: invalid filename input\n", progname); |
||||||
|
fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); |
||||||
|
exit(2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
* End of Customizable section |
||||||
|
* ===================================================================== |
||||||
|
*/ |
||||||
|
|
||||||
|
static void |
||||||
|
usage(void) |
||||||
|
{ |
||||||
|
printf("%s removes older WAL files from PostgreSQL archives.\n\n", progname); |
||||||
|
printf("Usage:\n"); |
||||||
|
printf(" %s [OPTION]... ARCHIVELOCATION OLDESTKEPTWALFILE\n", progname); |
||||||
|
printf("\n" |
||||||
|
"for use as an archive_cleanup_command in the recovery.conf when standby_mode = on:\n" |
||||||
|
" archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n" |
||||||
|
"e.g.\n" |
||||||
|
" archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n"); |
||||||
|
printf("\n" |
||||||
|
"or for use as a standalone archive cleaner:\n" |
||||||
|
"e.g.\n" |
||||||
|
" pg_archivecleanup /mnt/server/archiverdir 000000010000000000000010.00000020.backup\n"); |
||||||
|
printf("\nOptions:\n"); |
||||||
|
printf(" -d generates debug output (verbose mode)\n"); |
||||||
|
printf(" --help show this help, then exit\n"); |
||||||
|
printf(" --version output version information, then exit\n"); |
||||||
|
printf("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"); |
||||||
|
} |
||||||
|
|
||||||
|
/*------------ MAIN ----------------------------------------*/ |
||||||
|
int |
||||||
|
main(int argc, char **argv) |
||||||
|
{ |
||||||
|
int c; |
||||||
|
|
||||||
|
progname = get_progname(argv[0]); |
||||||
|
|
||||||
|
if (argc > 1) |
||||||
|
{ |
||||||
|
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) |
||||||
|
{ |
||||||
|
usage(); |
||||||
|
exit(0); |
||||||
|
} |
||||||
|
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) |
||||||
|
{ |
||||||
|
puts("pg_archivecleanup (PostgreSQL) " PG_VERSION); |
||||||
|
exit(0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "d")) != -1) |
||||||
|
{ |
||||||
|
switch (c) |
||||||
|
{ |
||||||
|
case 'd': /* Debug mode */ |
||||||
|
debug = true; |
||||||
|
break; |
||||||
|
default: |
||||||
|
fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); |
||||||
|
exit(2); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* We will go to the archiveLocation to check restartWALFileName. |
||||||
|
* restartWALFileName may not exist anymore, which would not be an error, so |
||||||
|
* we separate the archiveLocation and restartWALFileName so we can check |
||||||
|
* separately whether archiveLocation exists, if not that is an error |
||||||
|
*/ |
||||||
|
if (optind < argc) |
||||||
|
{ |
||||||
|
archiveLocation = argv[optind]; |
||||||
|
optind++; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
fprintf(stderr, "%s: must specify archive location\n", progname); |
||||||
|
fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); |
||||||
|
exit(2); |
||||||
|
} |
||||||
|
|
||||||
|
if (optind < argc) |
||||||
|
{ |
||||||
|
restartWALFileName = argv[optind]; |
||||||
|
optind++; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
fprintf(stderr, "%s: must specify restartfilename\n", progname); |
||||||
|
fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); |
||||||
|
exit(2); |
||||||
|
} |
||||||
|
|
||||||
|
if (optind < argc) |
||||||
|
{ |
||||||
|
fprintf(stderr, "%s: too many parameters\n", progname); |
||||||
|
fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); |
||||||
|
exit(2); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check archive exists and other initialization if required. |
||||||
|
*/ |
||||||
|
Initialize(); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check filename is a valid name, then process to find cut-off |
||||||
|
*/ |
||||||
|
SetWALFileNameForCleanup(); |
||||||
|
|
||||||
|
if (debug) |
||||||
|
{ |
||||||
|
fprintf(stderr, "%s: keep WAL file %s and later", progname, exclusiveCleanupFileName); |
||||||
|
fflush(stderr); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove WAL files older than cut-off |
||||||
|
*/ |
||||||
|
CleanupPriorWALFiles(); |
||||||
|
|
||||||
|
exit(0); |
||||||
|
} |
||||||
Loading…
Reference in new issue