00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "postgres_fe.h"
00014
00015 #include <ctype.h>
00016 #include <dirent.h>
00017 #include <sys/stat.h>
00018 #include <fcntl.h>
00019 #include <signal.h>
00020
00021 #ifndef WIN32
00022 #include <sys/time.h>
00023 #include <unistd.h>
00024
00025 #ifdef HAVE_GETOPT_H
00026 #include <getopt.h>
00027 #endif
00028 #else
00029 extern int getopt(int argc, char *const argv[], const char *optstring);
00030 #endif
00031
00032 extern char *optarg;
00033 extern int optind;
00034
00035 const char *progname;
00036
00037
00038 bool debug = false;
00039 bool dryrun = false;
00040 char *additional_ext = NULL;
00041
00042 char *archiveLocation;
00043 char *restartWALFileName;
00044 char WALFilePath[MAXPGPATH];
00045 char exclusiveCleanupFileName[MAXPGPATH];
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065 #define XLOG_DATA_FNAME_LEN 24
00066
00067 #define XLogFileName(fname, tli, log, seg) \
00068 snprintf(fname, XLOG_DATA_FNAME_LEN + 1, "%08X%08X%08X", tli, log, seg)
00069 #define XLOG_BACKUP_FNAME_LEN 40
00070
00071
00072
00073
00074
00075
00076 static void
00077 Initialize(void)
00078 {
00079
00080
00081
00082
00083 struct stat stat_buf;
00084
00085 if (stat(archiveLocation, &stat_buf) != 0 ||
00086 !S_ISDIR(stat_buf.st_mode))
00087 {
00088 fprintf(stderr, "%s: archive location \"%s\" does not exist\n",
00089 progname, archiveLocation);
00090 exit(2);
00091 }
00092 }
00093
00094 static void
00095 TrimExtension(char *filename, char *extension)
00096 {
00097 int flen;
00098 int elen;
00099
00100 if (extension == NULL)
00101 return;
00102
00103 elen = strlen(extension);
00104 flen = strlen(filename);
00105
00106 if (flen > elen && strcmp(filename + flen - elen, extension) == 0)
00107 filename[flen - elen] = '\0';
00108 }
00109
00110 static void
00111 CleanupPriorWALFiles(void)
00112 {
00113 int rc;
00114 DIR *xldir;
00115 struct dirent *xlde;
00116 char walfile[MAXPGPATH];
00117
00118 if ((xldir = opendir(archiveLocation)) != NULL)
00119 {
00120 while ((xlde = readdir(xldir)) != NULL)
00121 {
00122 strncpy(walfile, xlde->d_name, MAXPGPATH);
00123 TrimExtension(walfile, additional_ext);
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138 if (strlen(walfile) == XLOG_DATA_FNAME_LEN &&
00139 strspn(walfile, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN &&
00140 strcmp(walfile + 8, exclusiveCleanupFileName + 8) < 0)
00141 {
00142
00143
00144
00145
00146
00147 snprintf(WALFilePath, MAXPGPATH, "%s/%s",
00148 archiveLocation, xlde->d_name);
00149
00150 if (dryrun)
00151 {
00152
00153
00154
00155
00156
00157 printf("%s\n", WALFilePath);
00158 if (debug)
00159 fprintf(stderr,
00160 "%s: file \"%s\" would be removed\n",
00161 progname, WALFilePath);
00162 continue;
00163 }
00164
00165 if (debug)
00166 fprintf(stderr, "%s: removing file \"%s\"\n",
00167 progname, WALFilePath);
00168
00169 rc = unlink(WALFilePath);
00170 if (rc != 0)
00171 {
00172 fprintf(stderr, "%s: ERROR: could not remove file \"%s\": %s\n",
00173 progname, WALFilePath, strerror(errno));
00174 break;
00175 }
00176 }
00177 }
00178 closedir(xldir);
00179 }
00180 else
00181 fprintf(stderr, "%s: could not open archive location \"%s\": %s\n",
00182 progname, archiveLocation, strerror(errno));
00183 }
00184
00185
00186
00187
00188
00189
00190
00191 static void
00192 SetWALFileNameForCleanup(void)
00193 {
00194 bool fnameOK = false;
00195
00196 TrimExtension(restartWALFileName, additional_ext);
00197
00198
00199
00200
00201
00202
00203
00204
00205 if (strlen(restartWALFileName) == XLOG_DATA_FNAME_LEN &&
00206 strspn(restartWALFileName, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN)
00207 {
00208 strcpy(exclusiveCleanupFileName, restartWALFileName);
00209 fnameOK = true;
00210 }
00211 else if (strlen(restartWALFileName) == XLOG_BACKUP_FNAME_LEN)
00212 {
00213 int args;
00214 uint32 tli = 1,
00215 log = 0,
00216 seg = 0,
00217 offset = 0;
00218
00219 args = sscanf(restartWALFileName, "%08X%08X%08X.%08X.backup", &tli, &log, &seg, &offset);
00220 if (args == 4)
00221 {
00222 fnameOK = true;
00223
00224
00225
00226
00227
00228 XLogFileName(exclusiveCleanupFileName, tli, log, seg);
00229 }
00230 }
00231
00232 if (!fnameOK)
00233 {
00234 fprintf(stderr, "%s: invalid filename input\n", progname);
00235 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
00236 exit(2);
00237 }
00238 }
00239
00240
00241
00242
00243
00244
00245 static void
00246 usage(void)
00247 {
00248 printf("%s removes older WAL files from PostgreSQL archives.\n\n", progname);
00249 printf("Usage:\n");
00250 printf(" %s [OPTION]... ARCHIVELOCATION OLDESTKEPTWALFILE\n", progname);
00251 printf("\nOptions:\n");
00252 printf(" -d generate debug output (verbose mode)\n");
00253 printf(" -n dry run, show the names of the files that would be removed\n");
00254 printf(" -V, --version output version information, then exit\n");
00255 printf(" -x EXT clean up files if they have this extension\n");
00256 printf(" -?, --help show this help, then exit\n");
00257 printf("\n"
00258 "For use as archive_cleanup_command in recovery.conf when standby_mode = on:\n"
00259 " archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n"
00260 "e.g.\n"
00261 " archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n");
00262 printf("\n"
00263 "Or for use as a standalone archive cleaner:\n"
00264 "e.g.\n"
00265 " pg_archivecleanup /mnt/server/archiverdir 000000010000000000000010.00000020.backup\n");
00266 printf("\nReport bugs to <[email protected]>.\n");
00267 }
00268
00269
00270 int
00271 main(int argc, char **argv)
00272 {
00273 int c;
00274
00275 progname = get_progname(argv[0]);
00276
00277 if (argc > 1)
00278 {
00279 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
00280 {
00281 usage();
00282 exit(0);
00283 }
00284 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
00285 {
00286 puts("pg_archivecleanup (PostgreSQL) " PG_VERSION);
00287 exit(0);
00288 }
00289 }
00290
00291 while ((c = getopt(argc, argv, "x:dn")) != -1)
00292 {
00293 switch (c)
00294 {
00295 case 'd':
00296 debug = true;
00297 break;
00298 case 'n':
00299 dryrun = true;
00300 break;
00301 case 'x':
00302 additional_ext = strdup(optarg);
00303
00304 break;
00305 default:
00306 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
00307 exit(2);
00308 break;
00309 }
00310 }
00311
00312
00313
00314
00315
00316
00317
00318
00319 if (optind < argc)
00320 {
00321 archiveLocation = argv[optind];
00322 optind++;
00323 }
00324 else
00325 {
00326 fprintf(stderr, "%s: must specify archive location\n", progname);
00327 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
00328 exit(2);
00329 }
00330
00331 if (optind < argc)
00332 {
00333 restartWALFileName = argv[optind];
00334 optind++;
00335 }
00336 else
00337 {
00338 fprintf(stderr, "%s: must specify restartfilename\n", progname);
00339 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
00340 exit(2);
00341 }
00342
00343 if (optind < argc)
00344 {
00345 fprintf(stderr, "%s: too many parameters\n", progname);
00346 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
00347 exit(2);
00348 }
00349
00350
00351
00352
00353 Initialize();
00354
00355
00356
00357
00358 SetWALFileNameForCleanup();
00359
00360 if (debug)
00361 {
00362 snprintf(WALFilePath, MAXPGPATH, "%s/%s",
00363 archiveLocation, exclusiveCleanupFileName);
00364 fprintf(stderr, "%s: keep WAL file \"%s\" and later\n",
00365 progname, WALFilePath);
00366 }
00367
00368
00369
00370
00371 CleanupPriorWALFiles();
00372
00373 exit(0);
00374 }