1549 lines
48 KiB
C++
Executable File
1549 lines
48 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 "bin/elog.h"
|
|
#include "nodes/pg_list.h"
|
|
#include "storage/fd.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/datetime.h"
|
|
#include "common/fe_memutils.h"
|
|
|
|
/* global variables for con */
|
|
char conninfo_global[MAX_REPLNODE_NUM][MAX_VALUE_LEN] = {{0}, {0}, {0}, {0}, {0},
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
{0}, {0}, {0}
|
|
#endif
|
|
};
|
|
static const char* config_para_build[MAX_REPLNODE_NUM] = {"",
|
|
"replconninfo1",
|
|
"replconninfo2",
|
|
"replconninfo3",
|
|
"replconninfo4",
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
"replconninfo5",
|
|
"replconninfo6",
|
|
"replconninfo7"
|
|
#endif
|
|
};
|
|
|
|
/* 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};
|
|
int g_replconn_idx = -1;
|
|
int g_replication_type = -1;
|
|
#define RT_WITH_DUMMY_STANDBY 0
|
|
#define RT_WITH_MULTI_STANDBY 1
|
|
|
|
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) {
|
|
exit(1);
|
|
}
|
|
if (*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 : CheckReplChannel
|
|
* Description :
|
|
* Notes :
|
|
*/
|
|
static bool CheckReplChannel(const char* ChannelInfo)
|
|
{
|
|
char* iter = NULL;
|
|
char* ReplStr = NULL;
|
|
|
|
if (ChannelInfo == NULL) {
|
|
return false;
|
|
} else {
|
|
ReplStr = strdup(ChannelInfo);
|
|
if (ReplStr == NULL) {
|
|
return false;
|
|
} else {
|
|
/* check localhost */
|
|
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;
|
|
}
|
|
|
|
/* check localport */
|
|
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;
|
|
}
|
|
|
|
/* check remotehost */
|
|
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;
|
|
}
|
|
|
|
/* check remoteport */
|
|
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 (CheckReplChannel(token)) {
|
|
repl_len++;
|
|
}
|
|
|
|
token = strtok_r(NULL, ",", &p);
|
|
}
|
|
}
|
|
|
|
free(ReplStr);
|
|
ReplStr = NULL;
|
|
return repl_len;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief : ParseReplConnInfo
|
|
* Description :
|
|
* Notes :
|
|
*/
|
|
ReplConnInfo* ParseReplConnInfo(const char* ConnInfoList, int* InfoLength)
|
|
{
|
|
ReplConnInfo* repl = NULL;
|
|
|
|
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};
|
|
int tmp_localport = 0;
|
|
int tmp_localservice = 0;
|
|
char tmp_remotehost[IP_LEN] = {0};
|
|
int tmp_remoteport = 0;
|
|
int tmp_remoteservice = 0;
|
|
errno_t rc = EOK;
|
|
char* p = NULL;
|
|
|
|
if (ConnInfoList == NULL) {
|
|
return NULL;
|
|
} else {
|
|
ReplStr = strdup(ConnInfoList);
|
|
if (ReplStr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ptr = ReplStr;
|
|
while (*ptr != '\0') {
|
|
if (*ptr != ' ') {
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
if (*ptr == '\0') {
|
|
free(ReplStr);
|
|
ReplStr = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
repl_length = GetLengthAndCheckReplConn(ReplStr);
|
|
if (repl_length == 0) {
|
|
free(ReplStr);
|
|
ReplStr = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
repl = (ReplConnInfo*)malloc(sizeof(ReplConnInfo));
|
|
if (repl == NULL) {
|
|
free(ReplStr);
|
|
ReplStr = NULL;
|
|
return NULL;
|
|
}
|
|
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);
|
|
|
|
/* localservice */
|
|
iter = strstr(token, "localservice");
|
|
if (iter != NULL) {
|
|
iter += strlen("localservice");
|
|
;
|
|
while (*iter == ' ' || *iter == '=') {
|
|
iter++;
|
|
}
|
|
|
|
if (isdigit(*iter)) {
|
|
tmp_localservice = 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);
|
|
|
|
/* remoteservice */
|
|
iter = strstr(token, "remoteservice");
|
|
if (NULL != iter) {
|
|
iter += strlen("remoteservice");
|
|
while (*iter == ' ' || *iter == '=') {
|
|
iter++;
|
|
}
|
|
|
|
if (isdigit(*iter)) {
|
|
tmp_remoteservice = atoi(iter);
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
repl->localservice = tmp_localservice;
|
|
|
|
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;
|
|
repl->remoteservice = tmp_remoteservice;
|
|
|
|
token = strtok_r(NULL, ",", &p);
|
|
parsed++;
|
|
}
|
|
}
|
|
|
|
free(ReplStr);
|
|
ReplStr = NULL;
|
|
*InfoLength = repl_length;
|
|
|
|
return repl;
|
|
}
|
|
|
|
/*
|
|
* Brief : @@GaussDB@@
|
|
* Description : get available conn
|
|
* Notes :
|
|
*/
|
|
void get_conninfo(const char* filename)
|
|
{
|
|
char** optlines;
|
|
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);
|
|
}
|
|
|
|
/**********************************************************
|
|
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*)config_para_build[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, "", "");
|
|
}
|
|
|
|
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) || PQstatus(conn_get) != CONNECTION_OK) {
|
|
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 */
|
|
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;
|
|
|
|
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
|
|
ReplConnInfo* repl_conn_info = ParseReplConnInfo(conninfo_global[i - 1], &repl_arr_length);
|
|
if (repl_conn_info == NULL) {
|
|
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'",
|
|
repl_conn_info->localhost,
|
|
repl_conn_info->localport,
|
|
repl_conn_info->remotehost,
|
|
repl_conn_info->remoteport,
|
|
standby_recv_timeout,
|
|
standby_connect_timeout);
|
|
securec_check_ss_c(tnRet, "", "");
|
|
|
|
free(repl_conn_info);
|
|
repl_conn_info = NULL;
|
|
con_get = check_and_get_primary_conn(repl_conninfo_str, term);
|
|
if (con_get != NULL)
|
|
break;
|
|
}
|
|
|
|
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);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
return con_get;
|
|
}
|
|
|
|
void CheckBuildParamters(int conn_timeout, int recv_timeout, uint32 term)
|
|
{
|
|
PGconn* conn = NULL;
|
|
int i = 0;
|
|
int repl_arr_length = 0;
|
|
ReplConnInfo* repl_conn_info = NULL;
|
|
char repl_conninfo[MAXPGPATH];
|
|
PGresult* res = NULL;
|
|
errno_t errorno = EOK;
|
|
|
|
/* 2. get primary host. */
|
|
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
|
|
if (repl_conn_info != NULL) {
|
|
free(repl_conn_info);
|
|
repl_conn_info = NULL;
|
|
}
|
|
|
|
repl_conn_info = ParseReplConnInfo(conninfo_global[i - 1], &repl_arr_length);
|
|
if (repl_conn_info == NULL)
|
|
continue;
|
|
|
|
errorno = memset_s(repl_conninfo, MAXPGPATH, 0, MAXPGPATH);
|
|
securec_check_ss_c(errorno, "", "");
|
|
|
|
errorno = snprintf_s(repl_conninfo,
|
|
MAXPGPATH,
|
|
MAXPGPATH - 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'",
|
|
repl_conn_info->localhost,
|
|
repl_conn_info->localport,
|
|
repl_conn_info->remotehost,
|
|
repl_conn_info->remoteport,
|
|
conn_timeout,
|
|
recv_timeout);
|
|
securec_check_ss_c(errorno, "", "");
|
|
conn = check_and_get_primary_conn(repl_conninfo, term);
|
|
if (conn != NULL) {
|
|
PQfinish(conn);
|
|
conn = NULL;
|
|
g_replconn_idx = i - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == MAX_REPLNODE_NUM) {
|
|
pg_log(PG_PRINT,
|
|
"%s: invalid value for parameter \"replconninfo\" in postgresql.conf,"
|
|
"can't find primary.\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
|
|
errorno = memset_s(repl_conninfo, MAXPGPATH, 0, MAXPGPATH);
|
|
securec_check_ss_c(errorno, "", "");
|
|
|
|
/* conn info string */
|
|
errorno = snprintf_s(repl_conninfo,
|
|
MAXPGPATH,
|
|
sizeof(repl_conninfo) - 1,
|
|
"host=%s port=%d "
|
|
"dbname=replication replication=true fallback_application_name=gs_rewind connect_timeout=5",
|
|
repl_conn_info->remotehost,
|
|
repl_conn_info->remoteport);
|
|
|
|
securec_check_ss_c(errorno, "", "");
|
|
|
|
conn = PQconnectdb(repl_conninfo);
|
|
|
|
if (conn == NULL) {
|
|
pg_log(PG_PRINT,
|
|
"%s: connect the source server error: host=%s port=%d.\n",
|
|
progname,
|
|
repl_conn_info->remotehost,
|
|
repl_conn_info->remoteport);
|
|
exit(1);
|
|
}
|
|
|
|
if (CONNECTION_BAD == PQstatus(conn)) {
|
|
pg_log(PG_PRINT,
|
|
"%s: connect the source server error: host=%s port=%d.\n",
|
|
progname,
|
|
repl_conn_info->remotehost,
|
|
repl_conn_info->remoteport);
|
|
exit(1);
|
|
}
|
|
|
|
free(repl_conn_info);
|
|
repl_conn_info = NULL;
|
|
|
|
res = PQexec(conn, "SET xc_maintenance_mode to on");
|
|
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
|
PQclear(res);
|
|
res = NULL;
|
|
PQfinish(conn);
|
|
conn = NULL;
|
|
|
|
write_stderr("%s: set the connection xc_maintenance_mode to on error.\n", progname);
|
|
return;
|
|
}
|
|
|
|
PQclear(res);
|
|
res = NULL;
|
|
|
|
PQfinish(conn);
|
|
conn = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
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 (isspace((unsigned char)*p)) {
|
|
p++;
|
|
}
|
|
q = p;
|
|
while (*q && !(*q == '\n' || *q == '#')) {
|
|
if (!isspace((unsigned char)*q)) {
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* data dir */
|
|
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;
|
|
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;
|
|
}
|
|
|
|
#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, "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)
|
|
continue;
|
|
/* build from cn reserve this file,om will modify it */
|
|
if ((conn_str != NULL) && 0 == strncmp(de->d_name, "pg_hba.conf", strlen("pg_hba.conf")))
|
|
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 : 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 < MAX_REPLNODE_NUM; i++) {
|
|
lines_index = find_gucoption(
|
|
(const char**)optlines, (const char*)config_para_build[i], 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;
|
|
}
|
|
|
|
/*
|
|
* Brief : @@GaussDB@@
|
|
* Description : to connect the server,and return conn if success
|
|
* Notes :
|
|
*/
|
|
static PGconn* get_conn(const char* repl_conninfo)
|
|
{
|
|
PGconn* conn_get = NULL;
|
|
PGresult* res = NULL;
|
|
int primary_sversion = 0;
|
|
int standby_sversion = 0;
|
|
char* primary_pversion = NULL;
|
|
char* standby_pversion = NULL;
|
|
ServerMode primary_mode;
|
|
|
|
/* to connect server */
|
|
conn_get = PQconnectdb(repl_conninfo);
|
|
if ((!conn_get) || (PQstatus(conn_get) != CONNECTION_OK)) {
|
|
disconnect_and_return_null(conn_get);
|
|
}
|
|
|
|
/* IDENTIFY_VERSION */
|
|
res = PQexec(conn_get, "IDENTIFY_VERSION");
|
|
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
|
PQclear(res);
|
|
disconnect_and_return_null(conn_get);
|
|
}
|
|
if (PQnfields(res) != 3 || PQntuples(res) != 1) {
|
|
PQclear(res);
|
|
disconnect_and_return_null(conn_get);
|
|
}
|
|
primary_sversion = pg_atoi((const char*)PQgetvalue(res, 0, 0), 4, 0);
|
|
standby_sversion = PG_VERSION_NUM;
|
|
primary_pversion = PQgetvalue(res, 0, 1);
|
|
standby_pversion = strdup(PG_PROTOCOL_VERSION);
|
|
if (standby_pversion == NULL) {
|
|
PQclear(res);
|
|
disconnect_and_return_null(conn_get);
|
|
}
|
|
|
|
if (primary_sversion != standby_sversion ||
|
|
strncmp(primary_pversion, standby_pversion, strlen(PG_PROTOCOL_VERSION)) != 0) {
|
|
PQclear(res);
|
|
|
|
if (primary_sversion != standby_sversion) {
|
|
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,
|
|
standby_sversion);
|
|
free(standby_pversion);
|
|
standby_pversion = NULL;
|
|
disconnect_and_return_null(conn_get);
|
|
} 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,
|
|
standby_pversion);
|
|
free(standby_pversion);
|
|
standby_pversion = NULL;
|
|
disconnect_and_return_null(conn_get);
|
|
}
|
|
}
|
|
PQclear(res);
|
|
|
|
/* free immediately once not used. Can't be NULL. */
|
|
free(standby_pversion);
|
|
standby_pversion = NULL;
|
|
/* IDENTIFY_MODE */
|
|
res = PQexec(conn_get, "IDENTIFY_MODE");
|
|
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
|
PQclear(res);
|
|
disconnect_and_return_null(conn_get);
|
|
}
|
|
if (PQnfields(res) != 1 || PQntuples(res) != 1) {
|
|
PQclear(res);
|
|
disconnect_and_return_null(conn_get);
|
|
}
|
|
primary_mode = (ServerMode)pg_atoi((const char*)PQgetvalue(res, 0, 0), 4, 0);
|
|
if (!((primary_mode == PRIMARY_MODE && cascade_standby == false) ||
|
|
(primary_mode == STANDBY_MODE && cascade_standby == true) || (primary_mode == NORMAL_MODE))) {
|
|
PQclear(res);
|
|
disconnect_and_return_null(conn_get);
|
|
}
|
|
PQclear(res);
|
|
return conn_get;
|
|
}
|
|
|
|
bool check_conn(int conn_timeout, int recv_timeout)
|
|
{
|
|
|
|
ReplConnInfo* repl_conn_info = NULL;
|
|
|
|
PGconn* con_get = NULL;
|
|
char repl_conninfo[MAXPGPATH];
|
|
int repl_array_length = 0;
|
|
int i = 0;
|
|
bool ret = false;
|
|
|
|
/* parse conninfo */
|
|
repl_conn_info = ParseReplConnInfo(conninfo_global[0], &repl_array_length);
|
|
if (repl_conn_info == NULL) {
|
|
pg_log(PG_PRINT, "%s: invalid value for parameter \"replconninfo1\" in postgresql.conf.\n", progname);
|
|
return ret;
|
|
}
|
|
|
|
/* check if we can get the connection. */
|
|
for (i = 0; i < repl_array_length; i++) {
|
|
if (repl_conn_info != NULL) {
|
|
int nRet = snprintf_s(repl_conninfo,
|
|
sizeof(repl_conninfo),
|
|
sizeof(repl_conninfo) - 1,
|
|
"localhost=%s localport=%d host=%s port=%d "
|
|
"dbname=replication replication=true "
|
|
"fallback_application_name=gs_ctl "
|
|
"connect_timeout=%d rw_timeout=%d ",
|
|
repl_conn_info[i].localhost,
|
|
repl_conn_info[i].localport,
|
|
repl_conn_info[i].remotehost,
|
|
repl_conn_info[i].remoteport,
|
|
conn_timeout,
|
|
recv_timeout);
|
|
securec_check_ss_c(nRet, "", "");
|
|
|
|
con_get = get_conn(repl_conninfo);
|
|
if (con_get != NULL) {
|
|
PQfinish(con_get);
|
|
con_get = NULL;
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (repl_conn_info != NULL) {
|
|
free(repl_conn_info);
|
|
repl_conn_info = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
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 = NULL;
|
|
int repl_arr_length = 0;
|
|
repl_conn_info = ParseReplConnInfo(conninfo_global[g_replconn_idx], &repl_arr_length);
|
|
if (repl_conn_info != NULL) {
|
|
errorno = snprintf_s(slotname, len, len - 1, "%s_%s_%d", pgxcnodename, repl_conn_info->localhost,
|
|
repl_conn_info->localport);
|
|
securec_check_ss_c(errorno, "", "");
|
|
free(repl_conn_info);
|
|
repl_conn_info = NULL;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|