mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-22 22:56:29 +00:00
Check data directory not used by an active instance before cloning
This involves adding some infrastructure to parse pg_control in a reasonably version-independent fashion. This will probably be useful in other contexts where we need to verify the status of a data directory where the instance isn't running.
This commit is contained in:
@@ -27,7 +27,7 @@ include Makefile.global
|
|||||||
$(info Building against PostgreSQL $(MAJORVERSION))
|
$(info Building against PostgreSQL $(MAJORVERSION))
|
||||||
|
|
||||||
REPMGR_CLIENT_OBJS = repmgr-client.o repmgr-action-master.o repmgr-action-standby.o repmgr-action-cluster.o \
|
REPMGR_CLIENT_OBJS = repmgr-client.o repmgr-action-master.o repmgr-action-standby.o repmgr-action-cluster.o \
|
||||||
config.o log.o strutil.o dbutils.o dirutil.o compat.o
|
config.o log.o strutil.o dbutils.o dirutil.o compat.o controldata.o
|
||||||
REPMGRD_OBJS = repmgrd.o
|
REPMGRD_OBJS = repmgrd.o
|
||||||
|
|
||||||
$(REPMGR_CLIENT_OBJS): repmgr-client.h
|
$(REPMGR_CLIENT_OBJS): repmgr-client.h
|
||||||
@@ -58,6 +58,7 @@ additional-clean:
|
|||||||
rm -f repmgrd.o
|
rm -f repmgrd.o
|
||||||
rm -f compat.o
|
rm -f compat.o
|
||||||
rm -f config.o
|
rm -f config.o
|
||||||
|
rm -f controldata.o
|
||||||
rm -f dbutils.o
|
rm -f dbutils.o
|
||||||
rm -f dirutil.o
|
rm -f dirutil.o
|
||||||
rm -f log.o
|
rm -f log.o
|
||||||
|
|||||||
110
controldata.c
Normal file
110
controldata.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* controldata.c
|
||||||
|
* Copyright (c) 2ndQuadrant, 2010-2017
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "postgres_fe.h"
|
||||||
|
#include "port/pg_crc32c.h"
|
||||||
|
|
||||||
|
#include "repmgr.h"
|
||||||
|
#include "controldata.h"
|
||||||
|
|
||||||
|
static ControlFileInfo *get_controlfile(char *DataDir);
|
||||||
|
|
||||||
|
DBState
|
||||||
|
get_db_state(char *data_directory)
|
||||||
|
{
|
||||||
|
ControlFileInfo *control_file_info;
|
||||||
|
DBState state;
|
||||||
|
|
||||||
|
control_file_info = get_controlfile(data_directory);
|
||||||
|
|
||||||
|
if (control_file_info->control_file_processed == true)
|
||||||
|
state = control_file_info->control_file->state;
|
||||||
|
else
|
||||||
|
/* if we were unable to parse the control file, assume DB is shut down */
|
||||||
|
state = DB_SHUTDOWNED;
|
||||||
|
|
||||||
|
pfree(control_file_info->control_file);
|
||||||
|
pfree(control_file_info);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
describe_db_state(DBState state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case DB_STARTUP:
|
||||||
|
return _("starting up");
|
||||||
|
case DB_SHUTDOWNED:
|
||||||
|
return _("shut down");
|
||||||
|
case DB_SHUTDOWNED_IN_RECOVERY:
|
||||||
|
return _("shut down in recovery");
|
||||||
|
case DB_SHUTDOWNING:
|
||||||
|
return _("shutting down");
|
||||||
|
case DB_IN_CRASH_RECOVERY:
|
||||||
|
return _("in crash recovery");
|
||||||
|
case DB_IN_ARCHIVE_RECOVERY:
|
||||||
|
return _("in archive recovery");
|
||||||
|
case DB_IN_PRODUCTION:
|
||||||
|
return _("in production");
|
||||||
|
}
|
||||||
|
return _("unrecognized status code");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we maintain our own version of get_controlfile() as we don't care if
|
||||||
|
* the file isn't readable
|
||||||
|
*/
|
||||||
|
static ControlFileInfo *
|
||||||
|
get_controlfile(char *DataDir)
|
||||||
|
{
|
||||||
|
ControlFileInfo *control_file_info;
|
||||||
|
int fd;
|
||||||
|
char ControlFilePath[MAXPGPATH];
|
||||||
|
pg_crc32c crc;
|
||||||
|
|
||||||
|
control_file_info = palloc0(sizeof(ControlFileInfo));
|
||||||
|
control_file_info->control_file_processed = false;
|
||||||
|
control_file_info->control_file = palloc0(sizeof(ControlFileData));
|
||||||
|
|
||||||
|
snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
|
||||||
|
|
||||||
|
if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
|
||||||
|
{
|
||||||
|
log_debug("could not open file \"%s\" for reading: %s",
|
||||||
|
ControlFilePath, strerror(errno));
|
||||||
|
return control_file_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(fd, control_file_info->control_file, sizeof(ControlFileData)) != sizeof(ControlFileData))
|
||||||
|
{
|
||||||
|
log_debug("could not read file \"%s\": %s",
|
||||||
|
ControlFilePath, strerror(errno));
|
||||||
|
return control_file_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
control_file_info->control_file_processed = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't check the CRC here as we're potentially checking a pg_control
|
||||||
|
* file from a different PostgreSQL version to the one repmgr was compiled
|
||||||
|
* against. However we're only interested in the first few fields, which
|
||||||
|
* should be constant across supported versions
|
||||||
|
*
|
||||||
|
* XXX double-check this
|
||||||
|
*/
|
||||||
|
|
||||||
|
return control_file_info;
|
||||||
|
}
|
||||||
27
controldata.h
Normal file
27
controldata.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* controldata.h
|
||||||
|
* Copyright (c) 2ndQuadrant, 2010-2017
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CONTROLDATA_H_
|
||||||
|
#define _CONTROLDATA_H_
|
||||||
|
|
||||||
|
#include "postgres_fe.h"
|
||||||
|
#include "catalog/pg_control.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool control_file_processed;
|
||||||
|
ControlFileData *control_file;
|
||||||
|
} ControlFileInfo;
|
||||||
|
|
||||||
|
extern DBState
|
||||||
|
get_db_state(char *data_directory);
|
||||||
|
|
||||||
|
extern const char *
|
||||||
|
describe_db_state(DBState state);
|
||||||
|
|
||||||
|
#endif /* _CONTROLDATA_H_ */
|
||||||
@@ -138,7 +138,7 @@ do_master_register(void)
|
|||||||
node_info.priority = config_file_options.priority;
|
node_info.priority = config_file_options.priority;
|
||||||
|
|
||||||
initPQExpBuffer(&event_description);
|
initPQExpBuffer(&event_description);
|
||||||
puts("here");
|
|
||||||
if (record_found)
|
if (record_found)
|
||||||
{
|
{
|
||||||
record_created = update_node_record(conn,
|
record_created = update_node_record(conn,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "repmgr.h"
|
#include "repmgr.h"
|
||||||
#include "dirutil.h"
|
#include "dirutil.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "controldata.h"
|
||||||
|
|
||||||
#include "repmgr-client-global.h"
|
#include "repmgr-client-global.h"
|
||||||
#include "repmgr-action-standby.h"
|
#include "repmgr-action-standby.h"
|
||||||
@@ -148,15 +149,21 @@ do_standby_clone(void)
|
|||||||
}
|
}
|
||||||
else if (mode == barman)
|
else if (mode == barman)
|
||||||
{
|
{
|
||||||
|
/* XXX in Barman mode it's still possible to connect to the upstream,
|
||||||
|
* so only fail if that's not available.
|
||||||
|
*/
|
||||||
log_error(_("Barman mode requires a data directory"));
|
log_error(_("Barman mode requires a data directory"));
|
||||||
log_hint(_("use -D/--pgdata to explicitly specify a data directory"));
|
log_hint(_("use -D/--pgdata to explicitly specify a data directory"));
|
||||||
exit(ERR_BAD_CONFIG);
|
exit(ERR_BAD_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* target directory (-D/--pgdata) provided - use that as new data directory
|
* Target directory (-D/--pgdata) provided - use that as new data directory
|
||||||
* (useful when executing backup on local machine only or creating the backup
|
* (useful when executing backup on local machine only or creating the backup
|
||||||
* in a different local directory when backup source is a remote host)
|
* in a different local directory when backup source is a remote host)
|
||||||
|
*
|
||||||
|
* Note: if no directory provided, check_source_server() will later set
|
||||||
|
* local_data_directory from the upstream configuration.
|
||||||
*/
|
*/
|
||||||
if (local_data_directory_provided == true)
|
if (local_data_directory_provided == true)
|
||||||
{
|
{
|
||||||
@@ -245,6 +252,23 @@ do_standby_clone(void)
|
|||||||
check_source_server_via_barman();
|
check_source_server_via_barman();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* by this point we should know the target data directory - check
|
||||||
|
* there's no running Pg instance
|
||||||
|
*/
|
||||||
|
if (is_pg_dir(local_data_directory))
|
||||||
|
{
|
||||||
|
DBState state = get_db_state(local_data_directory);
|
||||||
|
|
||||||
|
if (state != DB_SHUTDOWNED && state != DB_SHUTDOWNED_IN_RECOVERY)
|
||||||
|
{
|
||||||
|
log_error(_("target data directory appears to contain an active PostgreSQL instance"));
|
||||||
|
log_detail(_("instance state is %s"), describe_db_state(state));
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (upstream_record_found == true)
|
if (upstream_record_found == true)
|
||||||
{
|
{
|
||||||
/* parse returned upstream conninfo string to recovery primary_conninfo params*/
|
/* parse returned upstream conninfo string to recovery primary_conninfo params*/
|
||||||
|
|||||||
Reference in New Issue
Block a user