1 /***********************************************************************************************************************************
2 Check Common Handler
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5
6 #include <string.h>
7
8 #include "command/check/common.h"
9 #include "common/debug.h"
10 #include "config/config.h"
11 #include "db/helper.h"
12 #include "info/infoArchive.h"
13 #include "info/infoBackup.h"
14 #include "postgres/interface.h"
15 #include "storage/helper.h"
16 #include "version.h"
17
18 /***********************************************************************************************************************************
19 Helper function
20 ***********************************************************************************************************************************/
21 static bool
checkArchiveCommand(const String * archiveCommand)22 checkArchiveCommand(const String *archiveCommand)
23 {
24 FUNCTION_TEST_BEGIN();
25 FUNCTION_TEST_PARAM(STRING, archiveCommand);
26 FUNCTION_TEST_END();
27
28 bool result = archiveCommand != NULL;
29
30 if (result && strstr(strZ(archiveCommand), PROJECT_BIN) == NULL)
31 result = false;
32
33 if (!result)
34 {
35 THROW_FMT(
36 ArchiveCommandInvalidError, "archive_command '%s' must contain %s",
37 archiveCommand != NULL ? strZ(archiveCommand) : "[" NULL_Z "]", PROJECT_BIN);
38 }
39
40 FUNCTION_TEST_RETURN(result);
41 }
42
43 /**********************************************************************************************************************************/
44 void
checkDbConfig(const unsigned int pgVersion,const unsigned int pgIdx,const Db * dbObject,bool isStandby)45 checkDbConfig(const unsigned int pgVersion, const unsigned int pgIdx, const Db *dbObject, bool isStandby)
46 {
47 FUNCTION_TEST_BEGIN();
48 FUNCTION_TEST_PARAM(UINT, pgVersion);
49 FUNCTION_TEST_PARAM(UINT, pgIdx);
50 FUNCTION_TEST_PARAM(DB, dbObject);
51 FUNCTION_TEST_PARAM(BOOL, isStandby);
52 FUNCTION_TEST_END();
53
54 ASSERT(dbObject != NULL);
55
56 MEM_CONTEXT_TEMP_BEGIN()
57 {
58 unsigned int dbVersion = dbPgVersion(dbObject);
59 const String *dbPath = dbPgDataPath(dbObject);
60
61 // Error if the version from the control file and the configured pg-path do not match the values obtained from the database
62 if (pgVersion != dbVersion || strCmp(cfgOptionIdxStr(cfgOptPgPath, pgIdx), dbPath) != 0)
63 {
64 THROW_FMT(
65 DbMismatchError, "version '%s' and path '%s' queried from cluster do not match version '%s' and '%s' read from '%s/"
66 PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "'\nHINT: the %s and %s settings likely reference different clusters.",
67 strZ(pgVersionToStr(dbVersion)), strZ(dbPath), strZ(pgVersionToStr(pgVersion)),
68 strZ(cfgOptionIdxDisplay(cfgOptPgPath, pgIdx)), strZ(cfgOptionIdxDisplay(cfgOptPgPath, pgIdx)),
69 cfgOptionIdxName(cfgOptPgPath, pgIdx), cfgOptionIdxName(cfgOptPgPort, pgIdx));
70 }
71
72 // Check archive configuration if option is valid for the command and set
73 if (!isStandby && cfgOptionValid(cfgOptArchiveCheck) && cfgOptionBool(cfgOptArchiveCheck))
74 {
75 // Error if archive_mode = off since pg_start_backup () will fail
76 if (strCmpZ(dbArchiveMode(dbObject), "off") == 0)
77 {
78 THROW(ArchiveDisabledError, "archive_mode must be enabled");
79 }
80
81 // Error if archive_mode = always unless check is disabled (support has not been added yet)
82 if (cfgOptionBool(cfgOptArchiveModeCheck) && strCmpZ(dbArchiveMode(dbObject), "always") == 0)
83 {
84 THROW(FeatureNotSupportedError, "archive_mode=always not supported");
85 }
86
87 // Check if archive_command is set and is valid
88 checkArchiveCommand(dbArchiveCommand(dbObject));
89 }
90 }
91 MEM_CONTEXT_TEMP_END();
92
93 FUNCTION_TEST_RETURN_VOID();
94 }
95
96 /**********************************************************************************************************************************/
97 void
checkStanzaInfo(const InfoPgData * archiveInfo,const InfoPgData * backupInfo)98 checkStanzaInfo(const InfoPgData *archiveInfo, const InfoPgData *backupInfo)
99 {
100 FUNCTION_TEST_BEGIN();
101 FUNCTION_TEST_PARAM_P(INFO_PG_DATA, archiveInfo);
102 FUNCTION_TEST_PARAM_P(INFO_PG_DATA, backupInfo);
103 FUNCTION_TEST_END();
104
105 ASSERT(archiveInfo != NULL);
106 ASSERT(backupInfo != NULL);
107
108 // Error if there is a mismatch between the archive and backup info files
109 if (archiveInfo->id != backupInfo->id || archiveInfo->systemId != backupInfo->systemId ||
110 archiveInfo->version != backupInfo->version)
111 {
112 THROW_FMT(
113 FileInvalidError, "backup info file and archive info file do not match\n"
114 "archive: id = %u, version = %s, system-id = %" PRIu64 "\n"
115 "backup : id = %u, version = %s, system-id = %" PRIu64 "\n"
116 "HINT: this may be a symptom of repository corruption!",
117 archiveInfo->id, strZ(pgVersionToStr(archiveInfo->version)), archiveInfo->systemId, backupInfo->id,
118 strZ(pgVersionToStr(backupInfo->version)), backupInfo->systemId);
119 }
120
121 FUNCTION_TEST_RETURN_VOID();
122 }
123
124 /**********************************************************************************************************************************/
125 void
checkStanzaInfoPg(const Storage * storage,const unsigned int pgVersion,const uint64_t pgSystemId,CipherType cipherType,const String * cipherPass)126 checkStanzaInfoPg(
127 const Storage *storage, const unsigned int pgVersion, const uint64_t pgSystemId, CipherType cipherType,
128 const String *cipherPass)
129 {
130 FUNCTION_TEST_BEGIN();
131 FUNCTION_LOG_PARAM(STORAGE, storage);
132 FUNCTION_LOG_PARAM(UINT, pgVersion);
133 FUNCTION_LOG_PARAM(UINT64, pgSystemId);
134 FUNCTION_LOG_PARAM(STRING_ID, cipherType);
135 FUNCTION_TEST_PARAM(STRING, cipherPass);
136 FUNCTION_TEST_END();
137
138 ASSERT(storage != NULL);
139
140 MEM_CONTEXT_TEMP_BEGIN()
141 {
142 // Check that the backup and archive info files exist
143 InfoArchive *infoArchive = infoArchiveLoadFile(storage, INFO_ARCHIVE_PATH_FILE_STR, cipherType, cipherPass);
144 InfoPgData archiveInfoPg = infoPgData(infoArchivePg(infoArchive), infoPgDataCurrentId(infoArchivePg(infoArchive)));
145 InfoBackup *infoBackup = infoBackupLoadFile(storage, INFO_BACKUP_PATH_FILE_STR, cipherType, cipherPass);
146 InfoPgData backupInfoPg = infoPgData(infoBackupPg(infoBackup), infoPgDataCurrentId(infoBackupPg(infoBackup)));
147
148 // Check that the info files pg data match each other
149 checkStanzaInfo(&archiveInfoPg, &backupInfoPg);
150
151 // Check that the version and system id match the current database
152 if (pgVersion != archiveInfoPg.version || pgSystemId != archiveInfoPg.systemId)
153 {
154 THROW(FileInvalidError, "backup and archive info files exist but do not match the database\n"
155 "HINT: is this the correct stanza?\n"
156 "HINT: did an error occur during stanza-upgrade?");
157 }
158 }
159 MEM_CONTEXT_TEMP_END();
160
161 FUNCTION_TEST_RETURN_VOID();
162 }
163