/* * 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; TrimExtension(restartWALFileName, additional_ext); /* * 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 (IsXLogFileName(restartWALFileName)) { strcpy(exclusiveCleanupFileName, restartWALFileName); fnameOK = true; } else if (IsBackupHistoryFileName(restartWALFileName)) { 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 */ XLogFileNameById(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); } }
/* * Remove existing XLOG files */ static void KillExistingXLOG(void) { DIR *xldir; struct dirent *xlde; char path[MAXPGPATH]; xldir = opendir(XLOGDIR); if (xldir == NULL) { fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"), progname, XLOGDIR, strerror(errno)); exit(1); } while (errno = 0, (xlde = readdir(xldir)) != NULL) { if (IsXLogFileName(xlde->d_name) || IsPartialXLogFileName(xlde->d_name)) { snprintf(path, MAXPGPATH, "%s/%s", XLOGDIR, xlde->d_name); if (unlink(path) < 0) { fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"), progname, path, strerror(errno)); exit(1); } } } if (errno) { fprintf(stderr, _("%s: could not read directory \"%s\": %s\n"), progname, XLOGDIR, strerror(errno)); exit(1); } if (closedir(xldir)) { fprintf(stderr, _("%s: could not close directory \"%s\": %s\n"), progname, XLOGDIR, strerror(errno)); exit(1); } }
static void CleanupPriorWALFiles(void) { int rc; DIR *xldir; struct dirent *xlde; char walfile[MAXPGPATH]; if ((xldir = opendir(archiveLocation)) != NULL) { while (errno = 0, (xlde = readdir(xldir)) != NULL) { /* * Truncation is essentially harmless, because we skip names of * length other than XLOG_FNAME_LEN. (In principle, one * could use a 1000-character additional_ext and get trouble.) */ strlcpy(walfile, xlde->d_name, MAXPGPATH); TrimExtension(walfile, additional_ext); /* * 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 ((IsXLogFileName(walfile) || IsPartialXLogFileName(walfile)) && strcmp(walfile + 8, exclusiveCleanupFileName + 8) < 0) { /* * Use the original file name again now, including any * extension that might have been chopped off before testing * the sequence. */ snprintf(WALFilePath, MAXPGPATH, "%s/%s", archiveLocation, xlde->d_name); if (dryrun) { /* * Prints the name of the file to be removed and skips the * actual removal. The regular printout is so that the * user can pipe the output into some other program. */ printf("%s\n", WALFilePath); if (debug) fprintf(stderr, "%s: file \"%s\" would be removed\n", progname, WALFilePath); continue; } if (debug) fprintf(stderr, "%s: removing file \"%s\"\n", progname, WALFilePath); rc = unlink(WALFilePath); if (rc != 0) { fprintf(stderr, "%s: ERROR: could not remove file \"%s\": %s\n", progname, WALFilePath, strerror(errno)); break; } } } if (errno) fprintf(stderr, "%s: could not read archive location \"%s\": %s\n", progname, archiveLocation, strerror(errno)); if (closedir(xldir)) fprintf(stderr, "%s: could not close archive location \"%s\": %s\n", progname, archiveLocation, strerror(errno)); } else fprintf(stderr, "%s: could not open archive location \"%s\": %s\n", progname, archiveLocation, strerror(errno)); }
static void CustomizableCleanupPriorWALFiles(void) { /* * Work out name of prior file from current filename */ if (nextWALFileType == XLOG_DATA) { int rc; DIR *xldir; struct dirent *xlde; /* * Assume it's OK to keep failing. The failure situation may change * over time, so we'd rather keep going on the main processing than * fail because we couldn't clean up yet. */ if ((xldir = opendir(archiveLocation)) != NULL) { while (errno = 0, (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 (IsXLogFileName(xlde->d_name) && strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0) { #ifdef WIN32 snprintf(WALFilePath, sizeof(WALFilePath), "%s\\%s", archiveLocation, xlde->d_name); #else snprintf(WALFilePath, sizeof(WALFilePath), "%s/%s", archiveLocation, xlde->d_name); #endif if (debug) fprintf(stderr, "\nremoving file \"%s\"", WALFilePath); rc = unlink(WALFilePath); if (rc != 0) { fprintf(stderr, "\n%s: ERROR: could not remove file \"%s\": %s\n", progname, WALFilePath, strerror(errno)); break; } } } if (errno) fprintf(stderr, "%s: could not read archive location \"%s\": %s\n", progname, archiveLocation, strerror(errno)); if (debug) fprintf(stderr, "\n"); } else fprintf(stderr, "%s: could not open archive location \"%s\": %s\n", progname, archiveLocation, strerror(errno)); if (closedir(xldir)) fprintf(stderr, "%s: could not close archive location \"%s\": %s\n", progname, archiveLocation, strerror(errno)); fflush(stderr); } }
/* * CustomizableNextWALFileReady() * * Is the requested file ready yet? */ static bool CustomizableNextWALFileReady(void) { if (stat(WALFilePath, &stat_buf) == 0) { /* * If we've not seen any WAL segments, we don't know the WAL segment * size, which we need. If it looks like a WAL segment, determine size * of segments for the cluster. */ if (WalSegSz == -1 && IsXLogFileName(nextWALFileName)) { if (SetWALSegSize()) { /* * Successfully determined WAL segment size. Can compute * cleanup cutoff now. */ need_cleanup = SetWALFileNameForCleanup(); if (debug) { fprintf(stderr, _("WAL segment size: %d \n"), WalSegSz); fprintf(stderr, "Keep archive history: "); if (need_cleanup) fprintf(stderr, "%s and later\n", exclusiveCleanupFileName); else fprintf(stderr, "no cleanup required\n"); } } } /* * If it's a backup file, return immediately. If it's a regular file * return only if it's the right size already. */ if (IsBackupHistoryFileName(nextWALFileName)) { nextWALFileType = XLOG_BACKUP_LABEL; return true; } else if (WalSegSz > 0 && stat_buf.st_size == WalSegSz) { #ifdef WIN32 /* * Windows 'cp' sets the final file size before the copy is * complete, and not yet ready to be opened by pg_standby. So we * wait for sleeptime secs before attempting to restore. If that * is not enough, we will rely on the retry/holdoff mechanism. * GNUWin32's cp does not have this problem. */ pg_usleep(sleeptime * 1000000L); #endif nextWALFileType = XLOG_DATA; return true; } /* * If still too small, wait until it is the correct size */ if (WalSegSz > 0 && stat_buf.st_size > WalSegSz) { if (debug) { fprintf(stderr, "file size greater than expected\n"); fflush(stderr); } exit(3); } } return false; }
int do_delete(pgBackupRange *range) { int i; int ret; parray *backup_list; bool do_delete = false; XLogRecPtr oldest_lsn = InvalidXLogRecPtr; TimeLineID oldest_tli; /* DATE are always required */ if (!pgBackupRangeIsValid(range)) elog(ERROR, "required delete range option not specified: delete DATE"); /* Lock backup catalog */ ret = catalog_lock(); if (ret == -1) elog(ERROR, "can't lock backup catalog."); else if (ret == 1) elog(ERROR, "another pg_arman is running, stop delete."); /* Get complete list of backups */ backup_list = catalog_get_backup_list(NULL); if (!backup_list) elog(ERROR, "No backup list found, can't process any more."); /* Find backups to be deleted */ for (i = 0; i < parray_num(backup_list); i++) { pgBackup *backup = (pgBackup *) parray_get(backup_list, i); /* delete backup and update status to DELETED */ if (do_delete) { /* check for interrupt */ if (interrupted) elog(ERROR, "interrupted during delete backup"); pgBackupDeleteFiles(backup); continue; } /* Found the latest full backup */ if (backup->backup_mode >= BACKUP_MODE_FULL && backup->status == BACKUP_STATUS_OK && backup->start_time <= range->begin) { do_delete = true; oldest_lsn = backup->start_lsn; oldest_tli = backup->tli; } } /* release catalog lock */ catalog_unlock(); /* cleanup */ parray_walk(backup_list, pgBackupFree); parray_free(backup_list); /* * Delete in archive WAL segments that are not needed anymore. The oldest * segment to be kept is the first segment that the oldest full backup * found around needs to keep. */ if (!XLogRecPtrIsInvalid(oldest_lsn)) { XLogSegNo targetSegNo; char oldestSegmentNeeded[MAXFNAMELEN]; DIR *arcdir; struct dirent *arcde; char wal_file[MAXPGPATH]; int rc; XLByteToSeg(oldest_lsn, targetSegNo); XLogFileName(oldestSegmentNeeded, oldest_tli, targetSegNo); elog(LOG, "Removing segments older than %s", oldestSegmentNeeded); /* * Now is time to do the actual work and to remove all the segments * not needed anymore. */ if ((arcdir = opendir(arclog_path)) != NULL) { while (errno = 0, (arcde = readdir(arcdir)) != 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 ((IsXLogFileName(arcde->d_name) || IsPartialXLogFileName(arcde->d_name)) && strcmp(arcde->d_name + 8, oldestSegmentNeeded + 8) < 0) { /* * Use the original file name again now, including any * extension that might have been chopped off before testing * the sequence. */ snprintf(wal_file, MAXPGPATH, "%s/%s", arclog_path, arcde->d_name); rc = unlink(wal_file); if (rc != 0) { elog(WARNING, "could not remove file \"%s\": %s", wal_file, strerror(errno)); break; } elog(LOG, "removed WAL segment \"%s\"", wal_file); } } if (errno) elog(WARNING, "could not read archive location \"%s\": %s", arclog_path, strerror(errno)); if (closedir(arcdir)) elog(WARNING, "could not close archive location \"%s\": %s", arclog_path, strerror(errno)); } else elog(WARNING, "could not open archive location \"%s\": %s", arclog_path, strerror(errno)); } return 0; }
/* * Scan existing XLOG files and determine the highest existing WAL address * * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size * are assumed valid (note that we allow the old xlog seg size to differ * from what we're using). On exit, newXlogId and newXlogSeg are set to * suitable values for the beginning of replacement WAL (in our seg size). */ static void FindEndOfXLOG(void) { DIR *xldir; struct dirent *xlde; uint64 segs_per_xlogid; uint64 xlogbytepos; /* * Initialize the max() computation using the last checkpoint address from * old pg_control. Note that for the moment we are working with segment * numbering according to the old xlog seg size. */ segs_per_xlogid = (UINT64CONST(0x0000000100000000) / ControlFile.xlog_seg_size); newXlogSegNo = ControlFile.checkPointCopy.redo / ControlFile.xlog_seg_size; /* * Scan the pg_xlog directory to find existing WAL segment files. We * assume any present have been used; in most scenarios this should be * conservative, because of xlog.c's attempts to pre-create files. */ xldir = opendir(XLOGDIR); if (xldir == NULL) { fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"), progname, XLOGDIR, strerror(errno)); exit(1); } while (errno = 0, (xlde = readdir(xldir)) != NULL) { if (IsXLogFileName(xlde->d_name) || IsPartialXLogFileName(xlde->d_name)) { unsigned int tli, log, seg; XLogSegNo segno; /* * Note: We don't use XLogFromFileName here, because we want to * use the segment size from the control file, not the size the * pg_resetxlog binary was compiled with */ sscanf(xlde->d_name, "%08X%08X%08X", &tli, &log, &seg); segno = ((uint64) log) * segs_per_xlogid + seg; /* * Note: we take the max of all files found, regardless of their * timelines. Another possibility would be to ignore files of * timelines other than the target TLI, but this seems safer. * Better too large a result than too small... */ if (segno > newXlogSegNo) newXlogSegNo = segno; } } if (errno) { fprintf(stderr, _("%s: could not read directory \"%s\": %s\n"), progname, XLOGDIR, strerror(errno)); exit(1); } if (closedir(xldir)) { fprintf(stderr, _("%s: could not close directory \"%s\": %s\n"), progname, XLOGDIR, strerror(errno)); exit(1); } /* * Finally, convert to new xlog seg size, and advance by one to ensure we * are in virgin territory. */ xlogbytepos = newXlogSegNo * ControlFile.xlog_seg_size; newXlogSegNo = (xlogbytepos + XLogSegSize - 1) / XLogSegSize; newXlogSegNo++; }