Files
openGauss-server/src/bin/pg_ctl/pg_build.cpp
2023-07-18 16:42:52 +08:00

1951 lines
64 KiB
C++
Executable File

/* -------------------------------------------------------------------------
*
* pg_build.c -
*
* Author:
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/bin/pg_ctl/pg_build.c
* -------------------------------------------------------------------------
*/
#include <dirent.h>
#include "funcapi.h"
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include "backup.h"
#include "postgres.h"
#include "knl/knl_variable.h"
#include "pg_build.h"
#include "streamutil.h"
#include "logging.h"
#include "tool_common.h"
#include "bin/elog.h"
#include "nodes/pg_list.h"
#include "replication/replicainternal.h"
#include "storage/smgr/fd.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
#include "common/fe_memutils.h"
#include "libpq/libpq-fe.h"
#include "libpq/libpq-int.h"
#include "storage/file/fio_device.h"
/* global variables for con */
char conninfo_global[MAX_REPLNODE_NUM][MAX_VALUE_LEN] = {{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}};
static const char* config_para_build[MAX_REPLNODE_NUM] = {"",
"replconninfo1",
"replconninfo2",
"replconninfo3",
"replconninfo4",
"replconninfo5",
"replconninfo6",
"replconninfo7",
"replconninfo8"};
static const char* config_para_cross_cluster_build[MAX_REPLNODE_NUM] = {
"",
"cross_cluster_replconninfo1",
"cross_cluster_replconninfo2",
"cross_cluster_replconninfo3",
"cross_cluster_replconninfo4",
"cross_cluster_replconninfo5",
"cross_cluster_replconninfo6",
"cross_cluster_replconninfo7",
"cross_cluster_replconninfo8"
};
/* Node name */
char pgxcnodename[MAX_VALUE_LEN] = {0};
char config_cascade_standby[MAXPGPATH] = {0};
bool cascade_standby = false;
int replconn_num = 0;
int conn_flag = 0;
char g_buildapplication_name[MAX_VALUE_LEN] = {0};
char g_buildprimary_slotname[MAX_VALUE_LEN] = {0};
char g_str_replication_type[MAX_VALUE_LEN] = {0};
char g_repl_auth_mode[MAX_VALUE_LEN] = {0};
char g_repl_uuid[MAX_VALUE_LEN] = {0};
int g_replconn_idx = -1;
int g_replication_type = -1;
bool is_cross_region_build = false;
#define RT_WITH_DUMMY_STANDBY 0
#define RT_WITH_MULTI_STANDBY 1
static void walkdir(const char *path, int (*action) (const char *fname, bool isdir), bool process_symlinks);
static void check_repl_uuid(char *repl_uuid)
{
#define IsAlNum(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') || ((c) >= '0' && (c) <= '9'))
if (repl_uuid == NULL) {
return;
}
int ptr = 0;
if (strlen(repl_uuid) >= NAMEDATALEN) {
pg_log(PG_PRINT, _("Max repl_uuid string length is 63.\n"));
exit(1);
}
while (repl_uuid[ptr] != '\0') {
if (!IsAlNum(repl_uuid[ptr])) {
pg_log(PG_PRINT, _("repl_uuid only accepts alphabetic or digital character, case insensitive.\n"));
exit(1);
}
repl_uuid[ptr] = tolower(repl_uuid[ptr]);
ptr++;
}
return;
}
int32 pg_atoi(const char* s, int size, int c)
{
long l;
char* badp = NULL;
/*
* Some versions of strtol treat the empty string as an error, but some
* seem not to. Make an explicit test to be sure we catch it.
*/
if (s == NULL || *s == 0) {
exit(1);
}
errno = 0;
l = strtol(s, &badp, 10);
/* We made no progress parsing the string, so bail out */
if (s == badp) {
exit(1);
}
switch (size) {
case sizeof(int32):
if (errno == ERANGE
#if defined(HAVE_LONG_INT_64)
/* won't get ERANGE on these with 64-bit longs... */
|| l < INT_MIN || l > INT_MAX
#endif
)
exit(1);
break;
case sizeof(int16):
if (errno == ERANGE || l < SHRT_MIN || l > SHRT_MAX)
exit(1);
break;
case sizeof(int8):
if (errno == ERANGE || l < SCHAR_MIN || l > SCHAR_MAX)
exit(1);
break;
default:
exit(1);
}
/*
* Skip any trailing whitespace; if anything but whitespace remains before
* the terminating character, bail out
*/
while (*badp && *badp != c && isspace((unsigned char)*badp)) {
badp++;
}
if (*badp && *badp != c) {
exit(1);
}
return (int32)l;
}
/*
* get the lines from a text file - return NULL if file can't be opened
*/
char** readfile(const char* path)
{
int fd = -1;
int nlines;
char** result = NULL;
char* buffer = NULL;
char* linebegin = NULL;
int i = 0;
int n = 0;
int len = 0;
struct stat statbuf;
errno_t rc = 0;
/*
* Slurp the file into memory.
*
* The file can change concurrently, so we read the whole file into memory
* with a single read() call. That's not guaranteed to get an atomic
* snapshot, but in practice, for a small file, it's close enough for the
* current use.
*/
fd = open(path, O_RDONLY | PG_BINARY, 0);
if (fd < 0) {
return NULL;
}
if (fstat(fd, &statbuf) < 0) {
close(fd);
fd = -1;
return NULL;
}
if (statbuf.st_size == 0) {
/* empty file */
close(fd);
result = (char**)pg_malloc(sizeof(char*));
*result = NULL;
return result;
}
if (statbuf.st_size + 1 > MAX_CONFIG_FILE_SIZE) {
/* empty file */
close(fd);
result = (char**)pg_malloc(sizeof(char*));
*result = NULL;
return result;
}
buffer = (char*)pg_malloc(statbuf.st_size + 1);
len = read(fd, buffer, statbuf.st_size + 1);
close(fd);
fd = -1;
if (len != statbuf.st_size) {
/* oops, the file size changed between fstat and read */
free(buffer);
buffer = NULL;
return NULL;
}
/*
* Count newlines. We expect there to be a newline after each full line,
* including one at the end of file. If there isn't a newline at the end,
* any characters after the last newline will be ignored.
*/
nlines = 0;
for (i = 0; i < len; i++) {
if (buffer[i] == '\n') {
nlines++;
}
}
/* set up the result buffer */
result = (char**)pg_malloc((nlines + 1) * sizeof(char*));
/* now split the buffer into lines */
linebegin = buffer;
n = 0;
for (i = 0; i < len; i++) {
if (buffer[i] == '\n') {
int slen = &buffer[i] - linebegin + 1;
char* linebuf = (char*)pg_malloc(slen + 1);
rc = memcpy_s(linebuf, (slen + 1), linebegin, slen);
securec_check_c(rc, "", "");
linebuf[slen] = '\0';
result[n++] = linebuf;
linebegin = &buffer[i + 1];
}
}
result[n] = NULL;
free(buffer);
buffer = NULL;
return result;
}
/*
* @@GaussDB@@
* Brief : BuildCheckReplChannel
* Description :
* Notes :
*/
static bool BuildCheckReplChannel(const char* ChannelInfo)
{
char* iter = NULL;
char* ReplStr = NULL;
if (ChannelInfo == NULL) {
return false;
} else {
ReplStr = strdup(ChannelInfo);
if (ReplStr == NULL) {
return false;
} else {
iter = strstr(ReplStr, "localhost");
if (iter == NULL) {
free(ReplStr);
ReplStr = NULL;
return false;
}
iter += strlen("localhost");
while (*iter == ' ' || *iter == '=') {
iter++;
}
if ((!isdigit(*iter) && *iter != ':' && !isalpha(*iter)) ||
(0 == strncmp(iter, "localport", strlen("localport")))) {
free(ReplStr);
ReplStr = NULL;
return false;
}
iter = strstr(ReplStr, "localport");
if (iter == NULL) {
free(ReplStr);
ReplStr = NULL;
return false;
}
iter += strlen("localport");
while (*iter == ' ' || *iter == '=') {
iter++;
}
if (!isdigit(*iter)) {
free(ReplStr);
ReplStr = NULL;
return false;
}
iter = strstr(ReplStr, "remotehost");
if (NULL == iter) {
free(ReplStr);
ReplStr = NULL;
return false;
}
iter += strlen("remotehost");
while (' ' == *iter || '=' == *iter) {
iter++;
}
if ((!isdigit(*iter) && *iter != ':' && !isalpha(*iter)) ||
(0 == strncmp(iter, "remoteport", strlen("remoteport")))) {
free(ReplStr);
ReplStr = NULL;
return false;
}
iter = strstr(ReplStr, "remoteport");
if (iter == NULL) {
free(ReplStr);
ReplStr = NULL;
return false;
}
iter += strlen("remoteport");
while (*iter == ' ' || *iter == '=') {
iter++;
}
if (!isdigit(*iter)) {
free(ReplStr);
ReplStr = NULL;
return false;
}
}
}
free(ReplStr);
ReplStr = NULL;
return true;
}
/*
* @@GaussDB@@
* Brief : GetLengthAndCheckReplConn
* Description :
* Notes :
*/
int GetLengthAndCheckReplConn(const char* ConnInfoList)
{
int repl_len = 0;
char* ReplStr = NULL;
char* token = NULL;
char* p = NULL;
if (ConnInfoList == NULL) {
return repl_len;
} else {
ReplStr = strdup(ConnInfoList);
if (ReplStr == NULL) {
return repl_len;
}
token = strtok_r(ReplStr, ",", &p);
while (token != NULL) {
if (BuildCheckReplChannel(token)) {
repl_len++;
}
token = strtok_r(NULL, ",", &p);
}
}
free(ReplStr);
ReplStr = NULL;
return repl_len;
}
/*
* @@GaussDB@@
* Brief : ParseReplConnInfo
* Description :
* Notes :
*/
bool ParseReplConnInfo(const char* ConnInfoList, int* InfoLength, ReplConnInfo* repl)
{
int repl_length = 0;
char* iter = NULL;
char* pNext = NULL;
char* ReplStr = NULL;
char* token = NULL;
char* ptr = NULL;
int parsed = 0;
int iplen = 0;
char tmp_localhost[IP_LEN] = {0};
char cascadeToken[IP_LEN] = {0};
char crossRegionToken[IP_LEN] = {0};
int tmp_localport = 0;
char tmp_remotehost[IP_LEN] = {0};
int tmp_remoteport = 0;
int cascadeLen = strlen("iscascade");
int corssRegionLen = strlen("isCrossRegion");
errno_t rc = EOK;
char* p = NULL;
if (ConnInfoList == NULL) {
return false;
} else {
ReplStr = strdup(ConnInfoList);
if (ReplStr == NULL) {
return false;
}
ptr = ReplStr;
while (*ptr != '\0') {
if (*ptr != ' ') {
break;
}
ptr++;
}
if (*ptr == '\0') {
free(ReplStr);
ReplStr = NULL;
return false;
}
repl_length = GetLengthAndCheckReplConn(ReplStr);
if (repl_length == 0) {
free(ReplStr);
ReplStr = NULL;
return false;
}
rc = memset_s(repl, sizeof(ReplConnInfo), 0, sizeof(ReplConnInfo));
securec_check_c(rc, "", "");
token = strtok_r(ReplStr, ",", &p);
while (token != NULL) {
rc = memset_s(tmp_localhost, IP_LEN, 0, sizeof(tmp_localhost));
securec_check_c(rc, "\0", "\0");
rc = memset_s(tmp_remotehost, IP_LEN, 0, sizeof(tmp_remotehost));
securec_check_c(rc, "\0", "\0");
/* localhost */
iter = strstr(token, "localhost");
if (iter == NULL) {
token = strtok_r(NULL, ",", &p);
continue;
}
iter += strlen("localhost");
while (' ' == *iter || '=' == *iter) {
iter++;
}
if (!isdigit(*iter) && *iter != ':' && !isalpha(*iter)) {
token = strtok_r(NULL, ",", &p);
continue;
}
pNext = iter;
iplen = 0;
while (*pNext != ' ' && 0 != strncmp(pNext, "localport", strlen("localport"))) {
iplen++;
pNext++;
}
rc = strncpy_s(tmp_localhost, IP_LEN, iter, iplen);
securec_check_c(rc, "", "");
tmp_localhost[IP_LEN - 1] = '\0';
/* localport */
iter = strstr(token, "localport");
if (iter == NULL) {
token = strtok_r(NULL, ",", &p);
continue;
}
iter += strlen("localport");
while (*iter == ' ' || *iter == '=') {
iter++;
}
if (!isdigit(*iter)) {
token = strtok_r(NULL, ",", &p);
continue;
}
tmp_localport = atoi(iter);
/* remotehost */
iter = strstr(token, "remotehost");
if (iter == NULL) {
token = strtok_r(NULL, ",", &p);
continue;
}
iter += strlen("remotehost");
while (*iter == ' ' || *iter == '=') {
iter++;
}
if (!isdigit(*iter) && *iter != ':' && !isalpha(*iter)) {
token = strtok_r(NULL, ",", &p);
continue;
}
pNext = iter;
iplen = 0;
while (*pNext != ' ' && 0 != strncmp(pNext, "remoteport", strlen("remoteport"))) {
iplen++;
pNext++;
}
rc = strncpy_s(tmp_remotehost, IP_LEN, iter, iplen);
securec_check_c(rc, "", "");
tmp_remotehost[IP_LEN - 1] = '\0';
/* remoteport */
iter = strstr(token, "remoteport");
if (NULL == iter) {
token = strtok_r(NULL, ",", &p);
continue;
}
iter += strlen("remoteport");
while (*iter == ' ' || *iter == '=') {
iter++;
}
if (!isdigit(*iter)) {
token = strtok_r(NULL, ",", &p);
continue;
}
tmp_remoteport = atoi(iter);
/* is cascade? */
iter = strstr(token, "iscascade");
if (iter != NULL) {
iter += cascadeLen;
while (*iter == ' ' || *iter == '=') {
iter++;
}
rc = strncpy_s(cascadeToken, IP_LEN, iter, strlen("true"));
securec_check_c(rc, "", "");
if (strcmp(cascadeToken, "true") == 0) {
repl->isCascade = true;
}
}
/* is cross region? */
iter = strstr(token, "iscrossregion");
if (iter != NULL) {
iter += corssRegionLen;
while (*iter == ' ' || *iter == '=') {
iter++;
}
rc = strncpy_s(crossRegionToken, IP_LEN, iter, strlen("true"));
securec_check_c(rc, "", "");
if (strcmp(crossRegionToken, "true") == 0) {
repl->isCrossRegion = true;
}
}
/* copy the valus from tmp */
rc = strncpy_s(repl->localhost, IP_LEN, tmp_localhost, IP_LEN - 1);
securec_check_c(rc, "", "");
repl->localhost[IP_LEN - 1] = '\0';
repl->localport = tmp_localport;
rc = strncpy_s(repl->remotehost, IP_LEN, tmp_remotehost, IP_LEN - 1);
securec_check_c(rc, "", "");
repl->remotehost[IP_LEN - 1] = '\0';
repl->remoteport = tmp_remoteport;
token = strtok_r(NULL, ",", &p);
parsed++;
}
}
free(ReplStr);
ReplStr = NULL;
*InfoLength = repl_length;
return true;
}
/*
* Brief : @@GaussDB@@
* Description : get available conn
* Notes :
*/
void get_conninfo(const char* filename)
{
char** optlines;
const char **conninfo_para = NULL;
int lines_index = 0;
int optvalue_off;
int optvalue_len;
int opt_index = 0;
errno_t rc = EOK;
if (filename == NULL) {
pg_log(PG_PRINT, _("filename is NULL"));
exit(1);
}
if (build_mode == CROSS_CLUSTER_FULL_BUILD || build_mode == CROSS_CLUSTER_INC_BUILD ||
build_mode == CROSS_CLUSTER_STANDBY_FULL_BUILD || build_mode == BUILD_CHECK) {
/* For shared storage cluster */
conninfo_para = config_para_cross_cluster_build;
} else {
conninfo_para = config_para_build;
}
/* cleaning global conninfo list */
for (int i = 0; i < MAX_REPLNODE_NUM; i++) {
rc = memset_s(conninfo_global[i], MAX_VALUE_LEN, 0, MAX_VALUE_LEN);
securec_check_ss_c(rc, "", "");
}
/**********************************************************
Try to read the config file
************************************************************/
if ((optlines = readfile(filename)) != NULL) {
int i;
/* get connection str from commands,different from DN Standby connect to DN Primary */
if (NULL != conn_str) {
size_t conn_len = (size_t)(strlen(conn_str) + 1);
rc = strncpy_s(conninfo_global[0],
MAX_VALUE_LEN,
conn_str,
(conn_len >= MAX_VALUE_LEN ? MAX_VALUE_LEN - 2 : conn_len - 1));
securec_check_c(rc, "", "");
} else {
/* read repconninfo[...] */
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
lines_index = find_gucoption((const char**)optlines,
(const char*)conninfo_para[i],
NULL,
NULL,
&optvalue_off,
&optvalue_len);
if (lines_index != INVALID_LINES_IDX) {
rc = strncpy_s(conninfo_global[i - 1],
MAX_VALUE_LEN,
optlines[lines_index] + optvalue_off + 1,
(size_t)Min(optvalue_len - 2, MAX_VALUE_LEN - 1));
securec_check_c(rc, "", "");
}
}
}
/* read cascade standby */
lines_index =
find_gucoption((const char**)optlines, CONFIG_CASCADE_STANDBY, NULL, NULL, &optvalue_off, &optvalue_len);
if (lines_index == INVALID_LINES_IDX) {
cascade_standby = false;
}
#ifndef ENABLE_LLT
else {
rc = strncpy_s(config_cascade_standby,
MAXPGPATH,
optlines[lines_index] + optvalue_off,
(size_t)Min(optvalue_len, MAX_VALUE_LEN - 1));
securec_check_c(rc, "", "");
if (0 == strcmp(config_cascade_standby, "on")) {
cascade_standby = true;
} else if (0 == strcmp(config_cascade_standby, "off")) {
cascade_standby = false;
}
}
#endif
if (g_buildapplication_name != NULL && strlen(g_buildapplication_name) <= 0) {
lines_index =
find_gucoption((const char**)optlines, "application_name", NULL, NULL, &optvalue_off, &optvalue_len);
if (lines_index != INVALID_LINES_IDX) {
rc = strncpy_s(g_buildapplication_name,
MAX_VALUE_LEN,
optlines[lines_index] + optvalue_off + 1,
(size_t)Min(optvalue_len - 2, MAX_VALUE_LEN - 1));
securec_check_c(rc, "", "");
}
}
if (g_replication_type == -1) {
lines_index =
find_gucoption((const char**)optlines, "replication_type", NULL, NULL, &optvalue_off, &optvalue_len);
if (lines_index != INVALID_LINES_IDX) {
rc = strncpy_s(g_str_replication_type,
MAX_VALUE_LEN,
optlines[lines_index] + optvalue_off,
(size_t)Min(optvalue_len, MAX_VALUE_LEN - 1));
securec_check_c(rc, "", "");
g_str_replication_type[1] = 0;
g_replication_type = atoi(g_str_replication_type);
if(g_replication_type != RT_WITH_DUMMY_STANDBY && g_replication_type != RT_WITH_MULTI_STANDBY) {
write_stderr(_("replication_type invalid.\n"));
exit(1);
}
} else {
/* set as default value */
g_replication_type = RT_WITH_DUMMY_STANDBY;
}
}
if (g_buildprimary_slotname != NULL && strlen(g_buildprimary_slotname) <= 0) {
lines_index =
find_gucoption((const char**)optlines, "primary_slotname", NULL, NULL, &optvalue_off, &optvalue_len);
if (lines_index != INVALID_LINES_IDX) {
rc = strncpy_s(g_buildprimary_slotname,
MAX_VALUE_LEN,
optlines[lines_index] + optvalue_off + 1,
(size_t)Min(optvalue_len - 2, MAX_VALUE_LEN - 1));
securec_check_c(rc, "", "");
}
}
/* read pgxc_node */
lines_index = find_gucoption((const char**)optlines, CONFIG_NODENAME, NULL, NULL, &optvalue_off, &optvalue_len);
if (lines_index != INVALID_LINES_IDX) {
rc = strncpy_s(pgxcnodename,
MAX_VALUE_LEN,
optlines[lines_index] + optvalue_off + 1,
(size_t)Min(optvalue_len - 2, MAX_VALUE_LEN - 1));
securec_check_c(rc, "", "");
}
/* read repl_auth_mode */
lines_index = find_gucoption((const char**)optlines,
CONFIG_REPL_AUTH_MODE, NULL, NULL, &optvalue_off, &optvalue_len, '\'');
if (lines_index != INVALID_LINES_IDX) {
rc = strncpy_s(g_repl_auth_mode, MAX_VALUE_LEN, optlines[lines_index] + optvalue_off,
(size_t)Min(optvalue_len, MAX_VALUE_LEN - 1));
securec_check_c(rc, "", "");
}
/* read repl_uuid */
lines_index = find_gucoption((const char**)optlines,
CONFIG_REPL_UUID, NULL, NULL, &optvalue_off, &optvalue_len, '\'');
if (lines_index != INVALID_LINES_IDX) {
rc = strncpy_s(g_repl_uuid, MAX_VALUE_LEN, optlines[lines_index] + optvalue_off,
(size_t)Min(optvalue_len, MAX_VALUE_LEN - 1));
securec_check_c(rc, "", "");
}
check_repl_uuid(g_repl_uuid);
pg_log(PG_WARNING, "Get repl_auth_mode is %s and repl_uuid is %s\n", g_repl_auth_mode, g_repl_uuid);
while (optlines[opt_index] != NULL) {
free(optlines[opt_index]);
optlines[opt_index] = NULL;
opt_index++;
}
free(optlines);
optlines = NULL;
} else {
pg_log(PG_PRINT, _("%s cannot be opened.\n"), filename);
exit(1);
}
}
#define disconnect_and_return_null(tempconn) \
do { \
if ((tempconn) != NULL) { \
PQfinish(tempconn); \
tempconn = NULL; \
} \
tempconn = NULL; \
return tempconn; \
} while (0)
/*
* Brief : @@GaussDB@@
* Description : compare local version and protocal version with remote server.
* Notes :
*/
static bool check_remote_version(PGconn* conn_get, uint32 term)
{
PGresult* res = NULL;
int primary_sversion = 0;
char* primary_pversion = NULL;
bool version_match = false;
uint32 primary_term;
Assert(conn_get != NULL);
res = PQexec(conn_get, "IDENTIFY_VERSION");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
goto exit;
}
if (PQnfields(res) != 3 || PQntuples(res) != 1) {
goto exit;
}
primary_sversion = pg_atoi((const char*)PQgetvalue(res, 0, 0), 4, 0);
primary_pversion = PQgetvalue(res, 0, 1);
primary_term = pg_atoi((const char*)PQgetvalue(res, 0, 2), 4, 0);
if (term > primary_term) {
pg_log(PG_PRINT, "the primary term %u is smaller than the standby term %u.\n", primary_term, term);
goto exit;
}
if (primary_sversion != PG_VERSION_NUM ||
strncmp(primary_pversion, PG_PROTOCOL_VERSION, strlen(PG_PROTOCOL_VERSION)) != 0) {
if (primary_sversion != PG_VERSION_NUM) {
pg_log(PG_PRINT,
"%s: database system version is different between the primary and standby "
"The primary's system version is %d, the standby's system version is %d.\n",
progname,
primary_sversion,
PG_VERSION_NUM);
} else {
pg_log(PG_PRINT,
"%s: the primary protocal version %s is not the same as the standby protocal version %s.\n",
progname,
primary_pversion,
PG_PROTOCOL_VERSION);
}
goto exit;
}
version_match = true;
exit:
PQclear(res);
return version_match;
}
/*
* Brief : @@GaussDB@@
* Description : get remote server's mode
* Notes :
*/
static ServerMode get_remote_mode(PGconn* conn_get)
{
PGresult* res = NULL;
ServerMode primary_mode = UNKNOWN_MODE;
Assert(conn_get != NULL);
res = PQexec(conn_get, "IDENTIFY_MODE");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
goto exit;
}
if (PQnfields(res) != 1 || PQntuples(res) != 1) {
goto exit;
}
primary_mode = (ServerMode)pg_atoi((const char*)PQgetvalue(res, 0, 0), 4, 0);
exit:
PQclear(res);
/* conn_get alse alive, shoule release it outside. */
return primary_mode;
}
/*
* Brief : @@GaussDB@@
* Description : connect the server by connstr and
* check whether the remote server is primary,
* return conn if success
* Notes :
*/
static PGconn* check_and_get_primary_conn(const char* repl_conninfo, uint32 term)
{
PGconn* conn_get = NULL;
ServerMode remote_mode = UNKNOWN_MODE;
Assert(repl_conninfo != NULL);
/* 1. Connect server */
conn_get = PQconnectdb(repl_conninfo);
if (conn_get == NULL) {
pg_log(PG_WARNING, _("build connection failed cause get connection is null.\n"));
disconnect_and_return_null(conn_get);
}
if (PQstatus(conn_get) != CONNECTION_OK) {
pg_log(PG_WARNING, _("build connection to %s failed cause %s.\n"),
(conn_get->pghost != NULL) ? conn_get->pghost : conn_get->pghostaddr, PQerrorMessage(conn_get));
disconnect_and_return_null(conn_get);
}
/* 2. IDENTIFY_VERSION */
if (!check_remote_version(conn_get, term)) {
disconnect_and_return_null(conn_get);
}
/* 3. IDENTIFY_MODE */
if (!need_copy_upgrade_file) {
remote_mode = get_remote_mode(conn_get);
if (remote_mode != NORMAL_MODE && remote_mode != PRIMARY_MODE) {
disconnect_and_return_null(conn_get);
}
}
/* here we get the right primary connect */
return conn_get;
}
PGconn* check_and_conn(int conn_timeout, int recv_timeout, uint32 term)
{
PGconn* con_get = NULL;
char repl_conninfo_str[MAXPGPATH];
int tnRet = 0;
int repl_arr_length;
int i = 0;
int parse_failed_num = 0;
ReplConnInfo repl_conn_info;
bool is_uuid_auth = (strcmp(g_repl_auth_mode, REPL_AUTH_MODE_UUID) == 0 && strlen(g_repl_uuid) > 0);
char uuidOption[MAXPGPATH] = {0};
if (is_uuid_auth) {
tnRet = snprintf_s(uuidOption, sizeof(uuidOption), sizeof(uuidOption) - 1, "-c repl_uuid=%s", g_repl_uuid);
securec_check_ss_c(tnRet, "\0", "\0");
}
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
bool parseOk = ParseReplConnInfo(conninfo_global[i - 1], &repl_arr_length, &repl_conn_info);
if (!parseOk) {
parse_failed_num++;
continue;
}
is_cross_region_build = repl_conn_info.isCrossRegion;
tnRet = memset_s(repl_conninfo_str, MAXPGPATH, 0, MAXPGPATH);
securec_check_ss_c(tnRet, "", "");
is_cross_region_build = false;
if (register_username != NULL && register_password != NULL) {
if (*register_username == '.') {
register_username += 2;
}
tnRet = snprintf_s(repl_conninfo_str,
sizeof(repl_conninfo_str),
sizeof(repl_conninfo_str) - 1,
"localhost=%s localport=%d host=%s port=%d "
"dbname=postgres replication=hadr_main_standby "
"fallback_application_name=gs_ctl "
"connect_timeout=%d rw_timeout=%d "
"options='-c remotetype=application' user=%s password=%s",
repl_conn_info.localhost,
repl_conn_info.localport,
repl_conn_info.remotehost,
repl_conn_info.remoteport,
conn_timeout,
recv_timeout, register_username, register_password);
is_cross_region_build = true;
} else {
tnRet = snprintf_s(repl_conninfo_str,
sizeof(repl_conninfo_str),
sizeof(repl_conninfo_str) - 1,
"localhost=%s localport=%d host=%s port=%d "
"dbname=replication replication=true "
"fallback_application_name=gs_ctl "
"connect_timeout=%d rw_timeout=%d "
"options='-c remotetype=application %s'",
repl_conn_info.localhost,
repl_conn_info.localport,
repl_conn_info.remotehost,
repl_conn_info.remoteport,
conn_timeout,
recv_timeout,
uuidOption);
}
securec_check_ss_c(tnRet, "", "");
con_get = check_and_get_primary_conn(repl_conninfo_str, term);
tnRet = memset_s(repl_conninfo_str, MAXPGPATH, 0, MAXPGPATH);
securec_check_ss_c(tnRet, "", "");
if (is_cross_region_build) {
if (con_get != NULL && (g_replconn_idx == -1 || i == g_replconn_idx)) {
g_replconn_idx = i;
pg_log(PG_WARNING, "build try host(%s) port(%d) success\n", repl_conn_info.remotehost,
repl_conn_info.remoteport);
break;
}
} else {
if (con_get != NULL) {
pg_log(PG_WARNING, "build try host(%s) port(%d) success\n", repl_conn_info.remotehost,
repl_conn_info.remoteport);
break;
}
}
pg_log(PG_WARNING, "build try host(%s) port(%d) failed\n", repl_conn_info.remotehost,
repl_conn_info.remoteport);
if (con_get != NULL) {
PQfinish(con_get);
con_get = NULL;
}
}
if (parse_failed_num == MAX_REPLNODE_NUM - 1) {
pg_log(PG_WARNING, " invalid value for parameter \"replconninfo\" in postgresql.conf.\n");
if (con_get != NULL)
PQfinish(con_get);
return NULL;
}
return con_get;
}
/* check connection for standby build standby */
PGconn* check_and_conn_for_standby(int conn_timeout, int recv_timeout, uint32 term)
{
PGconn* con_get = NULL;
char repl_conninfo_str[MAXPGPATH];
ServerMode remote_mode = UNKNOWN_MODE;
int tnRet = 0;
int repl_arr_length;
int i = 0;
int parse_failed_num = 0;
ReplConnInfo repl_conn_info;
bool is_uuid_auth = (strcmp(g_repl_auth_mode, REPL_AUTH_MODE_UUID) == 0 && strlen(g_repl_uuid) > 0);
char uuidOption[MAXPGPATH] = {0};
if (is_uuid_auth) {
tnRet = snprintf_s(uuidOption, sizeof(uuidOption), sizeof(uuidOption) - 1, "-c repl_uuid=%s", g_repl_uuid);
securec_check_ss_c(tnRet, "\0", "\0");
}
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
bool parseOk = ParseReplConnInfo(conninfo_global[i - 1], &repl_arr_length, &repl_conn_info);
if (!parseOk || repl_conn_info.isCrossRegion) {
parse_failed_num++;
continue;
}
tnRet = memset_s(repl_conninfo_str, MAXPGPATH, 0, MAXPGPATH);
securec_check_ss_c(tnRet, "", "");
tnRet = snprintf_s(repl_conninfo_str,
sizeof(repl_conninfo_str),
sizeof(repl_conninfo_str) - 1,
"localhost=%s localport=%d host=%s port=%d "
"dbname=replication replication=true "
"fallback_application_name=gs_ctl "
"connect_timeout=%d rw_timeout=%d "
"options='-c remotetype=application %s'",
repl_conn_info.localhost,
repl_conn_info.localport,
repl_conn_info.remotehost,
repl_conn_info.remoteport,
conn_timeout,
recv_timeout,
uuidOption);
securec_check_ss_c(tnRet, "", "");
con_get = PQconnectdb(repl_conninfo_str);
if (con_get != NULL && PQstatus(con_get) == CONNECTION_OK && check_remote_version(con_get, term)) {
remote_mode = get_remote_mode(con_get);
if ((remote_mode == STANDBY_MODE || remote_mode == MAIN_STANDBY_MODE) &&
(g_replconn_idx == -1 || i == g_replconn_idx)) {
g_replconn_idx = i;
pg_log(PG_WARNING, "standby build try host(%s) port(%d) success\n", repl_conn_info.remotehost,
repl_conn_info.remoteport);
break;
} else {
PQfinish(con_get);
con_get = NULL;
}
} else {
if (con_get != NULL) {
PQfinish(con_get);
con_get = NULL;
}
if (conn_str != NULL) {
pg_log(PG_WARNING, "The given address can not been access.\n");
exit(1);
}
}
pg_log(PG_WARNING, "standby build try host(%s) port(%d) failed\n", repl_conn_info.remotehost,
repl_conn_info.remoteport);
}
if (parse_failed_num == MAX_REPLNODE_NUM - 1) {
pg_log(PG_WARNING, "Invalid value for parameter \"replconninfo\" in postgresql.conf or no correct standby.\n");
if (con_get != NULL) {
PQfinish(con_get);
}
exit(1);
}
return con_get;
}
/*
* Brief : @@GaussDB@@
* Description : find the value of guc para according to name
* Notes :
*/
int find_gucoption(
const char** optlines, const char* opt_name, int* name_offset, int* name_len,
int* value_offset, int* value_len, unsigned char strip_char)
{
#define SKIP_CHAR(c, skip_c) (isspace((c)) || (c) == (skip_c))
const char* p = NULL;
const char* q = NULL;
const char* tmp = NULL;
int i = 0;
size_t paramlen = 0;
if (optlines == NULL || opt_name == NULL) {
return INVALID_LINES_IDX;
}
paramlen = (size_t)strnlen(opt_name, MAX_PARAM_LEN);
if (name_len != NULL) {
*name_len = (int)paramlen;
}
for (i = 0; optlines[i] != NULL; i++) {
p = optlines[i];
while (isspace((unsigned char)*p)) {
p++;
}
if (strncmp(p, opt_name, paramlen) != 0) {
continue;
}
if (name_offset != NULL) {
*name_offset = p - optlines[i];
}
p += paramlen;
while (isspace((unsigned char)*p)) {
p++;
}
if (*p != '=') {
continue;
}
p++;
while (SKIP_CHAR(((unsigned char)*p), strip_char)) {
p++;
}
q = p;
while (*q && !(*q == '\n' || *q == '#')) {
if (!(SKIP_CHAR(((unsigned char)*q), strip_char))) {
tmp = ++q;
} else {
q++;
}
}
if (value_offset != NULL) {
*value_offset = p - optlines[i];
}
if (value_len != NULL) {
*value_len = (tmp == NULL) ? 0 : (tmp - p);
}
return i;
}
return INVALID_LINES_IDX;
}
/*
* Get paxos value of key from postgres.conf.
* Value is a char array whose length is MAXPGPATH.
*/
static bool GetDCFKeyValue(const char *filename, const char *key, char *value)
{
char **optlines;
int optvalue_off;
int optvalue_len;
int line_index = 0;
int opt_index = 0;
if (filename == nullptr || key == nullptr || value == nullptr) {
return false;
}
if ((optlines = readfile(filename)) == nullptr) {
return false;
}
line_index = find_gucoption((const char**)optlines,
key, nullptr, nullptr, &optvalue_off, &optvalue_len);
if (INVALID_LINES_IDX == line_index) {
return false;
}
int len = strlen(optlines[line_index] + optvalue_off);
const int minLen = 2; /* There is at least a '\n' and a character in the value. */
if (len < minLen) {
return false;
}
errno_t rc = strcpy_s(value, MAXPGPATH, optlines[line_index] + optvalue_off);
securec_check_c(rc, "\0", "\0");
value[len - 1] = '\0'; /* Remove '\n'. */
pg_log(PG_WARNING, _("DCF path is %s\n"), value);
while (optlines[opt_index] != nullptr) {
free(optlines[opt_index]);
optlines[opt_index] = nullptr;
opt_index++;
}
free(optlines);
optlines = nullptr;
/* Remove single quote from value */
if (value != nullptr && value[0] == '\'') {
int i = 0;
int len = strlen(value);
while (i < len) {
value[i] = value[i + 1];
i++;
}
const int singleToEnd = 2;
int endSingleQIdx = len - singleToEnd;
if (endSingleQIdx >= 0 && value[endSingleQIdx] == '\'') {
value[endSingleQIdx] = '\0';
}
}
pg_log(PG_WARNING, _("Final DCF path is %s\n"), value);
return true;
}
int IsBeginWith(const char *str1, char *str2)
{
if (str1 == NULL || str2 == NULL) {
return -1;
}
int len1 = strlen(str1);
int len2 = strlen(str2);
if ((len1 < len2) || (len1 == 0 || len2 == 0)) {
return -1;
}
char *p = str2;
int i = 0;
while (*p != '\0') {
if (*p != str1[i]) {
return 0;
}
p++;
i++;
}
return 1;
}
bool SsIsSkipPath(const char* dirname, bool needskipall)
{
if (!ss_instance_config.dss.enable_dss) {
return false;
}
if (strcmp(dirname, ".recycle") == 0) {
return true;
}
/* skip doublewrite of all instances*/
if (IsBeginWith(dirname, "pg_doublewrite") > 0) {
return true;
}
/* skip pg_control file when dss enable, only copy pg_control of main standby,
* we need to retain pg_control of other nodes, so pg_contol not be deleted directly.
*/
if (strcmp(dirname, "pg_control") == 0) {
return true;
}
/* skip directory which not belong to primary in dss */
if (needskipall) {
/* skip pg_xlog and doublewrite of all instances*/
if (IsBeginWith(dirname, "pg_xlog") > 0) {
return true;
}
} else {
/* skip other node pg_xlog except primary */
if (IsBeginWith(dirname, "pg_xlog") > 0) {
size_t dirNameLen = strlen("pg_xlog");
char instanceId[MAX_INSTANCEID_LEN] = {0};
errno_t rc = EOK;
rc = snprintf_s(instanceId, sizeof(instanceId), sizeof(instanceId) - 1, "%d",
ss_instance_config.dss.instance_id);
securec_check_ss_c(rc, "\0", "\0");
/* not skip pg_xlog directory in file systerm */
if (strlen(dirname) > dirNameLen && strcmp(dirname + dirNameLen, instanceId) != 0)
return true;
}
}
return false;
}
static void DeleteSubDataDir(const char* dirname)
{
DIR* dir = NULL;
char fullpath[MAXPGPATH] = {0};
struct dirent* de = NULL;
struct stat st;
errno_t rc = 0;
int nRet = 0;
/* data dir */
/* Delete dcf data path first for it maybe not under data dir */
char pgConfFile[MAXPGPATH] = {0};
nRet = snprintf_s(pgConfFile, MAXPGPATH, MAXPGPATH - 1, "%s/postgresql.conf", dirname);
securec_check_ss_c(nRet, "", "");
bool enableDCF = GetPaxosValue(pgConfFile);
char dcfLogPath[MAXPGPATH] = {0};
bool hasLogPath = false;
if (enableDCF) {
char dcfDataPath[MAXPGPATH] = {0};
bool hasDataPath = GetDCFKeyValue(pgConfFile, "dcf_data_path", dcfDataPath);
if (hasDataPath) {
/* Don't remove the dcf data path if it didn't exist. */
if (lstat(dcfDataPath, &st) != 0) {
pg_log(PG_WARNING, _("could not stat file or directory %s.\n"), dcfDataPath);
} else {
if (!rmtree(dcfDataPath, true)) {
pg_log(PG_WARNING, _("failed to remove dcf data dir %s.\n"), dcfDataPath);
exit(1);
}
pg_log(PG_WARNING, _("Remove dcf data dir %s.\n"), dcfDataPath);
}
}
hasLogPath = GetDCFKeyValue(pgConfFile, "dcf_log_path", dcfLogPath);
}
if ((dir = opendir(dirname)) != NULL) {
while ((de = gs_readdir(dir)) != NULL) {
/* Skip special stuff */
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
if (strcmp(de->d_name, "pg_log") == 0 || strcmp(de->d_name, "pg_location") == 0)
continue;
if (strcmp(de->d_name, "full_upgrade_bak") == 0)
continue;
if (strcmp(de->d_name, "pg_xlog") == 0)
continue;
if (g_is_obsmode && (strcmp(de->d_name, "pg_replslot") == 0))
continue;
if (is_dss_file(dirname) && SsIsSkipPath(de->d_name, true))
continue;
rc = memset_s(fullpath, MAXPGPATH, 0, MAXPGPATH);
securec_check_c(rc, "", "");
/* others */
nRet = snprintf_s(fullpath, MAXPGPATH, sizeof(fullpath) - 1, "%s/%s", dirname, de->d_name);
securec_check_ss_c(nRet, "", "");
if (lstat(fullpath, &st) != 0) {
pg_log(PG_WARNING, _("could not stat file or directory %s.\n"), fullpath);
continue;
}
/* Don't delete dcf log path */
if (enableDCF && hasLogPath) {
char comparedFullPath[MAXPGPATH] = {0};
char comparedLogPath[MAXPGPATH] = {0};
nRet = snprintf_s(comparedFullPath, MAXPGPATH, MAXPGPATH - 1, "%s/", fullpath);
securec_check_ss_c(nRet, "", "");
nRet = snprintf_s(comparedLogPath, MAXPGPATH, MAXPGPATH - 1, "%s/", dcfLogPath);
securec_check_ss_c(nRet, "", "");
int comparedFullPathLen = strlen(comparedFullPath);
if (strncmp(comparedFullPath, comparedLogPath, comparedFullPathLen) == 0) {
hasLogPath = false;
pg_log(PG_WARNING, _("Skip dcf log path %s.\n"), dcfLogPath);
continue;
}
}
#ifndef WIN32
if (S_ISLNK(st.st_mode))
#else
if (pgwin32_is_junction(fullpath))
#endif
{
#if defined(HAVE_READLINK) || defined(WIN32)
char linkpath[MAXPGPATH] = {0};
int rllen;
rllen = readlink(fullpath, linkpath, sizeof(linkpath));
if (rllen < 0) {
pg_log(PG_WARNING, _("could not read symbolic link.\n"));
continue;
}
if (rllen >= (int)sizeof(linkpath)) {
pg_log(PG_WARNING, _("symbolic link target is too long.\n"));
continue;
}
linkpath[MAXPGPATH - 1] = '\0';
/* delete linktarget */
if (!rmtree(linkpath, true)) {
pg_log(PG_WARNING, _("failed to remove dir %s.\n"), linkpath);
(void)closedir(dir);
exit(1);
}
/* delete link */
(void)unlink(fullpath);
#else
/*
* If the platform does not have symbolic links, it should not be
* possible to have tablespaces - clearly somebody else created
* them. Warn about it and ignore.
*/
pg_log(PG_WARNING, _("symbolic links are not supported on this platform.\n"));
(void)closedir(dir);
exit(1);
#endif /* HAVE_READLINK */
} else if (S_ISDIR(st.st_mode)) {
if (strcmp(de->d_name, "pg_replslot") == 0) {
/* remove physical slot and remain logic slot */
DIR* dir_slot = NULL;
struct dirent* de_slot = NULL;
if ((dir_slot = opendir(fullpath)) != NULL) {
while ((de_slot = gs_readdir(dir_slot)) != NULL) {
/* Skip special stuff */
if (strncmp(de_slot->d_name, ".", 1) == 0 || strncmp(de_slot->d_name, "..", 2) == 0)
continue;
/* others */
nRet = snprintf_s(fullpath,
MAXPGPATH,
sizeof(fullpath) - 1,
"%s/%s/%s",
dirname,
"pg_replslot",
de_slot->d_name);
securec_check_ss_c(nRet, "", "");
if (!rmtree(fullpath, true)) {
pg_log(PG_WARNING, _("failed to remove dir %s,errno=%d.\n"), fullpath, errno);
(void)closedir(dir);
exit(1);
}
}
(void)closedir(dir_slot);
}
} else if (!rmtree(fullpath, true)) {
pg_log(PG_WARNING, _("failed to remove dir %s, errno=%d.\n"), fullpath, errno);
(void)closedir(dir);
exit(1);
}
} else if (S_ISREG(st.st_mode)) {
if (strcmp(de->d_name, "postgresql.conf") == 0 || strcmp(de->d_name, "pg_ctl.lock") == 0 ||
strcmp(de->d_name, "postgresql.conf.lock") == 0 || strcmp(de->d_name, "postgresql.conf.bak.old") == 0 ||
strcmp(de->d_name, "postgresql.conf.bak") == 0 || strcmp(de->d_name, "postgresql.conf.guc.bak") == 0 ||
strcmp(de->d_name, "build_completed.start") == 0 || strcmp(de->d_name, "gs_build.pid") == 0 ||
strcmp(de->d_name, "postmaster.opts") == 0 || strcmp(de->d_name, "gaussdb.state") == 0 ||
strcmp(de->d_name, "disc_readonly_test") == 0 || strcmp(de->d_name, ssl_cert_file) == 0 ||
strcmp(de->d_name, ssl_key_file) == 0 || strcmp(de->d_name, ssl_ca_file) == 0 ||
strcmp(de->d_name, ssl_crl_file) == 0 || strcmp(de->d_name, ssl_cipher_file) == 0 ||
strcmp(de->d_name, ssl_rand_file) == 0 || strcmp(de->d_name, "rewind_lable") == 0 ||
strcmp(de->d_name, "gs_gazelle.conf") == 0 ||
(g_is_obsmode && strcmp(de->d_name, "base.tar.gz") == 0) ||
(g_is_obsmode && strcmp(de->d_name, "pg_hba.conf") == 0)||
(g_is_obsmode && strcmp(de->d_name, "pg_ident.conf") == 0) ||
(IS_CROSS_CLUSTER_BUILD && strcmp(de->d_name, "pg_hba.conf") == 0) ||
strcmp(de->d_name, "pg_hba.conf.old") == 0)
continue;
/* Skip paxos index files for building process will write them */
if (enableDCF && ((strcmp(de->d_name, "paxosindex") == 0) ||
(strcmp(de->d_name, "paxosindex.backup") == 0)))
continue;
/* build from cn reserve this file,om will modify it. */
if ((conn_str != NULL) && strncmp(de->d_name, "pg_hba.conf", strlen("pg_hba.conf")) == 0) {
continue;
}
if (unlink(fullpath)) {
pg_log(PG_WARNING, _("failed to remove file %s.\n"), fullpath);
(void)closedir(dir);
exit(1);
}
}
}
(void)closedir(dir);
}
}
/*
* Brief : @@GaussDB@@
* Description : delete data/ and pg_tblspc/
* Notes :
*/
void delete_datadir(const char* dirname)
{
DIR* dir = NULL;
char fullpath[MAXPGPATH] = {0};
char nodepath[MAXPGPATH] = {0};
char xlogpath[MAXPGPATH] = {0};
struct dirent* de = NULL;
struct stat st;
struct stat stbuf;
errno_t rc = 0;
int nRet = 0;
if (dirname == NULL) {
pg_log(PG_WARNING, _("input parameter is NULL.\n"));
exit(1);
}
nRet = snprintf_s(fullpath, MAXPGPATH, sizeof(fullpath) - 1, "%s/pg_tblspc", dirname);
securec_check_ss_c(nRet, "", "");
/* pg_tblspc */
if ((dir = opendir(fullpath)) != NULL) {
while ((de = gs_readdir(dir)) != NULL) {
char linkpath[MAXPGPATH] = {0};
int rllen = 0;
/* Skip special stuff */
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
rc = memset_s(fullpath, MAXPGPATH, 0, MAXPGPATH);
securec_check_c(rc, "", "");
nRet = snprintf_s(fullpath, MAXPGPATH, sizeof(fullpath) - 1, "%s/pg_tblspc/%s", dirname, de->d_name);
securec_check_ss_c(nRet, "", "");
#if defined(HAVE_READLINK) || defined(WIN32)
rllen = readlink(fullpath, linkpath, sizeof(linkpath));
if (rllen < 0) {
pg_log(PG_WARNING, _("could not read symbolic link.\n"));
continue;
}
if (rllen >= (int)sizeof(linkpath)) {
pg_log(PG_WARNING, _("symbolic link target is too long.\n"));
continue;
}
linkpath[MAXPGPATH - 1] = '\0';
/* */
if (stat(linkpath, &st) == 0 && S_ISDIR(st.st_mode)) {
nRet = snprintf_s(nodepath,
MAXPGPATH,
sizeof(nodepath) - 1,
"%s/%s_%s",
linkpath,
TABLESPACE_VERSION_DIRECTORY,
pgxcnodename);
securec_check_ss_c(nRet, "", "");
if (!rmtree(nodepath, true, true)) {
pg_log(PG_WARNING, _("failed to remove %s.\n"), nodepath);
(void)closedir(dir);
exit(1);
}
/* just try to delete the top folder regardless of success or failure. */
(void)unlink(linkpath);
}
/* remove link file. */
if (unlink(fullpath) < 0) {
pg_log(PG_WARNING, _("could not remove symbolic link file \"%s\": %s\n"), fullpath, strerror(errno));
(void)closedir(dir);
exit(1);
}
#else
/*
* If the platform does not have symbolic links, it should not be
* possible to have tablespaces - clearly somebody else created
* them. Warn about it and ignore.
*/
pg_log(PG_WARNING, _("symbolic links are not supported on this platform.\n"));
exit(1);
#endif
}
(void)closedir(dir);
}
/*
* in build process, primary node send string
* 1. 'data/pg_xlog'(primary pg_xlog not symbolic) or
* 2. pg_xlog linktarget(primary pg_xlog is symbolic)
* to standby. case 1, standby set xlog_location to basedir/pg_xlog,
* case 2, standby set xlog_location to primary linktarget directory.
*
* Enable user define xlog directory, datanode xlog directory is the value
* passed by -X(if set), and not depend on primary anymore. But in the build
* process, standby can't get the xlog directory info. the way to resolve
* this is to keep the basedir/pg_xlog, and delete all files and
* directories under it.
*/
if (strncmp(dirname, "+", 1) == 0 ) {
nRet = snprintf_s(xlogpath, MAXPGPATH, sizeof(xlogpath) - 1, "%s/pg_xlog%d", dirname,
ss_instance_config.dss.instance_id);
} else {
nRet = snprintf_s(xlogpath, MAXPGPATH, sizeof(xlogpath) - 1, "%s/pg_xlog", dirname);
}
securec_check_ss_c(nRet, "", "");
if (lstat(xlogpath, &stbuf) == 0) {
#ifndef WIN32
if (S_ISLNK(stbuf.st_mode))
#else
if (pgwin32_is_junction(xlogpath))
#endif
{
#if defined(HAVE_READLINK) || defined(WIN32)
char linkpath[MAXPGPATH] = {0};
int rllen;
rllen = readlink(xlogpath, linkpath, sizeof(linkpath));
if (rllen < 0) {
pg_log(PG_WARNING, _("could not read symbolic link.\n"));
}
if (rllen >= (int)sizeof(linkpath)) {
pg_log(PG_WARNING, _("symbolic link target is too long.\n"));
}
linkpath[MAXPGPATH - 1] = '\0';
/* delete targets under linktarget, but keep itself */
if (!rmtree(linkpath, false)) {
pg_log(PG_WARNING, _("failed to remove dir %s.\n"), linkpath);
exit(1);
}
#else
/*
* If the platform does not have symbolic links, it should not be
* possible to have tablespaces - clearly somebody else created
* them. Warn about it and ignore.
*/
pg_log(PG_WARNING, _("symbolic links are not supported on this platform.\n"));
exit(1);
#endif /* HAVE_READLINK */
} else {
/* delete targets under pg_xlog and itself */
if (!rmtree(xlogpath, true)) {
pg_log(PG_WARNING, _("failed to remove dir %s.\n"), xlogpath);
exit(1);
}
}
}
DeleteSubDataDir(dirname);
}
/*
* Brief : @@GaussDB@@
* Description : get the number of replication in postgresql.conf
* Notes :
*/
int get_replconn_number(const char* filename)
{
char** optlines;
int repl_num = 0;
int opt_index = 0;
if (filename == NULL) {
pg_log(PG_PRINT, _("the parameter filename is NULL in function get_replconn_number()"));
exit(1);
}
if ((optlines = readfile(filename)) != NULL) {
int optvalue_off = 0;
int optvalue_len = 0;
int lines_index = 0;
int i;
for (i = 1; i < DOUBLE_MAX_REPLNODE_NUM; i++) {
const char *para = NULL;
if (i > MAX_REPLNODE_NUM) {
para = config_para_cross_cluster_build[i - MAX_REPLNODE_NUM];
} else if (i < MAX_REPLNODE_NUM) {
para = config_para_build[i];
}
lines_index = find_gucoption(
(const char**)optlines, (const char*)para, NULL, NULL, &optvalue_off, &optvalue_len);
if (lines_index != INVALID_LINES_IDX) {
repl_num++;
}
}
while (optlines[opt_index] != NULL) {
free(optlines[opt_index]);
optlines[opt_index] = NULL;
opt_index++;
}
free(optlines);
optlines = NULL;
} else {
pg_log(PG_PRINT, _("%s cannot be opened.\n"), filename);
exit(1);
}
return repl_num;
}
void get_slot_name(char* slotname, size_t len)
{
int errorno = memset_s(slotname, len, 0, len);
securec_check_ss_c(errorno, "", "");
if (g_buildprimary_slotname != NULL && strlen(g_buildprimary_slotname) > 0) {
errorno = snprintf_s(slotname, len, len - 1, "%s", g_buildprimary_slotname);
securec_check_ss_c(errorno, "", "");
} else if (g_buildapplication_name != NULL && strlen(g_buildapplication_name) > 0) {
errorno = snprintf_s(slotname, len, len - 1, "%s", g_buildapplication_name);
securec_check_ss_c(errorno, "", "");
} else if (pgxcnodename != NULL && strlen(pgxcnodename) > 0) {
if(g_replication_type == RT_WITH_DUMMY_STANDBY) {
errorno = snprintf_s(
slotname, len, len - 1, "%s", pgxcnodename);
securec_check_ss_c(errorno, "", "");
} else if(g_replconn_idx != -1) {
ReplConnInfo repl_conn_info;
int repl_arr_length = 0;
bool parseOk = ParseReplConnInfo(conninfo_global[g_replconn_idx], &repl_arr_length, &repl_conn_info);
if (parseOk) {
errorno = snprintf_s(slotname, len, len - 1, "%s_%s_%d", pgxcnodename, repl_conn_info.localhost,
repl_conn_info.localport);
securec_check_ss_c(errorno, "", "");
}
}
}
}
/*
* rotate cbm force when build.
*/
bool libpqRotateCbmFile(PGconn* connObj, XLogRecPtr lsn)
{
PGresult* res = NULL;
char sql[MAX_QUERY_LEN] = {0};
errno_t errorno = EOK;
bool ec = true;
errorno = snprintf_s(sql, MAX_QUERY_LEN, MAX_QUERY_LEN - 1,
"select * from pg_catalog.pg_cbm_rotate_file('%08X/%08X'); ",
(uint32)(lsn >> 32), (uint32)(lsn));
securec_check_ss_c(errorno, "\0", "\0");
res = PQexec(connObj, sql);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
pg_log(PG_ERROR, "could not rotate cbm in 10min: %s", PQresultErrorMessage(res));
ec = false;
}
PQclear(res);
return ec;
}
/* Get whether paxos is enable from postgres.conf */
bool GetPaxosValue(const char *filename)
{
char **optlines;
int optvalue_off;
int optvalue_len;
const char *paxosPara = "enable_dcf";
int line_index = 0;
bool ret = false;
int opt_index = 0;
if (filename == nullptr) {
return false;
}
if ((optlines = readfile(filename)) != nullptr) {
line_index = find_gucoption((const char**)optlines,
paxosPara, nullptr, nullptr, &optvalue_off, &optvalue_len);
if (INVALID_LINES_IDX != line_index) {
if (strcmp("true\n", optlines[line_index] + optvalue_off) == 0 ||
strcmp("on\n", optlines[line_index] + optvalue_off) == 0 ||
strcmp("\'on\'\n", optlines[line_index] + optvalue_off) == 0)
ret = true;
}
while (optlines[opt_index] != nullptr) {
free(optlines[opt_index]);
optlines[opt_index] = nullptr;
opt_index++;
}
free(optlines);
optlines = nullptr;
return ret;
} else {
return false;
}
}
/*
* Issue fsync recursively on PGDATA and all its contents.
*
* We fsync regular files and directories wherever they are, but we follow
* symlinks only for pg_wal (or pg_xlog) and immediately under pg_tblspc.
* Other symlinks are presumed to point at files we're not responsible for
* fsyncing, and might not have privileges to write at all.
*
*/
void fsync_pgdata(const char *pg_data)
{
bool xlog_is_symlink = false;
char pg_xlog[MAXPGPATH] = {0};
char pg_tblspc[MAXPGPATH] = {0};
errno_t errorno = EOK;
if (is_dss_file(pg_data)) {
errorno = snprintf_s(pg_xlog, MAXPGPATH, MAXPGPATH - 1, "%s/pg_xlog%d", pg_data,
ss_instance_config.dss.instance_id);
} else {
errorno = snprintf_s(pg_xlog, MAXPGPATH, MAXPGPATH - 1, "%s/pg_xlog", pg_data);
}
securec_check_ss_c(errorno, "\0", "\0");
errorno = snprintf_s(pg_tblspc, MAXPGPATH, MAXPGPATH - 1, "%s/pg_tblspc", pg_data);
securec_check_ss_c(errorno, "\0", "\0");
#ifndef WIN32
{
struct stat st;
if (lstat(pg_xlog, &st) < 0) {
pg_log(PG_WARNING, _("could not stat file \"%s\": %s\n"), pg_xlog, strerror(errno));
exit(1);
}
else if (S_ISLNK(st.st_mode))
xlog_is_symlink = true;
}
#else
if (pgwin32_is_junction(pg_xlog))
xlog_is_symlink = true;
#endif
/*
* Now we do the fsync()s in the same order.
*
* The main call ignores symlinks, so in addition to specially processing
* pg_wal if it's a symlink, pg_tblspc has to be visited separately with
* process_symlinks = true. Note that if there are any plain directories
* in pg_tblspc, they'll get fsync'd twice. That's not an expected case
* so we don't worry about optimizing it.
*/
walkdir(pg_data, fsync_fname, false);
if (xlog_is_symlink)
walkdir(pg_xlog, fsync_fname, false);
walkdir(pg_tblspc, fsync_fname, true);
}
/*
* walkdir: recursively walk a directory, applying the action to each
* regular file and directory (including the named directory itself).
*
* If process_symlinks is true, the action and recursion are also applied
* to regular files and directories that are pointed to by symlinks in the
* given directory; otherwise symlinks are ignored. Symlinks are always
* ignored in subdirectories, ie we intentionally don't pass down the
* process_symlinks flag to recursive calls.
*
* Errors are reported but not considered fatal.
*
* See also walkdir in fd.cpp, which is a backend version of this logic.
*/
static void walkdir(const char *path, int (*action) (const char *fname, bool isdir), bool process_symlinks)
{
DIR *dir;
struct dirent *de = NULL;
errno_t errorno = EOK;
dir = opendir(path);
if (dir == NULL) {
pg_log(PG_WARNING, _("could not open directory \"%s\": %s\n"), path, strerror(errno));
return;
}
while (errno = 0, (de = readdir(dir)) != NULL) {
char subpath[MAXPGPATH * 2] = {0};
struct stat fst;
int sret;
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
if (strcmp(de->d_name, "pg_ctl.lock") == 0) {
continue;
}
errorno = snprintf_s(subpath, sizeof(subpath), sizeof(subpath) - 1, "%s/%s", path, de->d_name);
securec_check_ss_c(errorno, "\0", "\0");
if (process_symlinks)
sret = stat(subpath, &fst);
else
sret = lstat(subpath, &fst);
if (sret < 0) {
pg_log(PG_WARNING, _("could not stat file \"%s\": %s\n"), subpath, strerror(errno));
continue;
}
if (S_ISREG(fst.st_mode))
(*action) (subpath, false);
else if (S_ISDIR(fst.st_mode))
walkdir(subpath, action, false);
}
if (errno)
pg_log(PG_WARNING, _("could not read directory \"%s\": %s\n"), path, strerror(errno));
(void)closedir(dir);
/*
* It's important to fsync the destination directory itself as individual
* file fsyncs don't guarantee that the directory entry for the file is
* synced. Recent versions of ext4 have made the window much wider but
* it's been an issue for ext3 and other filesystems in the past.
*/
(*action) (path, true);
}
/*
* fsync_fname -- Try to fsync a file or directory
*
* Ignores errors trying to open unreadable files, or trying to fsync
* directories on systems where that isn't allowed/required. All other errors
* are fatal.
*/
int fsync_fname(const char *fname, bool isdir)
{
int fd = -1;
int flags;
int returncode;
/*
* Some OSs require directories to be opened read-only whereas other
* systems don't allow us to fsync files opened read-only; so we need both
* cases here. Using O_RDWR will cause us to fail to fsync files that are
* not writable by our userid, but we assume that's OK.
*/
flags = PG_BINARY;
if (!isdir)
flags |= O_RDWR;
else
flags |= O_RDONLY;
/*
* Open the file, silently ignoring errors about unreadable files (or
* unsupported operations, e.g. opening a directory under Windows), and
* logging others.
*/
fd = open(fname, flags, 0);
if (fd < 0) {
if (errno == EACCES || (isdir && errno == EISDIR))
return 0;
pg_log(PG_WARNING, _("could not open file \"%s\": %s\n"), fname, strerror(errno));
return -1;
}
returncode = fsync(fd);
/*
* Some OSes don't allow us to fsync directories at all, so we can ignore
* those errors. Anything else needs to be reported.
*/
if (returncode != 0 && !(isdir && (errno == EBADF || errno == EINVAL))) {
pg_log(PG_WARNING, _("could not fsync file \"%s\": %s\n"), fname, strerror(errno));
(void) close(fd);
exit(EXIT_FAILURE);
}
(void) close(fd);
return 0;
}