mirror of
https://github.com/postgrespro/pg_probackup.git
synced 2024-12-13 11:53:59 +02:00
596ac3a37b
System identifier is fetched via pg_read_binary_file() for Pg 9.5.
301 lines
6.5 KiB
C
301 lines
6.5 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* util.c: log messages to log file or stderr, and misc code.
|
|
*
|
|
* Portions Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
|
* Portions Copyright (c) 2015-2017, Postgres Professional
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "pg_probackup.h"
|
|
|
|
#include <time.h>
|
|
|
|
#include "storage/bufpage.h"
|
|
|
|
char *
|
|
base36enc(long unsigned int value)
|
|
{
|
|
char base36[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
/* log(2**64) / log(36) = 12.38 => max 13 char + '\0' */
|
|
char buffer[14];
|
|
unsigned int offset = sizeof(buffer);
|
|
|
|
buffer[--offset] = '\0';
|
|
do {
|
|
buffer[--offset] = base36[value % 36];
|
|
} while (value /= 36);
|
|
|
|
return strdup(&buffer[offset]); /* warning: this must be free-d by the user */
|
|
}
|
|
|
|
long unsigned int
|
|
base36dec(const char *text)
|
|
{
|
|
return strtoul(text, NULL, 36);
|
|
}
|
|
|
|
static void
|
|
checkControlFile(ControlFileData *ControlFile)
|
|
{
|
|
pg_crc32c crc;
|
|
|
|
/* Calculate CRC */
|
|
INIT_CRC32C(crc);
|
|
COMP_CRC32C(crc, (char *) ControlFile, offsetof(ControlFileData, crc));
|
|
FIN_CRC32C(crc);
|
|
|
|
/* Then compare it */
|
|
if (!EQ_CRC32C(crc, ControlFile->crc))
|
|
elog(ERROR, "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.");
|
|
|
|
if (ControlFile->pg_control_version % 65536 == 0 && ControlFile->pg_control_version / 65536 != 0)
|
|
elog(ERROR, "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.");
|
|
}
|
|
|
|
/*
|
|
* Verify control file contents in the buffer src, and copy it to *ControlFile.
|
|
*/
|
|
static void
|
|
digestControlFile(ControlFileData *ControlFile, char *src, size_t size)
|
|
{
|
|
#if PG_VERSION_NUM >= 100000
|
|
int ControlFileSize = PG_CONTROL_FILE_SIZE;
|
|
#else
|
|
int ControlFileSize = PG_CONTROL_SIZE;
|
|
#endif
|
|
|
|
if (size != ControlFileSize)
|
|
elog(ERROR, "unexpected control file size %d, expected %d",
|
|
(int) size, ControlFileSize);
|
|
|
|
memcpy(ControlFile, src, sizeof(ControlFileData));
|
|
|
|
/* Additional checks on control file */
|
|
checkControlFile(ControlFile);
|
|
}
|
|
|
|
/*
|
|
* Utility shared by backup and restore to fetch the current timeline
|
|
* used by a node.
|
|
*/
|
|
TimeLineID
|
|
get_current_timeline(bool safe)
|
|
{
|
|
ControlFileData ControlFile;
|
|
char *buffer;
|
|
size_t size;
|
|
|
|
/* First fetch file... */
|
|
buffer = slurpFile(pgdata, "global/pg_control", &size, safe);
|
|
if (safe && buffer == NULL)
|
|
return 0;
|
|
|
|
digestControlFile(&ControlFile, buffer, size);
|
|
pg_free(buffer);
|
|
|
|
return ControlFile.checkPointCopy.ThisTimeLineID;
|
|
}
|
|
|
|
uint64
|
|
get_system_identifier(char *pgdata_path)
|
|
{
|
|
ControlFileData ControlFile;
|
|
char *buffer;
|
|
size_t size;
|
|
|
|
/* First fetch file... */
|
|
buffer = slurpFile(pgdata_path, "global/pg_control", &size, false);
|
|
if (buffer == NULL)
|
|
return 0;
|
|
digestControlFile(&ControlFile, buffer, size);
|
|
pg_free(buffer);
|
|
|
|
return ControlFile.system_identifier;
|
|
}
|
|
|
|
uint64
|
|
get_remote_system_identifier(PGconn *conn)
|
|
{
|
|
#if PG_VERSION_NUM >= 90600
|
|
PGresult *res;
|
|
uint64 system_id_conn;
|
|
char *val;
|
|
|
|
res = pgut_execute(conn,
|
|
"SELECT system_identifier FROM pg_control_system()",
|
|
0, NULL, true);
|
|
val = PQgetvalue(res, 0, 0);
|
|
if (!parse_uint64(val, &system_id_conn, 0))
|
|
{
|
|
PQclear(res);
|
|
elog(ERROR, "%s is not system_identifier", val);
|
|
}
|
|
PQclear(res);
|
|
|
|
return system_id_conn;
|
|
#else
|
|
char *buffer;
|
|
size_t size;
|
|
ControlFileData ControlFile;
|
|
|
|
buffer = fetchFile(conn, "global/pg_control", &size);
|
|
digestControlFile(&ControlFile, buffer, size);
|
|
pg_free(buffer);
|
|
|
|
return ControlFile.system_identifier;
|
|
#endif
|
|
}
|
|
|
|
uint32
|
|
get_data_checksum_version(bool safe)
|
|
{
|
|
ControlFileData ControlFile;
|
|
char *buffer;
|
|
size_t size;
|
|
|
|
/* First fetch file... */
|
|
buffer = slurpFile(pgdata, "global/pg_control", &size, safe);
|
|
if (buffer == NULL)
|
|
return 0;
|
|
digestControlFile(&ControlFile, buffer, size);
|
|
pg_free(buffer);
|
|
|
|
return ControlFile.data_checksum_version;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert time_t value to ISO-8601 format string
|
|
*/
|
|
void
|
|
time2iso(char *buf, size_t len, time_t time)
|
|
{
|
|
struct tm *ptm = gmtime(&time);
|
|
time_t gmt = mktime(ptm);
|
|
time_t offset;
|
|
|
|
ptm = localtime(&time);
|
|
offset = time - gmt + (ptm->tm_isdst ? 3600 : 0);
|
|
|
|
strftime(buf, len, "%Y-%m-%d %H:%M:%S", ptm);
|
|
|
|
if (offset != 0)
|
|
{
|
|
buf += strlen(buf);
|
|
sprintf(buf, "%c%02d",
|
|
(offset >= 0) ? '+' : '-',
|
|
abs((int) offset) / SECS_PER_HOUR);
|
|
|
|
if (abs((int) offset) % SECS_PER_HOUR != 0)
|
|
{
|
|
buf += strlen(buf);
|
|
sprintf(buf, ":%02d",
|
|
abs((int) offset % SECS_PER_HOUR) / SECS_PER_MINUTE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* copied from timestamp.c */
|
|
pg_time_t
|
|
timestamptz_to_time_t(TimestampTz t)
|
|
{
|
|
pg_time_t result;
|
|
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
|
result = (pg_time_t) (t / USECS_PER_SEC +
|
|
((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY));
|
|
#else
|
|
result = (pg_time_t) (t +
|
|
((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY));
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
const char *
|
|
status2str(BackupStatus status)
|
|
{
|
|
static const char *statusName[] =
|
|
{
|
|
"UNKNOWN",
|
|
"OK",
|
|
"RUNNING",
|
|
"ERROR",
|
|
"DELETING",
|
|
"DELETED",
|
|
"DONE",
|
|
"CORRUPT"
|
|
};
|
|
|
|
if (status < BACKUP_STATUS_INVALID || BACKUP_STATUS_CORRUPT < status)
|
|
return "UNKNOWN";
|
|
|
|
return statusName[status];
|
|
}
|
|
|
|
void
|
|
remove_trailing_space(char *buf, int comment_mark)
|
|
{
|
|
int i;
|
|
char *last_char = NULL;
|
|
|
|
for (i = 0; buf[i]; i++)
|
|
{
|
|
if (buf[i] == comment_mark || buf[i] == '\n' || buf[i] == '\r')
|
|
{
|
|
buf[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
for (i = 0; buf[i]; i++)
|
|
{
|
|
if (!isspace(buf[i]))
|
|
last_char = buf + i;
|
|
}
|
|
if (last_char != NULL)
|
|
*(last_char + 1) = '\0';
|
|
|
|
}
|
|
|
|
void
|
|
remove_not_digit(char *buf, size_t len, const char *str)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0, j = 0; str[i] && j < len; i++)
|
|
{
|
|
if (!isdigit(str[i]))
|
|
continue;
|
|
buf[j++] = str[i];
|
|
}
|
|
buf[j] = '\0';
|
|
}
|
|
|
|
/* Fill pgBackup struct with default values */
|
|
void
|
|
pgBackup_init(pgBackup *backup)
|
|
{
|
|
backup->backup_id = INVALID_BACKUP_ID;
|
|
backup->backup_mode = BACKUP_MODE_INVALID;
|
|
backup->status = BACKUP_STATUS_INVALID;
|
|
backup->tli = 0;
|
|
backup->start_lsn = 0;
|
|
backup->stop_lsn = 0;
|
|
backup->start_time = (time_t) 0;
|
|
backup->end_time = (time_t) 0;
|
|
backup->recovery_xid = 0;
|
|
backup->recovery_time = (time_t) 0;
|
|
backup->data_bytes = BYTES_INVALID;
|
|
backup->block_size = BLCKSZ;
|
|
backup->wal_block_size = XLOG_BLCKSZ;
|
|
backup->stream = false;
|
|
backup->parent_backup = 0;
|
|
backup->server_version[0] = '\0';
|
|
}
|