pg_probackup
583 строки · 13.9 Кб
1/*-------------------------------------------------------------------------
2*
3* util.c: log messages to log file or stderr, and misc code.
4*
5* Portions Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
6* Portions Copyright (c) 2015-2021, Postgres Professional
7*
8*-------------------------------------------------------------------------
9*/
10
11#include "pg_probackup.h"12
13#include <time.h>14
15#include <unistd.h>16
17#include <sys/stat.h>18
19static const char *statusName[] =20{
21"UNKNOWN",22"OK",23"ERROR",24"RUNNING",25"MERGING",26"MERGED",27"DELETING",28"DELETED",29"DONE",30"ORPHAN",31"CORRUPT"32};33
34const char *35base36enc_to(long unsigned int value, char buf[ARG_SIZE_HINT base36bufsize])36{
37const char base36[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";38char buffer[base36bufsize];39char *p;40
41p = &buffer[sizeof(buffer)-1];42*p = '\0';43do {44*(--p) = base36[value % 36];45} while (value /= 36);46
47/* I know, it doesn't look safe */48strncpy(buf, p, base36bufsize);49
50return buf;51}
52
53long unsigned int54base36dec(const char *text)55{
56return strtoul(text, NULL, 36);57}
58
59static void60checkControlFile(ControlFileData *ControlFile)61{
62pg_crc32c crc;63
64/* Calculate CRC */65INIT_CRC32C(crc);66COMP_CRC32C(crc, (char *) ControlFile, offsetof(ControlFileData, crc));67FIN_CRC32C(crc);68
69/* Then compare it */70if (!EQ_CRC32C(crc, ControlFile->crc))71elog(ERROR, "Calculated CRC checksum does not match value stored in file.\n"72"Either the file is corrupt, or it has a different layout than this program\n"73"is expecting. The results below are untrustworthy.");74
75if ((ControlFile->pg_control_version % 65536 == 0 || ControlFile->pg_control_version % 65536 > 10000) &&76ControlFile->pg_control_version / 65536 != 0)77elog(ERROR, "Possible byte ordering mismatch\n"78"The byte ordering used to store the pg_control file might not match the one\n"79"used by this program. In that case the results below would be incorrect, and\n"80"the PostgreSQL installation would be incompatible with this data directory.");81}
82
83/*
84* Verify control file contents in the buffer src, and copy it to *ControlFile.
85*/
86static void87digestControlFile(ControlFileData *ControlFile, char *src, size_t size)88{
89#if PG_VERSION_NUM >= 10000090int ControlFileSize = PG_CONTROL_FILE_SIZE;91#else92int ControlFileSize = PG_CONTROL_SIZE;93#endif94
95if (size != ControlFileSize)96elog(ERROR, "Unexpected control file size %d, expected %d",97(int) size, ControlFileSize);98
99memcpy(ControlFile, src, sizeof(ControlFileData));100
101/* Additional checks on control file */102checkControlFile(ControlFile);103}
104
105/*
106* Write ControlFile to pg_control
107*/
108static void109writeControlFile(ControlFileData *ControlFile, const char *path, fio_location location)110{
111int fd;112char *buffer = NULL;113
114#if PG_VERSION_NUM >= 100000115int ControlFileSize = PG_CONTROL_FILE_SIZE;116#else117int ControlFileSize = PG_CONTROL_SIZE;118#endif119
120/* copy controlFileSize */121buffer = pg_malloc0(ControlFileSize);122memcpy(buffer, ControlFile, sizeof(ControlFileData));123
124/* Write pg_control */125fd = fio_open(path,126O_RDWR | O_CREAT | O_TRUNC | PG_BINARY, location);127
128if (fd < 0)129elog(ERROR, "Failed to open file: %s", path);130
131if (fio_write(fd, buffer, ControlFileSize) != ControlFileSize)132elog(ERROR, "Failed to overwrite file: %s", path);133
134if (fio_flush(fd) != 0)135elog(ERROR, "Failed to sync file: %s", path);136
137fio_close(fd);138pg_free(buffer);139}
140
141/*
142* Utility shared by backup and restore to fetch the current timeline
143* used by a node.
144*/
145TimeLineID
146get_current_timeline(PGconn *conn)147{
148
149PGresult *res;150TimeLineID tli = 0;151char *val;152
153res = pgut_execute_extended(conn,154"SELECT timeline_id FROM pg_catalog.pg_control_checkpoint()", 0, NULL, true, true);155
156if (PQresultStatus(res) == PGRES_TUPLES_OK)157val = PQgetvalue(res, 0, 0);158else159return get_current_timeline_from_control(instance_config.pgdata, FIO_DB_HOST, false);160
161if (!parse_uint32(val, &tli, 0))162{163PQclear(res);164elog(WARNING, "Invalid value of timeline_id %s", val);165
166/* TODO 3.0 remove it and just error out */167return get_current_timeline_from_control(instance_config.pgdata, FIO_DB_HOST, false);168}169
170return tli;171}
172
173/* Get timeline from pg_control file */
174TimeLineID
175get_current_timeline_from_control(const char *pgdata_path, fio_location location, bool safe)176{
177ControlFileData ControlFile;178char *buffer;179size_t size;180
181/* First fetch file... */182buffer = slurpFile(pgdata_path, XLOG_CONTROL_FILE, &size,183safe, location);184if (safe && buffer == NULL)185return 0;186
187digestControlFile(&ControlFile, buffer, size);188pg_free(buffer);189
190return ControlFile.checkPointCopy.ThisTimeLineID;191}
192
193/*
194* Get last check point record ptr from pg_tonrol.
195*/
196XLogRecPtr
197get_checkpoint_location(PGconn *conn)198{
199#if PG_VERSION_NUM >= 90600200PGresult *res;201uint32 lsn_hi;202uint32 lsn_lo;203XLogRecPtr lsn;204
205#if PG_VERSION_NUM >= 100000206res = pgut_execute(conn,207"SELECT checkpoint_lsn FROM pg_catalog.pg_control_checkpoint()",2080, NULL);209#else210res = pgut_execute(conn,211"SELECT checkpoint_location FROM pg_catalog.pg_control_checkpoint()",2120, NULL);213#endif214XLogDataFromLSN(PQgetvalue(res, 0, 0), &lsn_hi, &lsn_lo);215PQclear(res);216/* Calculate LSN */217lsn = ((uint64) lsn_hi) << 32 | lsn_lo;218
219return lsn;220#else221char *buffer;222size_t size;223ControlFileData ControlFile;224
225buffer = slurpFile(instance_config.pgdata, XLOG_CONTROL_FILE, &size, false, FIO_DB_HOST);226digestControlFile(&ControlFile, buffer, size);227pg_free(buffer);228
229return ControlFile.checkPoint;230#endif231}
232
233uint64
234get_system_identifier(const char *pgdata_path, fio_location location, bool safe)235{
236ControlFileData ControlFile;237char *buffer;238size_t size;239
240/* First fetch file... */241buffer = slurpFile(pgdata_path, XLOG_CONTROL_FILE, &size, safe, location);242if (safe && buffer == NULL)243return 0;244digestControlFile(&ControlFile, buffer, size);245pg_free(buffer);246
247return ControlFile.system_identifier;248}
249
250uint64
251get_remote_system_identifier(PGconn *conn)252{
253#if PG_VERSION_NUM >= 90600254PGresult *res;255uint64 system_id_conn;256char *val;257
258res = pgut_execute(conn,259"SELECT system_identifier FROM pg_catalog.pg_control_system()",2600, NULL);261val = PQgetvalue(res, 0, 0);262if (!parse_uint64(val, &system_id_conn, 0))263{264PQclear(res);265elog(ERROR, "%s is not system_identifier", val);266}267PQclear(res);268
269return system_id_conn;270#else271char *buffer;272size_t size;273ControlFileData ControlFile;274
275buffer = slurpFile(instance_config.pgdata, XLOG_CONTROL_FILE, &size, false, FIO_DB_HOST);276digestControlFile(&ControlFile, buffer, size);277pg_free(buffer);278
279return ControlFile.system_identifier;280#endif281}
282
283uint32
284get_xlog_seg_size(const char *pgdata_path)285{
286#if PG_VERSION_NUM >= 110000287ControlFileData ControlFile;288char *buffer;289size_t size;290
291/* First fetch file... */292buffer = slurpFile(pgdata_path, XLOG_CONTROL_FILE, &size, false, FIO_DB_HOST);293digestControlFile(&ControlFile, buffer, size);294pg_free(buffer);295
296return ControlFile.xlog_seg_size;297#else298return (uint32) XLOG_SEG_SIZE;299#endif300}
301
302uint32
303get_data_checksum_version(bool safe)304{
305ControlFileData ControlFile;306char *buffer;307size_t size;308
309/* First fetch file... */310buffer = slurpFile(instance_config.pgdata, XLOG_CONTROL_FILE, &size,311safe, FIO_DB_HOST);312if (buffer == NULL)313return 0;314digestControlFile(&ControlFile, buffer, size);315pg_free(buffer);316
317return ControlFile.data_checksum_version;318}
319
320pg_crc32c
321get_pgcontrol_checksum(const char *pgdata_path)322{
323ControlFileData ControlFile;324char *buffer;325size_t size;326
327/* First fetch file... */328buffer = slurpFile(pgdata_path, XLOG_CONTROL_FILE, &size, false, FIO_BACKUP_HOST);329
330digestControlFile(&ControlFile, buffer, size);331pg_free(buffer);332
333return ControlFile.crc;334}
335
336void
337get_redo(const char *pgdata_path, fio_location pgdata_location, RedoParams *redo)338{
339ControlFileData ControlFile;340char *buffer;341size_t size;342
343/* First fetch file... */344buffer = slurpFile(pgdata_path, XLOG_CONTROL_FILE, &size, false, pgdata_location);345
346digestControlFile(&ControlFile, buffer, size);347pg_free(buffer);348
349redo->lsn = ControlFile.checkPointCopy.redo;350redo->tli = ControlFile.checkPointCopy.ThisTimeLineID;351
352if (ControlFile.minRecoveryPoint > 0 &&353ControlFile.minRecoveryPoint < redo->lsn)354{355redo->lsn = ControlFile.minRecoveryPoint;356redo->tli = ControlFile.minRecoveryPointTLI;357}358
359if (ControlFile.backupStartPoint > 0 &&360ControlFile.backupStartPoint < redo->lsn)361{362redo->lsn = ControlFile.backupStartPoint;363redo->tli = ControlFile.checkPointCopy.ThisTimeLineID;364}365
366redo->checksum_version = ControlFile.data_checksum_version;367}
368
369/*
370* Rewrite minRecoveryPoint of pg_control in backup directory. minRecoveryPoint
371* 'as-is' is not to be trusted.
372*/
373void
374set_min_recovery_point(pgFile *file, const char *backup_path,375XLogRecPtr stop_backup_lsn)376{
377ControlFileData ControlFile;378char *buffer;379size_t size;380char fullpath[MAXPGPATH];381
382/* First fetch file content */383buffer = slurpFile(instance_config.pgdata, XLOG_CONTROL_FILE, &size, false, FIO_DB_HOST);384digestControlFile(&ControlFile, buffer, size);385
386elog(LOG, "Current minRecPoint %X/%X",387(uint32) (ControlFile.minRecoveryPoint >> 32),388(uint32) ControlFile.minRecoveryPoint);389
390elog(LOG, "Setting minRecPoint to %X/%X",391(uint32) (stop_backup_lsn >> 32),392(uint32) stop_backup_lsn);393
394ControlFile.minRecoveryPoint = stop_backup_lsn;395
396/* Update checksum in pg_control header */397INIT_CRC32C(ControlFile.crc);398COMP_CRC32C(ControlFile.crc, (char *) &ControlFile,399offsetof(ControlFileData, crc));400FIN_CRC32C(ControlFile.crc);401
402/* overwrite pg_control */403join_path_components(fullpath, backup_path, XLOG_CONTROL_FILE);404writeControlFile(&ControlFile, fullpath, FIO_LOCAL_HOST);405
406/* Update pg_control checksum in backup_list */407file->crc = ControlFile.crc;408
409pg_free(buffer);410}
411
412/*
413* Copy pg_control file to backup. We do not apply compression to this file.
414*/
415void
416copy_pgcontrol_file(const char *from_fullpath, fio_location from_location,417const char *to_fullpath, fio_location to_location, pgFile *file)418{
419ControlFileData ControlFile;420char *buffer;421size_t size;422
423buffer = slurpFile(from_fullpath, "", &size, false, from_location);424
425digestControlFile(&ControlFile, buffer, size);426
427file->crc = ControlFile.crc;428file->read_size = size;429file->write_size = size;430file->uncompressed_size = size;431
432writeControlFile(&ControlFile, to_fullpath, to_location);433
434pg_free(buffer);435}
436
437/*
438* Parse string representation of the server version.
439*/
440uint32
441parse_server_version(const char *server_version_str)442{
443int nfields;444uint32 result = 0;445int major_version = 0;446int minor_version = 0;447
448nfields = sscanf(server_version_str, "%d.%d", &major_version, &minor_version);449if (nfields == 2)450{451/* Server version lower than 10 */452if (major_version > 10)453elog(ERROR, "Server version format doesn't match major version %d", major_version);454result = major_version * 10000 + minor_version * 100;455}456else if (nfields == 1)457{458if (major_version < 10)459elog(ERROR, "Server version format doesn't match major version %d", major_version);460result = major_version * 10000;461}462else463elog(ERROR, "Unknown server version format %s", server_version_str);464
465return result;466}
467
468/*
469* Parse string representation of the program version.
470*/
471uint32
472parse_program_version(const char *program_version)473{
474int nfields;475int major = 0,476minor = 0,477micro = 0;478uint32 result = 0;479
480if (program_version == NULL || program_version[0] == '\0')481return 0;482
483nfields = sscanf(program_version, "%d.%d.%d", &major, &minor, µ);484if (nfields == 3)485result = major * 10000 + minor * 100 + micro;486else487elog(ERROR, "Unknown program version format %s", program_version);488
489return result;490}
491
492const char *493status2str(BackupStatus status)494{
495if (status < BACKUP_STATUS_INVALID || BACKUP_STATUS_CORRUPT < status)496return "UNKNOWN";497
498return statusName[status];499}
500
501const char *502status2str_color(BackupStatus status)503{
504char *status_str = pgut_malloc(20);505
506/* UNKNOWN */507if (status == BACKUP_STATUS_INVALID)508snprintf(status_str, 20, "%s%s%s", TC_YELLOW_BOLD, "UNKNOWN", TC_RESET);509/* CORRUPT, ERROR and ORPHAN */510else if (status == BACKUP_STATUS_CORRUPT || status == BACKUP_STATUS_ERROR ||511status == BACKUP_STATUS_ORPHAN)512snprintf(status_str, 20, "%s%s%s", TC_RED_BOLD, statusName[status], TC_RESET);513/* MERGING, MERGED, DELETING and DELETED */514else if (status == BACKUP_STATUS_MERGING || status == BACKUP_STATUS_MERGED ||515status == BACKUP_STATUS_DELETING || status == BACKUP_STATUS_DELETED)516snprintf(status_str, 20, "%s%s%s", TC_YELLOW_BOLD, statusName[status], TC_RESET);517/* OK and DONE */518else519snprintf(status_str, 20, "%s%s%s", TC_GREEN_BOLD, statusName[status], TC_RESET);520
521return status_str;522}
523
524BackupStatus
525str2status(const char *status)526{
527BackupStatus i;528
529for (i = BACKUP_STATUS_INVALID; i <= BACKUP_STATUS_CORRUPT; i++)530{531if (pg_strcasecmp(status, statusName[i]) == 0) return i;532}533
534return BACKUP_STATUS_INVALID;535}
536
537bool
538datapagemap_is_set(datapagemap_t *map, BlockNumber blkno)539{
540int offset;541int bitno;542
543offset = blkno / 8;544bitno = blkno % 8;545
546return (map->bitmapsize <= offset) ? false : (map->bitmap[offset] & (1 << bitno)) != 0;547}
548
549/*
550* A debugging aid. Prints out the contents of the page map.
551*/
552void
553datapagemap_print_debug(datapagemap_t *map)554{
555datapagemap_iterator_t *iter;556BlockNumber blocknum;557
558iter = datapagemap_iterate(map);559while (datapagemap_next(iter, &blocknum))560elog(VERBOSE, " block %u", blocknum);561
562pg_free(iter);563}
564
565const char*566backup_id_of(pgBackup *backup)567{
568/* Change this Assert when backup_id will not be bound to start_time */569Assert(backup->backup_id == backup->start_time || backup->start_time == 0);570
571if (backup->backup_id_encoded[0] == '\x00')572{573base36enc_to(backup->backup_id, backup->backup_id_encoded);574}575return backup->backup_id_encoded;576}
577
578void
579reset_backup_id(pgBackup *backup)580{
581backup->backup_id = INVALID_BACKUP_ID;582memset(backup->backup_id_encoded, 0, sizeof(backup->backup_id_encoded));583}
584