bool readControlFile(ControlFileData *ctrl, const char *pgdata) { char path[MAXPGPATH]; int fd; pg_crc32 crc; snprintf(path, MAXPGPATH, "%s/global/pg_control", pgdata); if ((fd = open(path, O_RDONLY | PG_BINARY, 0)) == -1) return false; if (read(fd, ctrl, sizeof(ControlFileData)) != sizeof(ControlFileData)) return false; close(fd); /* Check the CRC. */ INIT_CRC32(crc); COMP_CRC32(crc, (char *) ctrl, offsetof(ControlFileData, crc)); FIN_CRC32(crc); if (!EQ_CRC32(crc, ctrl->crc)) return false; return true; }
int main(int argc, char *argv[]) { ControlFileData ControlFile; int fd; char ControlFilePath[MAXPGPATH]; char *DataDir; pg_crc32 crc; char pgctime_str[128]; char ckpttime_str[128]; char sysident_str[32]; char *strftime_fmt = "%c"; const char *progname; set_pglocale_pgservice(argv[0], "pg_controldata"); progname = get_progname(argv[0]); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_controldata (PostgreSQL) " PG_VERSION); exit(0); } } if (argc > 1) DataDir = argv[1]; else DataDir = getenv("PGDATA"); if (DataDir == NULL) { fprintf(stderr, _("%s: no data directory specified\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir); if ((fd = open(ControlFilePath, O_RDONLY)) == -1) { fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, ControlFilePath, strerror(errno)); exit(2); } if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData)) { fprintf(stderr, _("%s: could not read file \"%s\": %s\n"), progname, ControlFilePath, strerror(errno)); exit(2); } close(fd); /* Check the CRC. */ INIT_CRC32(crc); COMP_CRC32(crc, (char *) &ControlFile, offsetof(ControlFileData, crc)); FIN_CRC32(crc); if (!EQ_CRC32(crc, ControlFile.crc)) printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n" "Either the file is corrupt, or it has a different layout than this program\n" "is expecting. The results below are untrustworthy.\n\n")); /* * Use variable for format to suppress overly-anal-retentive gcc warning * about %c */ strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt, localtime(&(ControlFile.time))); strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt, localtime(&(ControlFile.checkPointCopy.time))); /* * Format system_identifier separately to keep platform-dependent format * code out of the translatable message string. */ snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT, ControlFile.system_identifier); printf(_("pg_control version number: %u\n"), ControlFile.pg_control_version); printf(_("Catalog version number: %u\n"), ControlFile.catalog_version_no); printf(_("Database system identifier: %s\n"), sysident_str); printf(_("Database cluster state: %s\n"), dbState(ControlFile.state)); printf(_("pg_control last modified: %s\n"), pgctime_str); printf(_("Current log file ID: %u\n"), ControlFile.logId); printf(_("Next log file segment: %u\n"), ControlFile.logSeg); printf(_("Latest checkpoint location: %X/%X\n"), ControlFile.checkPoint.xlogid, ControlFile.checkPoint.xrecoff); printf(_("Prior checkpoint location: %X/%X\n"), ControlFile.prevCheckPoint.xlogid, ControlFile.prevCheckPoint.xrecoff); printf(_("Latest checkpoint's REDO location: %X/%X\n"), ControlFile.checkPointCopy.redo.xlogid, ControlFile.checkPointCopy.redo.xrecoff); printf(_("Latest checkpoint's UNDO location: %X/%X\n"), ControlFile.checkPointCopy.undo.xlogid, ControlFile.checkPointCopy.undo.xrecoff); printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile.checkPointCopy.ThisTimeLineID); printf(_("Latest checkpoint's NextXID: %u\n"), ControlFile.checkPointCopy.nextXid); printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile.checkPointCopy.nextOid); printf(_("Latest checkpoint's NextMultiXactId: %u\n"), ControlFile.checkPointCopy.nextMulti); printf(_("Latest checkpoint's NextMultiOffset: %u\n"), ControlFile.checkPointCopy.nextMultiOffset); printf(_("Time of latest checkpoint: %s\n"), ckpttime_str); printf(_("Maximum data alignment: %u\n"), ControlFile.maxAlign); /* we don't print floatFormat since can't say much useful about it */ printf(_("Database block size: %u\n"), ControlFile.blcksz); printf(_("Blocks per segment of large relation: %u\n"), ControlFile.relseg_size); printf(_("Bytes per WAL segment: %u\n"), ControlFile.xlog_seg_size); printf(_("Maximum length of identifiers: %u\n"), ControlFile.nameDataLen); printf(_("Maximum columns in an index: %u\n"), ControlFile.indexMaxKeys); printf(_("Date/time type storage: %s\n"), (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers"))); printf(_("Maximum length of locale name: %u\n"), ControlFile.localeBuflen); printf(_("LC_COLLATE: %s\n"), ControlFile.lc_collate); printf(_("LC_CTYPE: %s\n"), ControlFile.lc_ctype); return 0; }
int main(int argc, char *argv[]) { ControlFileData ControlFile; int fd; char ControlFilePath[MAXPGPATH]; char *DataDir; pg_crc32 crc; time_t time_tmp; char pgctime_str[128]; char ckpttime_str[128]; char sysident_str[32]; const char *strftime_fmt = "%c"; const char *progname; set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_controldata")); progname = get_progname(argv[0]); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(progname); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_controldata (PostgreSQL) " PG_VERSION); exit(0); } } if (argc > 1) DataDir = argv[1]; else DataDir = getenv("PGDATA"); if (DataDir == NULL) { fprintf(stderr, _("%s: no data directory specified\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir); if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1) { fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, ControlFilePath, strerror(errno)); exit(2); } if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData)) { fprintf(stderr, _("%s: could not read file \"%s\": %s\n"), progname, ControlFilePath, strerror(errno)); exit(2); } close(fd); /* Check the CRC. */ INIT_CRC32(crc); COMP_CRC32(crc, (char *) &ControlFile, offsetof(ControlFileData, crc)); FIN_CRC32(crc); if (!EQ_CRC32(crc, ControlFile.crc)) printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n" "Either the file is corrupt, or it has a different layout than this program\n" "is expecting. The results below are untrustworthy.\n\n")); /* * This slightly-chintzy coding will work as long as the control file * timestamps are within the range of time_t; that should be the case in * all foreseeable circumstances, so we don't bother importing the * backend's timezone library into pg_controldata. * * Use variable for format to suppress overly-anal-retentive gcc warning * about %c */ time_tmp = (time_t) ControlFile.time; strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt, localtime(&time_tmp)); time_tmp = (time_t) ControlFile.checkPointCopy.time; strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt, localtime(&time_tmp)); /* * Format system_identifier separately to keep platform-dependent format * code out of the translatable message string. */ snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT, ControlFile.system_identifier); printf(_("pg_control version number: %u\n"), ControlFile.pg_control_version); if (ControlFile.pg_control_version % 65536 == 0 && ControlFile.pg_control_version / 65536 != 0) printf(_("WARNING: possible byte ordering mismatch\n" "The byte ordering used to store the pg_control file might not match the one\n" "used by this program. In that case the results below would be incorrect, and\n" "the PostgreSQL installation would be incompatible with this data directory.\n")); printf(_("Catalog version number: %u\n"), ControlFile.catalog_version_no); printf(_("Database system identifier: %s\n"), sysident_str); printf(_("Database cluster state: %s\n"), dbState(ControlFile.state)); printf(_("pg_control last modified: %s\n"), pgctime_str); printf(_("Latest checkpoint location: %X/%X\n"), ControlFile.checkPoint.xlogid, ControlFile.checkPoint.xrecoff); printf(_("Prior checkpoint location: %X/%X\n"), ControlFile.prevCheckPoint.xlogid, ControlFile.prevCheckPoint.xrecoff); printf(_("Latest checkpoint's REDO location: %X/%X\n"), ControlFile.checkPointCopy.redo.xlogid, ControlFile.checkPointCopy.redo.xrecoff); printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile.checkPointCopy.ThisTimeLineID); printf(_("Latest checkpoint's full_page_writes: %s\n"), ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off")); printf(_("Latest checkpoint's NextXID: %u/%u\n"), ControlFile.checkPointCopy.nextXidEpoch, ControlFile.checkPointCopy.nextXid); printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile.checkPointCopy.nextOid); printf(_("Latest checkpoint's NextMultiXactId: %u\n"), ControlFile.checkPointCopy.nextMulti); printf(_("Latest checkpoint's NextMultiOffset: %u\n"), ControlFile.checkPointCopy.nextMultiOffset); printf(_("Latest checkpoint's oldestXID: %u\n"), ControlFile.checkPointCopy.oldestXid); printf(_("Latest checkpoint's oldestXID's DB: %u\n"), ControlFile.checkPointCopy.oldestXidDB); printf(_("Latest checkpoint's oldestActiveXID: %u\n"), ControlFile.checkPointCopy.oldestActiveXid); printf(_("Time of latest checkpoint: %s\n"), ckpttime_str); printf(_("Minimum recovery ending location: %X/%X\n"), ControlFile.minRecoveryPoint.xlogid, ControlFile.minRecoveryPoint.xrecoff); printf(_("Backup start location: %X/%X\n"), ControlFile.backupStartPoint.xlogid, ControlFile.backupStartPoint.xrecoff); printf(_("Backup end location: %X/%X\n"), ControlFile.backupEndPoint.xlogid, ControlFile.backupEndPoint.xrecoff); printf(_("End-of-backup record required: %s\n"), ControlFile.backupEndRequired ? _("yes") : _("no")); printf(_("Current wal_level setting: %s\n"), wal_level_str(ControlFile.wal_level)); printf(_("Current max_connections setting: %d\n"), ControlFile.MaxConnections); printf(_("Current max_prepared_xacts setting: %d\n"), ControlFile.max_prepared_xacts); printf(_("Current max_locks_per_xact setting: %d\n"), ControlFile.max_locks_per_xact); printf(_("Maximum data alignment: %u\n"), ControlFile.maxAlign); /* we don't print floatFormat since can't say much useful about it */ printf(_("Database block size: %u\n"), ControlFile.blcksz); printf(_("Blocks per segment of large relation: %u\n"), ControlFile.relseg_size); printf(_("WAL block size: %u\n"), ControlFile.xlog_blcksz); printf(_("Bytes per WAL segment: %u\n"), ControlFile.xlog_seg_size); printf(_("Maximum length of identifiers: %u\n"), ControlFile.nameDataLen); printf(_("Maximum columns in an index: %u\n"), ControlFile.indexMaxKeys); printf(_("Maximum size of a TOAST chunk: %u\n"), ControlFile.toast_max_chunk_size); printf(_("Date/time type storage: %s\n"), (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers"))); printf(_("Float4 argument passing: %s\n"), (ControlFile.float4ByVal ? _("by value") : _("by reference"))); printf(_("Float8 argument passing: %s\n"), (ControlFile.float8ByVal ? _("by value") : _("by reference"))); return 0; }
/* * Read and validate the state file for xid. * * If it looks OK (has a valid magic number and CRC), return the palloc'd * contents of the file. Otherwise return NULL. */ static char * ReadTwoPhaseFile(TransactionId xid) { char path[MAXPGPATH]; char *buf; TwoPhaseFileHeader *hdr; int fd; struct stat stat; uint32 crc_offset; pg_crc32 calc_crc, file_crc; TwoPhaseFilePath(path, xid); fd = BasicOpenFile(path, O_RDONLY | PG_BINARY, 0); if (fd < 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not open two-phase state file \"%s\": %m", path))); return NULL; } /* * Check file length. We can determine a lower bound pretty easily. We * set an upper bound to avoid palloc() failure on a corrupt file, though * we can't guarantee that we won't get an out of memory error anyway, * even on a valid file. */ if (fstat(fd, &stat)) { close(fd); ereport(WARNING, (errcode_for_file_access(), errmsg("could not stat two-phase state file \"%s\": %m", path))); return NULL; } if (stat.st_size < (MAXALIGN(sizeof(TwoPhaseFileHeader)) + MAXALIGN(sizeof(TwoPhaseRecordOnDisk)) + sizeof(pg_crc32)) || stat.st_size > MaxAllocSize) { close(fd); return NULL; } crc_offset = stat.st_size - sizeof(pg_crc32); if (crc_offset != MAXALIGN(crc_offset)) { close(fd); return NULL; } /* * OK, slurp in the file. */ buf = (char *) palloc(stat.st_size); if (read(fd, buf, stat.st_size) != stat.st_size) { close(fd); ereport(WARNING, (errcode_for_file_access(), errmsg("could not read two-phase state file \"%s\": %m", path))); pfree(buf); return NULL; } close(fd); hdr = (TwoPhaseFileHeader *) buf; if (hdr->magic != TWOPHASE_MAGIC || hdr->total_len != stat.st_size) { pfree(buf); return NULL; } INIT_CRC32(calc_crc); COMP_CRC32(calc_crc, buf, crc_offset); FIN_CRC32(calc_crc); file_crc = *((pg_crc32 *) (buf + crc_offset)); if (!EQ_CRC32(calc_crc, file_crc)) { pfree(buf); return NULL; } return buf; }
/* * load_relmap_file -- load data from the shared or local map file * * Because the map file is essential for access to core system catalogs, * failure to read it is a fatal error. * * Note that the local case requires DatabasePath to be set up. */ static void load_relmap_file(bool shared) { RelMapFile *map; char mapfilename[MAXPGPATH]; pg_crc32 crc; int fd; if (shared) { snprintf(mapfilename, sizeof(mapfilename), "global/%s", RELMAPPER_FILENAME); map = &shared_map; } else { snprintf(mapfilename, sizeof(mapfilename), "%s/%s", DatabasePath, RELMAPPER_FILENAME); map = &local_map; } /* Read data ... */ fd = OpenTransientFile(mapfilename, O_RDONLY | PG_BINARY, S_IRUSR | S_IWUSR); if (fd < 0) ereport(FATAL, (errcode_for_file_access(), errmsg("could not open relation mapping file \"%s\": %m", mapfilename))); /* * Note: we could take RelationMappingLock in shared mode here, but it * seems unnecessary since our read() should be atomic against any * concurrent updater's write(). If the file is updated shortly after we * look, the sinval signaling mechanism will make us re-read it before we * are able to access any relation that's affected by the change. */ if (read(fd, map, sizeof(RelMapFile)) != sizeof(RelMapFile)) ereport(FATAL, (errcode_for_file_access(), errmsg("could not read relation mapping file \"%s\": %m", mapfilename))); CloseTransientFile(fd); /* check for correct magic number, etc */ if (map->magic != RELMAPPER_FILEMAGIC || map->num_mappings < 0 || map->num_mappings > MAX_MAPPINGS) ereport(FATAL, (errmsg("relation mapping file \"%s\" contains invalid data", mapfilename))); /* verify the CRC */ INIT_CRC32(crc); COMP_CRC32(crc, (char *) map, offsetof(RelMapFile, crc)); FIN_CRC32(crc); if (!EQ_CRC32(crc, map->crc)) ereport(FATAL, (errmsg("relation mapping file \"%s\" contains incorrect checksum", mapfilename))); }
/* * CRC-check an XLOG record. We do not believe the contents of an XLOG * record (other than to the minimal extent of computing the amount of * data to read in) until we've checked the CRCs. * * We assume all of the record has been read into memory at *record. */ static bool RecordIsValid(XLogRecord *record, XLogRecPtr recptr) { pg_crc32 crc; int i; uint32 len = record->xl_len; BkpBlock bkpb; char *blk; /* First the rmgr data */ INIT_CRC32(crc); COMP_CRC32(crc, XLogRecGetData(record), len); /* Add in the backup blocks, if any */ blk = (char *) XLogRecGetData(record) + len; for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++) { uint32 blen; if (!(record->xl_info & XLR_SET_BKP_BLOCK(i))) continue; memcpy(&bkpb, blk, sizeof(BkpBlock)); if (bkpb.hole_offset + bkpb.hole_length > BLCKSZ) { printf("incorrect hole size in record at %X/%X\n", recptr.xlogid, recptr.xrecoff); return false; } blen = sizeof(BkpBlock) + BLCKSZ - bkpb.hole_length; COMP_CRC32(crc, blk, blen); blk += blen; } /* skip total xl_tot_len check if physical log has been removed. */ #if PG_VERSION_NUM < 80300 || PG_VERSION_NUM >= 90200 if (record->xl_info & XLR_BKP_BLOCK_MASK) #else if (!(record->xl_info & XLR_BKP_REMOVABLE) || record->xl_info & XLR_BKP_BLOCK_MASK) #endif { /* Check that xl_tot_len agrees with our calculation */ if (blk != (char *) record + record->xl_tot_len) { printf("incorrect total length in record at %X/%X\n", recptr.xlogid, recptr.xrecoff); return false; } } /* Finally include the record header */ COMP_CRC32(crc, (char *) record + sizeof(pg_crc32), SizeOfXLogRecord - sizeof(pg_crc32)); FIN_CRC32(crc); if (!EQ_CRC32(record->xl_crc, crc)) { printf("incorrect resource manager data checksum in record at %X/%X\n", recptr.xlogid, recptr.xrecoff); return false; } return true; }
/* * Try to read the existing pg_control file. * * This routine is also responsible for updating old pg_control versions * to the current format. (Currently we don't do anything of the sort.) */ static bool ReadControlFile(void) { int fd; int len; char *buffer; pg_crc32 crc; if ((fd = open(XLOG_CONTROL_FILE, O_RDONLY | PG_BINARY, 0)) < 0) { /* * If pg_control is not there at all, or we can't read it, the odds * are we've been handed a bad DataDir path, so give up. User can do * "touch pg_control" to force us to proceed. */ fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, XLOG_CONTROL_FILE, strerror(errno)); if (errno == ENOENT) fprintf(stderr, _("If you are sure the data directory path is correct, execute\n" " touch %s\n" "and try again.\n"), XLOG_CONTROL_FILE); exit(1); } /* Use malloc to ensure we have a maxaligned buffer */ buffer = (char *) pg_malloc(PG_CONTROL_SIZE); len = read(fd, buffer, PG_CONTROL_SIZE); if (len < 0) { fprintf(stderr, _("%s: could not read file \"%s\": %s\n"), progname, XLOG_CONTROL_FILE, strerror(errno)); exit(1); } close(fd); if (len >= sizeof(ControlFileData) && ((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION) { /* Check the CRC. */ INIT_CRC32(crc); COMP_CRC32(crc, buffer, offsetof(ControlFileData, crc)); FIN_CRC32(crc); if (EQ_CRC32(crc, ((ControlFileData *) buffer)->crc)) { /* Valid data... */ memcpy(&ControlFile, buffer, sizeof(ControlFile)); return true; } fprintf(stderr, _("%s: pg_control exists but has invalid CRC; proceed with caution\n"), progname); /* We will use the data anyway, but treat it as guessed. */ memcpy(&ControlFile, buffer, sizeof(ControlFile)); guessed = true; return true; } /* Looks like it's a mess. */ fprintf(stderr, _("%s: pg_control exists but is broken or unknown version; ignoring it\n"), progname); return false; }
/* * Load a single slot from disk into memory. */ static void RestoreSlotFromDisk(const char *name) { ReplicationSlotOnDisk cp; int i; char path[MAXPGPATH]; int fd; bool restored = false; int readBytes; pg_crc32 checksum; /* no need to lock here, no concurrent access allowed yet */ /* delete temp file if it exists */ sprintf(path, "pg_replslot/%s/state.tmp", name); if (unlink(path) < 0 && errno != ENOENT) ereport(PANIC, (errcode_for_file_access(), errmsg("could not unlink file \"%s\": %m", path))); sprintf(path, "pg_replslot/%s/state", name); elog(DEBUG1, "restoring replication slot from \"%s\"", path); fd = OpenTransientFile(path, O_RDONLY | PG_BINARY, 0); /* * We do not need to handle this as we are rename()ing the directory into * place only after we fsync()ed the state file. */ if (fd < 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", path))); /* * Sync state file before we're reading from it. We might have crashed * while it wasn't synced yet and we shouldn't continue on that basis. */ if (pg_fsync(fd) != 0) { CloseTransientFile(fd); ereport(PANIC, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", path))); } /* Also sync the parent directory */ START_CRIT_SECTION(); fsync_fname(path, true); END_CRIT_SECTION(); /* read part of statefile that's guaranteed to be version independent */ readBytes = read(fd, &cp, ReplicationSlotOnDiskConstantSize); if (readBytes != ReplicationSlotOnDiskConstantSize) { int saved_errno = errno; CloseTransientFile(fd); errno = saved_errno; ereport(PANIC, (errcode_for_file_access(), errmsg("could not read file \"%s\", read %d of %u: %m", path, readBytes, (uint32) ReplicationSlotOnDiskConstantSize))); } /* verify magic */ if (cp.magic != SLOT_MAGIC) ereport(PANIC, (errcode_for_file_access(), errmsg("replication slot file \"%s\" has wrong magic %u instead of %u", path, cp.magic, SLOT_MAGIC))); /* verify version */ if (cp.version != SLOT_VERSION) ereport(PANIC, (errcode_for_file_access(), errmsg("replication slot file \"%s\" has unsupported version %u", path, cp.version))); /* boundary check on length */ if (cp.length != ReplicationSlotOnDiskDynamicSize) ereport(PANIC, (errcode_for_file_access(), errmsg("replication slot file \"%s\" has corrupted length %u", path, cp.length))); /* Now that we know the size, read the entire file */ readBytes = read(fd, (char *)&cp + ReplicationSlotOnDiskConstantSize, cp.length); if (readBytes != cp.length) { int saved_errno = errno; CloseTransientFile(fd); errno = saved_errno; ereport(PANIC, (errcode_for_file_access(), errmsg("could not read file \"%s\", read %d of %u: %m", path, readBytes, cp.length))); } CloseTransientFile(fd); /* now verify the CRC32 */ INIT_CRC32(checksum); COMP_CRC32(checksum, (char *)&cp + ReplicationSlotOnDiskConstantSize, ReplicationSlotOnDiskDynamicSize); if (!EQ_CRC32(checksum, cp.checksum)) ereport(PANIC, (errmsg("replication slot file %s: checksum mismatch, is %u, should be %u", path, checksum, cp.checksum))); /* nothing can be active yet, don't lock anything */ for (i = 0; i < max_replication_slots; i++) { ReplicationSlot *slot; slot = &ReplicationSlotCtl->replication_slots[i]; if (slot->in_use) continue; /* restore the entire set of persistent data */ memcpy(&slot->data, &cp.slotdata, sizeof(ReplicationSlotPersistentData)); /* initialize in memory state */ slot->effective_xmin = cp.slotdata.xmin; slot->in_use = true; slot->active = false; restored = true; break; } if (!restored) ereport(PANIC, (errmsg("too many replication slots active before shutdown"), errhint("Increase max_replication_slots and try again."))); }