Files
openGauss-server/src/bin/pg_probackup/remote.cpp

292 lines
8.6 KiB
C++

/*-------------------------------------------------------------------------
*
* remote.c
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
*
*-------------------------------------------------------------------------
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#ifdef WIN32
#define __thread __declspec(thread)
#else
#include <pthread.h>
#endif
#include "pg_probackup.h"
#include "file.h"
#include "common/fe_memutils.h"
#define MAX_CMDLINE_LENGTH 4096
#define MAX_CMDLINE_OPTIONS 256
/* is not used now #define ERR_BUF_SIZE 4096 may be removed in the future*/
#define PIPE_SIZE (64*1024)
static int split_options(int argc, char* argv[], int max_options, char* options)
{
char* opt = options;
char in_quote = '\0';
while (true) {
switch (*opt) {
case '\'':
case '\"':
if (!in_quote) {
in_quote = *opt++;
continue;
}
if (*opt == in_quote && *++opt != in_quote) {
in_quote = '\0';
continue;
}
break;
case '\0':
if (opt != options) {
argv[argc++] = options;
if (argc >= max_options)
elog(ERROR, "Too much options");
}
return argc;
case ' ':
argv[argc++] = options;
if (argc >= max_options)
elog(ERROR, "Too much options");
*opt++ = '\0';
options = opt;
continue;
default:
break;
}
opt += 1;
}
return argc;
}
static __thread int child_pid;
void wait_ssh(void)
{
/*
* We need to wait termination of SSH process to eliminate zombies.
* There is no waitpid() function at Windows but there are no zombie processes caused by lack of wait/waitpid.
* So just disable waitpid for Windows.
*/
#ifndef WIN32
int status;
waitpid(child_pid, &status, 0);
elog(LOG, "SSH process %d is terminated with status %d", child_pid, status);
#endif
}
#ifdef WIN32
void launch_ssh(char* argv[])
{
int infd = atoi(argv[2]);
int outfd = atoi(argv[3]);
SYS_CHECK(close(STDIN_FILENO));
SYS_CHECK(close(STDOUT_FILENO));
SYS_CHECK(dup2(infd, STDIN_FILENO));
SYS_CHECK(dup2(outfd, STDOUT_FILENO));
SYS_CHECK(execvp(argv[4], argv+4));
}
#endif
static bool needs_quotes(char const* path)
{
return strchr(path, ' ') != NULL;
}
bool launch_agent(void)
{
char cmd[MAX_CMDLINE_LENGTH];
char* ssh_argv[MAX_CMDLINE_OPTIONS];
char libenv[MAX_CMDLINE_LENGTH] = {0};
int ssh_argc;
int outfd[2];
int infd[2];
int errfd[2];
int agent_version;
errno_t rc = 0;
ssh_argc = 0;
#ifdef WIN32
ssh_argv[ssh_argc++] = PROGRAM_NAME_FULL;
ssh_argv[ssh_argc++] = (char *)"ssh";
ssh_argc += 2; /* reserve space for pipe descriptors */
#endif
ssh_argv[ssh_argc++] = (char *)instance_config.remote.proto;
if (instance_config.remote.port != NULL) {
ssh_argv[ssh_argc++] = (char *)"-p";
ssh_argv[ssh_argc++] = (char *)instance_config.remote.port;
}
if (instance_config.remote.user != NULL) {
ssh_argv[ssh_argc++] = (char *)"-l";
ssh_argv[ssh_argc++] = (char *)instance_config.remote.user;
}
if (instance_config.remote.ssh_config != NULL) {
ssh_argv[ssh_argc++] = (char *)"-F";
ssh_argv[ssh_argc++] = (char *)instance_config.remote.ssh_config;
}
if (instance_config.remote.ssh_options != NULL) {
ssh_argc = split_options(ssh_argc, (char **)const_cast<char **>(ssh_argv), MAX_CMDLINE_OPTIONS, pg_strdup(instance_config.remote.ssh_options));
}
ssh_argv[ssh_argc++] = (char *)"-o";
ssh_argv[ssh_argc++] = (char *)"PasswordAuthentication=no";
ssh_argv[ssh_argc++] = (char *)"-o";
ssh_argv[ssh_argc++] = (char *)"Compression=no";
ssh_argv[ssh_argc++] = (char *)"-o";
ssh_argv[ssh_argc++] = (char *)"LogLevel=error";
ssh_argv[ssh_argc++] = (char *)instance_config.remote.host;
ssh_argv[ssh_argc++] = (char *)cmd;
ssh_argv[ssh_argc] = NULL;
if (instance_config.remote.libpath)
{
#ifdef WIN32
rc = snprintf_s(libenv, sizeof(libenv), sizeof(libenv) - 1, "set LD_LIBRARY_PATH=%s &&",
instance_config.remote.libpath);
securec_check_ss_c(rc, "\0", "\0");
#else
rc = snprintf_s(libenv, sizeof(libenv), sizeof(libenv) - 1, "export LD_LIBRARY_PATH=%s &&",
instance_config.remote.libpath);
securec_check_ss_c(rc, "\0", "\0");
#endif
}
if (instance_config.remote.path)
{
char const* probackup = PROGRAM_NAME_FULL;
char* sep = (char *)strrchr(probackup, '/');
if (sep != NULL) {
probackup = sep + 1;
}
#ifdef WIN32
else {
sep = strrchr(probackup, '\\');
if (sep != NULL) {
probackup = sep + 1;
}
}
if (needs_quotes(instance_config.remote.path) || needs_quotes(PROGRAM_NAME_FULL))
{
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "\"%s %s\\%s\" agent",
libenv, instance_config.remote.path, probackup);
securec_check_ss_c(rc, "\0", "\0");
}
else
{
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "%s %s\\%s agent",
libenv, instance_config.remote.path, probackup);
securec_check_ss_c(rc, "\0", "\0");
}
#else
if (needs_quotes(instance_config.remote.path) || needs_quotes(PROGRAM_NAME_FULL))
{
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "\"%s %s/%s\" agent",
libenv, instance_config.remote.path, probackup);
securec_check_ss_c(rc, "\0", "\0");
}
else
{
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "%s %s/%s agent",
libenv, instance_config.remote.path, probackup);
securec_check_ss_c(rc, "\0", "\0");
}
#endif
} else {
if (needs_quotes(PROGRAM_NAME_FULL))
{
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "\"%s\" agent", PROGRAM_NAME_FULL);
securec_check_ss_c(rc, "\0", "\0");
}
else
{
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "%s agent", PROGRAM_NAME_FULL);
securec_check_ss_c(rc, "\0", "\0");
}
}
#ifdef WIN32
SYS_CHECK(_pipe(infd, PIPE_SIZE, _O_BINARY)) ;
SYS_CHECK(_pipe(outfd, PIPE_SIZE, _O_BINARY));
ssh_argv[2] = (const char *)format_text("%d", outfd[0]);
ssh_argv[3] = (const char *)format_text("%d", infd[1]);
{
intptr_t pid = _spawnvp(_P_NOWAIT, ssh_argv[0], ssh_argv);
if (pid < 0)
return false;
child_pid = GetProcessId((HANDLE)pid);
#else
SYS_CHECK(pipe(infd));
SYS_CHECK(pipe(outfd));
SYS_CHECK(pipe(errfd));
SYS_CHECK(child_pid = fork());
if (child_pid == 0) { /* child */
SYS_CHECK(close(STDIN_FILENO));
SYS_CHECK(close(STDOUT_FILENO));
SYS_CHECK(close(STDERR_FILENO));
SYS_CHECK(dup2(outfd[0], STDIN_FILENO));
SYS_CHECK(dup2(infd[1], STDOUT_FILENO));
SYS_CHECK(dup2(errfd[1], STDERR_FILENO));
SYS_CHECK(close(infd[0]));
SYS_CHECK(close(infd[1]));
SYS_CHECK(close(outfd[0]));
SYS_CHECK(close(outfd[1]));
SYS_CHECK(close(errfd[0]));
SYS_CHECK(close(errfd[1]));
if (execvp(ssh_argv[0], ssh_argv) < 0) {
return false;
}
} else {
#endif
elog(LOG, "Start SSH client process, pid %d", child_pid);
SYS_CHECK(close(infd[1])); /* These are being used by the child */
SYS_CHECK(close(outfd[0]));
SYS_CHECK(close(errfd[1]));
fio_redirect(infd[0], outfd[1], errfd[0]); /* write to stdout */
}
/* Make sure that remote agent has the same version
* TODO: we must also check PG version and fork edition
*/
agent_version = fio_get_agent_version();
if (agent_version != AGENT_PROTOCOL_VERSION)
{
char agent_version_str[1024];
errno_t rc = sprintf_s(agent_version_str, 1024, "%d.%d.%d",
agent_version / 10000,
(agent_version / 100) % 100,
agent_version % 100);
securec_check_ss_c(rc, "\0", "\0");
elog(ERROR, "Remote agent version %s does not match local program version %s",
agent_version_str, PROGRAM_VERSION);
}
return true;
}