1102 lines
32 KiB
C++
1102 lines
32 KiB
C++
/*
|
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 2021, openGauss Contributors
|
|
*
|
|
* openGauss is licensed under Mulan PSL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
* You may obtain a copy of Mulan PSL v2 at:
|
|
*
|
|
* http://license.coscl.org.cn/MulanPSL2
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PSL v2 for more details.
|
|
* -------------------------------------------------------------------------
|
|
*
|
|
* gaussdb_version.cpp
|
|
*
|
|
* Disable some features by parsing configuration file.
|
|
* This configuration file named 'gaussdb.version' was generated by OM script.
|
|
* We parse this configuration file and initialize global private variable 'disabled_features_flag',
|
|
* so that we can call is_feature_disabled() function to check if one feature was disabed.
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/process/postmaster/gaussdb_version.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "securec.h"
|
|
#include "securec_check.h"
|
|
#include "utils/elog.h"
|
|
#include "utils/pg_crc.h"
|
|
#include "utils/palloc.h"
|
|
#include "libpq/sha2.h"
|
|
#include "alarm/alarm.h"
|
|
#include "libpq/pqformat.h"
|
|
#include "gaussdb_version.h"
|
|
#include "gssignal/gs_signal.h"
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
#include "pgxc/pgxc.h"
|
|
|
|
extern THR_LOCAL char* application_name;
|
|
|
|
/* The maximum number of the gaussdb features. */
|
|
#define MAX_FEATURE_NUM 1024
|
|
/* The number of the sha256 count, it must be equal with the number of the product version. */
|
|
#define SHA256_DIGESTS_COUNT 2
|
|
/* The maximun length of the feature name. */
|
|
#define MAX_ALARM_FEATURE_NAME_LEN 64
|
|
|
|
/* The standard edition license name. */
|
|
#define STANDARD_EDITION_NAME "Standard Edition License"
|
|
/* The premium edition license name. */
|
|
#define PREMIUM_EDITION_NAME "Advanced Edition License"
|
|
/* The premium feature edition name. */
|
|
#define FEATURE_VECTOR_ENGINE_NAME "Feature-Vector Engine License"
|
|
|
|
/* Product version control file name. */
|
|
#define PRODUCT_VERSION_FILE "gaussdb.version"
|
|
/* License version control file name. */
|
|
#define LICENSE_VERSION_FILE "gaussdb.license"
|
|
|
|
/* Application OM. */
|
|
#define APPLICATION_OM "OM"
|
|
|
|
/* The feature report time interval. */
|
|
#define ALARM_REPORT_TIME_INTERVAL ((time_t)(24 * 60 * 60))
|
|
|
|
typedef struct {
|
|
bool disabled_features_flag; /**
|
|
* Set the default value, means that enable all feature by default.
|
|
* The disabled status is mandatory, user can not use the disabled features.
|
|
*/
|
|
bool isReported; /* Whether the alarm is reported. */
|
|
time_t lastReportTime; /* Save the alarm last reported time. */
|
|
Alarm alarmPermissionDenied; /* Alarm item that used to report the permission denied alarm. */
|
|
} FeatureInfo;
|
|
|
|
typedef struct {
|
|
const char* productFileName; /* The default control file name. */
|
|
uint32 licenseVersion; /* The version flag in the control file. */
|
|
FeatureInfo featureInfo[MAX_FEATURE_NUM]; /* The feature information item. */
|
|
bool isLoaded; /* Whether the feature control file is loaded. */
|
|
} LicenseControl; /* The data structure that used to store the control file information. */
|
|
|
|
/* The control file header information. */
|
|
typedef struct {
|
|
unsigned int crc32_code; /* Store the crc32 code of the data information. */
|
|
unsigned int base64_data_len; /* Store the data length of the data information after the base64 decode. */
|
|
unsigned int data_len; /* Store the data length of the data information. */
|
|
} version_header;
|
|
|
|
/* The control file data information. */
|
|
typedef struct {
|
|
unsigned int product_flag; /* Store the product flag and the license flag(reversed). */
|
|
unsigned int disabled_feature_len; /* Store the number of the disabled features. */
|
|
unsigned short disabled_feature_list[1]; /* Store the disabled feature id. */
|
|
} version_data;
|
|
|
|
/* The control file information. */
|
|
typedef struct {
|
|
version_header* header; /* Store the control file header information. */
|
|
version_data* data; /* Store the control file data information. */
|
|
} gaussdb_version;
|
|
|
|
typedef struct {
|
|
const char* featureName;
|
|
const feature_name feature;
|
|
} feature_name_map;
|
|
|
|
/* Flag that whether the license module is initialized. */
|
|
static bool isInitialized = false;
|
|
/**
|
|
* The data of this array will be filled in the package operation.
|
|
* And reset after the package operation.
|
|
* Please do not modify it.
|
|
*/
|
|
const char *sha256_digests[SHA256_DIGESTS_COUNT] = {"5237e9ad5b6ecf8d0abba664972bdcb106595b9ec2f52083915e7c829d348f0d",
|
|
"06354c2857fbf21e5862005a7e60ad210dc4b635dbde891d6e60cbddea465b16"};
|
|
/* The product control file information. */
|
|
static LicenseControl versionControl = {PRODUCT_VERSION_FILE, PRODUCT_VERSION_UNKNOWN, {{0}}, false};
|
|
/* The license control file information. */
|
|
static LicenseControl licenseControl = {LICENSE_VERSION_FILE, 0, {{0}}, false};
|
|
|
|
/* The Premium edition license features. */
|
|
static const feature_name ADVANCED_EDITION[] = {
|
|
EXPRESS_CLUSTER, CROSS_DC_COLLABORATION, ROW_LEVEL_SECURITY, TRANSPARENT_ENCRYPTION, PRIVATE_TABLE};
|
|
/* The map between the feature name and feature id. */
|
|
static const feature_name_map featureNameMap[] = {{"Multi Value Column", MULTI_VALUE_COLUMN},
|
|
{"Json", JSON},
|
|
{"Xml", XML},
|
|
{"Data Storge Format", DATA_STORAGE_FORMAT},
|
|
{"GTM Free", GTM_FREE},
|
|
{"Double Live Disaster Recovery In The Same City", DOUBLE_LIVE_DISASTER_RECOVERY_IN_THE_SAME_CITY},
|
|
{"Disaster Recovery In Two Places And Three Centers", DISASTER_RECOVERY_IN_TWO_PLACES_AND_THREE_CENTRES},
|
|
{"GPU Acceleration In Multidimensional Collision Analysis",
|
|
GPU_ACCELERATION_IN_MULTIDIMENSIONAL_COLLISION_ANALYSIS},
|
|
{"Full Text Index", FULL_TEXT_INDEX},
|
|
{"Extension Connector", EXTENSION_CONNECTOR},
|
|
{"Sql On HDFS", SQL_ON_HDFS},
|
|
{"Sql On OBS", SQL_ON_OBS},
|
|
{"Express Cluster", EXPRESS_CLUSTER},
|
|
{"Cross DC Collaboration", CROSS_DC_COLLABORATION},
|
|
{"Graph Computing Engine", GRAPH_COMPUTING_ENGINE},
|
|
{"Sequential Data Engine", SEQUENTIAL_DATA_ENGINE},
|
|
{"PostGis Docking", POSTGIS_DOCKING},
|
|
{"HA Single Primary Multi Standby", HA_SINGLE_PRIMARY_MULTI_STANDBY},
|
|
{"Row Level Security", ROW_LEVEL_SECURITY},
|
|
{"Transparent Encryption", TRANSPARENT_ENCRYPTION},
|
|
{"Private Table", PRIVATE_TABLE}};
|
|
/* These feature will report the alarm on the DN, we will not limit it. */
|
|
static const feature_name EXCEPT_FEATURES[] = {EXTENSION_CONNECTOR};
|
|
/* Skip the application name. */
|
|
static const char* APPLICATION_WHITE_LIST[] = {APPLICATION_OM};
|
|
|
|
static int base64_decode(const unsigned char*, unsigned int, char*, unsigned int*);
|
|
extern "C" {
|
|
static int parse_gaussdb_version_file(gaussdb_version*, const char*);
|
|
}
|
|
static int sha256_encode(const unsigned char*, size_t, char*, size_t);
|
|
static int loadConrtolData(LicenseControl* control);
|
|
static void reportPermissionDeniedAlarm(feature_name feature);
|
|
static void reportResumeAlarm();
|
|
static const char* getLicenseEditionName(feature_name feature);
|
|
static const char* getFeatureName(feature_name feature, char* alarmFeatureName, size_t alarmFeatureNameLen);
|
|
|
|
/**
|
|
* @brief: Get the product version number.
|
|
*
|
|
* @return uint32: Return the product version number.
|
|
*/
|
|
uint32 get_product_version()
|
|
{
|
|
if (!versionControl.isLoaded) {
|
|
return PRODUCT_VERSION_UNKNOWN;
|
|
}
|
|
|
|
return versionControl.licenseVersion;
|
|
}
|
|
|
|
/**
|
|
* @brief: Decode the base64 data.
|
|
*
|
|
* @param [in] indata: The input data.
|
|
* @param [in] inlen: The size of the input data.
|
|
* @param [out] outdata: The output data, you should give enough space.
|
|
* @param [in] outlen: The size of output data.
|
|
*
|
|
* @return 0: Success
|
|
* @return -1: Failure
|
|
*/
|
|
int base64_decode(const unsigned char* indata, unsigned int inlen, char* outdata, unsigned int* outlen)
|
|
{
|
|
static const unsigned char base64_suffix_map[256] = {255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
253,
|
|
255,
|
|
255,
|
|
253,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
253,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
62,
|
|
255,
|
|
255,
|
|
255,
|
|
63,
|
|
52,
|
|
53,
|
|
54,
|
|
55,
|
|
56,
|
|
57,
|
|
58,
|
|
59,
|
|
60,
|
|
61,
|
|
255,
|
|
255,
|
|
255,
|
|
254,
|
|
255,
|
|
255,
|
|
255,
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6,
|
|
7,
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
12,
|
|
13,
|
|
14,
|
|
15,
|
|
16,
|
|
17,
|
|
18,
|
|
19,
|
|
20,
|
|
21,
|
|
22,
|
|
23,
|
|
24,
|
|
25,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
26,
|
|
27,
|
|
28,
|
|
29,
|
|
30,
|
|
31,
|
|
32,
|
|
33,
|
|
34,
|
|
35,
|
|
36,
|
|
37,
|
|
38,
|
|
39,
|
|
40,
|
|
41,
|
|
42,
|
|
43,
|
|
44,
|
|
45,
|
|
46,
|
|
47,
|
|
48,
|
|
49,
|
|
50,
|
|
51,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
255};
|
|
unsigned int t = 0;
|
|
unsigned int x = 0;
|
|
unsigned int y = 0;
|
|
unsigned int i = 0;
|
|
unsigned char c = 0;
|
|
int g = 3;
|
|
|
|
if (indata == NULL || inlen == 0 || outdata == NULL || outlen == NULL || inlen % 4 != 0) {
|
|
ereport(WARNING, (errmsg("input arguments are error, please check those.")));
|
|
goto error;
|
|
}
|
|
|
|
while (x < inlen && indata[x] != 0) {
|
|
c = base64_suffix_map[indata[x++]];
|
|
if (c == 255) {
|
|
break;
|
|
}
|
|
if (c == 253) {
|
|
continue;
|
|
}
|
|
if (c == 254) {
|
|
c = 0;
|
|
g--;
|
|
}
|
|
t = (t << 6) | c;
|
|
if (++y == 4) {
|
|
outdata[i++] = (unsigned char)((t >> 16) & 0xff);
|
|
if (g > 1) {
|
|
outdata[i++] = (unsigned char)((t >> 8) & 0xff);
|
|
}
|
|
if (g > 2) {
|
|
outdata[i++] = (unsigned char)(t & 0xff);
|
|
}
|
|
y = t = 0;
|
|
}
|
|
}
|
|
|
|
*outlen = i;
|
|
return 0;
|
|
|
|
error:
|
|
ereport(WARNING, (errmsg("failed to decode base64 for configuration file.")));
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the sha256 digest.
|
|
*
|
|
* @param [in] indata: The input data.
|
|
* @param [in] inlen: The size of the input data.
|
|
* @param [out] outdata: The output data.
|
|
* @param [in] outlen: The size of the output data.
|
|
*
|
|
* @return 0: Success
|
|
* @return -1: Failure
|
|
*/
|
|
int sha256_encode(const unsigned char* indata, size_t inlen, char* outdata, size_t outlen)
|
|
{
|
|
SHA256_CTX2 sha256_ctx;
|
|
unsigned char buf[SHA256_DIGEST_LENGTH] = {0};
|
|
errno_t rc;
|
|
|
|
if (outlen < SHA256_DIGEST_STRING_LENGTH)
|
|
return -1;
|
|
|
|
SHA256_Init2(&sha256_ctx);
|
|
SHA256_Update2(&sha256_ctx, indata, inlen);
|
|
SHA256_Final2(buf, &sha256_ctx);
|
|
|
|
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
|
rc = sprintf_s(outdata + i * 2, outlen - i * 2, "%02x", buf[i]);
|
|
if (rc == -1) {
|
|
ereport(WARNING, (errmsg("failed to get the sha256 digest for configuration file.")));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
outdata[SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @beief: Get the License Edition by the feature id.
|
|
*
|
|
* @param feature: The feature id.
|
|
*
|
|
* @return: Return the License Edition name. If the feature is not in the premium edition and the premium feature
|
|
* edition, Return NULL.
|
|
*/
|
|
const char* getLicenseEditionName(feature_name feature)
|
|
{
|
|
for (size_t i = 0; i < sizeof(ADVANCED_EDITION) / sizeof(feature_name); i++) {
|
|
if (ADVANCED_EDITION[i] == feature) {
|
|
return PREMIUM_EDITION_NAME;
|
|
}
|
|
}
|
|
|
|
if (feature == GPU_ACCELERATION_IN_MULTIDIMENSIONAL_COLLISION_ANALYSIS) {
|
|
return FEATURE_VECTOR_ENGINE_NAME;
|
|
}
|
|
|
|
/* Other will be treated as a standard feature. */
|
|
if (feature < FEATURE_NAME_MAX_VALUE) {
|
|
return STANDARD_EDITION_NAME;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief: Get the feature name by the feature id.
|
|
*
|
|
* @param feature: The input feature id.
|
|
* @param alarmFeatureName: Store the feature name with alarm type.
|
|
* @param alarmFeatureNameLen: The feature name buffer length.
|
|
*
|
|
* @return: Return the specified feature name, if feature id is illegal, return "Invalid Feature Name".
|
|
*/
|
|
const char* getFeatureName(feature_name feature, char* alarmFeatureName, size_t alarmFeatureNameLen)
|
|
{
|
|
errno_t ret = 0;
|
|
const char* featureName = NULL;
|
|
|
|
/* Get the feature name. */
|
|
for (unsigned long long i = 0; i < sizeof(featureNameMap) / sizeof(feature_name_map); i++) {
|
|
if (featureNameMap[i].feature == feature) {
|
|
featureName = featureNameMap[i].featureName;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If the feature name does not exist, return the default name. */
|
|
if (featureName == NULL) {
|
|
featureName = "Unknown Feature Name";
|
|
}
|
|
|
|
if (alarmFeatureName != NULL && alarmFeatureNameLen > 0) {
|
|
ret = strncpy_s(alarmFeatureName, alarmFeatureNameLen, featureName, strlen(featureName));
|
|
securec_check_c(ret, "", "");
|
|
|
|
/* Replace the feature name to alarm type. */
|
|
for (size_t i = 0; i < alarmFeatureNameLen; i++) {
|
|
if (alarmFeatureName[i] == ' ') {
|
|
alarmFeatureName[i] = '_';
|
|
}
|
|
}
|
|
}
|
|
|
|
return featureName;
|
|
}
|
|
|
|
/**
|
|
* @brief Check the report condition.
|
|
* The EC feature will report on the random DN instance.
|
|
* The other feature will report on the Coordinator instance with external connection.
|
|
*/
|
|
bool checkAlarmReportCondition(feature_name feature)
|
|
{
|
|
/* If the application name is in the white list, we will not report the alarm. */
|
|
if (u_sess->attr.attr_common.application_name != NULL) {
|
|
for (size_t i = 0; i < lengthof(APPLICATION_WHITE_LIST); i++) {
|
|
if (strcmp(u_sess->attr.attr_common.application_name, APPLICATION_WHITE_LIST[i]) == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Only the coordinator which was connected by external can send alram. */
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
|
|
return true;
|
|
}
|
|
|
|
/* The DataNode will only report the EC feature. */
|
|
for (size_t i = 0; i < sizeof(EXCEPT_FEATURES) / sizeof(feature_name); i++) {
|
|
if (EXCEPT_FEATURES[i] == feature && IS_PGXC_DATANODE) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief: Report an alarm that the target feature was not authorized to use.
|
|
*
|
|
* @param [in] feature: The feature id.
|
|
*
|
|
* @return: void
|
|
*/
|
|
void reportPermissionDeniedAlarm(feature_name feature)
|
|
{
|
|
AlarmAdditionalParam tempAdditionalParam;
|
|
const char* edition = NULL;
|
|
const char* featureName = NULL;
|
|
char alarmFeatureName[MAX_ALARM_FEATURE_NAME_LEN] = {0};
|
|
|
|
/* Check the alarm report condition. */
|
|
if (!checkAlarmReportCondition(feature)) {
|
|
return;
|
|
}
|
|
|
|
/* We don't alarm the feature except the premium edition and the premium feature edition. */
|
|
edition = getLicenseEditionName(feature);
|
|
if (edition == NULL) {
|
|
return;
|
|
}
|
|
/* Get the feature name. */
|
|
featureName = getFeatureName(feature, alarmFeatureName, sizeof(alarmFeatureName));
|
|
|
|
/**
|
|
* We will not report the alarm. Because the previous alarm has been reported, but the time interval for
|
|
* re-reporting has not yet been reached.
|
|
*/
|
|
if (time(NULL) - ALARM_REPORT_TIME_INTERVAL <= licenseControl.featureInfo[feature].lastReportTime &&
|
|
licenseControl.featureInfo[feature].isReported) {
|
|
return;
|
|
}
|
|
|
|
/* fill the alarm message */
|
|
WriteAlarmAdditionalInfo(&tempAdditionalParam,
|
|
"",
|
|
alarmFeatureName,
|
|
"",
|
|
&licenseControl.featureInfo[feature].alarmPermissionDenied,
|
|
ALM_AT_Fault,
|
|
featureName,
|
|
edition);
|
|
|
|
/* report the alarm */
|
|
AlarmReporter(&licenseControl.featureInfo[feature].alarmPermissionDenied, ALM_AT_Fault, &tempAdditionalParam);
|
|
|
|
/* Set the alarm is reported. */
|
|
licenseControl.featureInfo[feature].isReported = true;
|
|
/* Save the reported time. */
|
|
licenseControl.featureInfo[feature].lastReportTime = time(NULL);
|
|
|
|
ereport(LOG,
|
|
(errmsg(
|
|
"Report an alarm that the feature \"%s\" was not authorized to use on the \"%s\".", featureName, edition)));
|
|
}
|
|
|
|
/**
|
|
* @brief: Report some resume alarm for the reported features that these features was authorized to use now.
|
|
*
|
|
* @return: void
|
|
*/
|
|
void reportResumeAlarm()
|
|
{
|
|
for (int i = 0; i < FEATURE_NAME_MAX_VALUE; i++) {
|
|
char alarmFeatureName[MAX_ALARM_FEATURE_NAME_LEN] = {0};
|
|
|
|
/**
|
|
* Report the resumed alarm:
|
|
* 1. Now the feature is enabled.
|
|
*/
|
|
if (licenseControl.featureInfo[i].disabled_features_flag == false) {
|
|
AlarmAdditionalParam tempAdditionalParam;
|
|
const char* edition = NULL;
|
|
const char* featureName = NULL;
|
|
|
|
/* We don't alarm the feature except the premium edition and the premium feature edition. */
|
|
edition = getLicenseEditionName((feature_name)i);
|
|
if (edition == NULL) {
|
|
continue;
|
|
}
|
|
/* Get the feature name. */
|
|
featureName = getFeatureName((feature_name)i, alarmFeatureName, sizeof(alarmFeatureName));
|
|
|
|
/* fill the alarm message */
|
|
WriteAlarmAdditionalInfo(&tempAdditionalParam,
|
|
"",
|
|
alarmFeatureName,
|
|
"",
|
|
&licenseControl.featureInfo[i].alarmPermissionDenied,
|
|
ALM_AT_Resume,
|
|
featureName,
|
|
edition);
|
|
/* report the alarm */
|
|
AlarmReporter(&licenseControl.featureInfo[i].alarmPermissionDenied, ALM_AT_Resume, &tempAdditionalParam);
|
|
|
|
ereport(LOG,
|
|
(errmsg("Report an resume alarm that the feature \"%s\" has been authorized to use on the %s now.",
|
|
featureName,
|
|
edition)));
|
|
|
|
/* Reset the reported time. */
|
|
licenseControl.featureInfo[i].lastReportTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief: Check whether the specified feature is disabled.
|
|
*
|
|
* @param [in] feature: The specified feature.
|
|
*
|
|
* @return true: The feature is disabled.
|
|
* @return false: The feature is enabled.
|
|
*/
|
|
bool is_feature_disabled(feature_name feature)
|
|
{
|
|
/* If the feature is disabled by the product control file. */
|
|
if (versionControl.featureInfo[feature].disabled_features_flag)
|
|
return true;
|
|
|
|
/* The liccense control does not support the GaussDB 300 product. */
|
|
if (versionControl.licenseVersion == PRODUCT_VERSION_GAUSSDB300) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* We will report an alarm in the following situations:
|
|
* 1. The feature is disabled by the license control file.
|
|
* 2. The license control file is not loaded.
|
|
* 3. The alarm module and the license alarm item must be loaded.
|
|
*/
|
|
if (isInitialized && (licenseControl.featureInfo[feature].disabled_features_flag || !licenseControl.isLoaded)) {
|
|
/* Report the permission alarm. */
|
|
reportPermissionDeniedAlarm(feature);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief: Get the feature control information from the file, and store these config into config data structure.
|
|
*
|
|
* @param [inout] control: The input feature control data structure.
|
|
*
|
|
* @return 0: Get the feature control information from the file successfully.
|
|
* @return -1: Failed to parse the config file or
|
|
*/
|
|
int loadConrtolData(LicenseControl* control)
|
|
{
|
|
gaussdb_version version;
|
|
unsigned short* list = NULL;
|
|
uint32 len;
|
|
int ret = 0;
|
|
uint32 i = 0;
|
|
|
|
do {
|
|
/* Initialize the feature information. */
|
|
for (i = 0; i < FEATURE_NAME_MAX_VALUE; i++) {
|
|
/* First, set the flage to false. */
|
|
control->featureInfo[i].disabled_features_flag = false;
|
|
|
|
/* Initialize one time. */
|
|
if (!isInitialized) {
|
|
/* Initialize the alarm item. */
|
|
AlarmItemInitialize(&control->featureInfo[i].alarmPermissionDenied,
|
|
ALM_AI_FeaturePermissionDenied,
|
|
ALM_AS_Normal,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
/* Parse the gaussdb version file. */
|
|
version.data = NULL;
|
|
version.header = NULL;
|
|
ret = parse_gaussdb_version_file(&version, control->productFileName);
|
|
if (ret != 0) {
|
|
/* Set the license control */
|
|
control->isLoaded = control->isLoaded ? true : false;
|
|
|
|
ereport(WARNING, (errmsg("failed to parse feature control file: %s.", control->productFileName)));
|
|
break;
|
|
}
|
|
|
|
/* Store the feature control information. */
|
|
control->licenseVersion = version.data->product_flag;
|
|
|
|
/* Set the features flag. */
|
|
list = version.data->disabled_feature_list;
|
|
len = version.data->disabled_feature_len;
|
|
for (i = 0; i < len; i++) {
|
|
/* If it is in the disabled list, set it to true. */
|
|
if (list[i] < MAX_FEATURE_NUM && list[i] < FEATURE_NAME_MAX_VALUE) {
|
|
control->featureInfo[list[i]].disabled_features_flag = true;
|
|
}
|
|
}
|
|
|
|
/* Set the license control */
|
|
control->isLoaded = true;
|
|
} while (0);
|
|
|
|
if (version.data != NULL)
|
|
pfree(version.data);
|
|
if (version.header != NULL)
|
|
pfree(version.header);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief: Parse "gaussdb.version" and the "gaussdb.license" file, this file is provided by OM script.
|
|
*
|
|
* @param [out] version_info: The pointer of the gaussdb_version data structure.
|
|
* @param [in] filename: The control file information data structure.
|
|
*
|
|
* @note: The caller must free elements of this structure when you need not to use this variable.
|
|
*
|
|
* @return 0: Success
|
|
* @return -1: Failure
|
|
*/
|
|
int parse_gaussdb_version_file(gaussdb_version* version_info, const char* filename)
|
|
{
|
|
struct stat file_info;
|
|
unsigned int crc32 = 0;
|
|
off_t file_size;
|
|
char buff[1024] = {0};
|
|
char sha256_digest[SHA256_DIGEST_STRING_LENGTH] = {0};
|
|
bool sha256_found_flag = false;
|
|
int sha256_null_count = 0;
|
|
unsigned int outlen = 0;
|
|
int rc;
|
|
int fd = -1;
|
|
unsigned char* file_content = NULL;
|
|
version_header* header = NULL;
|
|
version_data* data = NULL;
|
|
errno_t errorno = EOK;
|
|
errorno = memset_s(&file_info, sizeof(struct stat), 0, sizeof(struct stat));
|
|
securec_check(errorno, "\0", "\0");
|
|
|
|
/* get configuration file informations */
|
|
char filepath[MAXPGPATH] = {0};
|
|
char* gausshome = gs_getenv_r("GAUSSHOME");
|
|
/* first, we will get GAUSSHOME path */
|
|
if (gausshome != NULL) {
|
|
char real_gausshome[PATH_MAX + 1] = {'\0'};
|
|
if (realpath(gausshome, real_gausshome) == NULL) {
|
|
ereport(WARNING,
|
|
(errmodule(MOD_EXECUTOR), errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
|
errmsg("Failed to obtain environment value $GAUSSHOME!"),
|
|
errdetail("N/A"),
|
|
errcause("Incorrect environment value."),
|
|
erraction("Please refer to backend log for more details.")));
|
|
goto error;
|
|
}
|
|
gausshome = NULL;
|
|
check_backend_env(real_gausshome);
|
|
rc = sprintf_s(filepath, sizeof(filepath), "%s/bin/%s", real_gausshome, filename);
|
|
if (rc == -1) {
|
|
ereport(WARNING, (errmsg("failed to call secure function.")));
|
|
goto error;
|
|
}
|
|
} else {
|
|
ereport(WARNING, (errmsg("not found GAUSSHOME enviroment variable.")));
|
|
/* try to find this file in current working directory */
|
|
rc = sprintf_s(filepath, sizeof(filepath), "./%s", filename);
|
|
if (rc == -1) {
|
|
ereport(WARNING, (errmsg("failed to call secure function.")));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
fd = open(filepath, O_RDONLY);
|
|
if (fd < 0) {
|
|
ereport(WARNING,
|
|
(errmsg("failed to open feature control file, please check whether it exists: FileName=%s,"
|
|
" Errno=%d, Errmessage=%s.",
|
|
filename,
|
|
errno,
|
|
gs_strerror(errno))));
|
|
goto error;
|
|
}
|
|
|
|
if (fstat(fd, &file_info) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
/* get the content of configuration file */
|
|
file_size = file_info.st_size;
|
|
if (file_size <= 0) {
|
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("file_size is invalid:[%ld]", file_size)));
|
|
}
|
|
file_content = (unsigned char*)palloc(file_size);
|
|
if (file_content == NULL) {
|
|
ereport(WARNING, (errmsg("can not palloc memory.")));
|
|
goto error;
|
|
}
|
|
if (read(fd, file_content, file_size) != file_size) {
|
|
goto error;
|
|
}
|
|
|
|
/* check sha256 code */
|
|
if (sha256_encode(file_content, file_size, sha256_digest, sizeof(sha256_digest)) == -1) {
|
|
ereport(WARNING, (errmsg("can not get sha256 digest.")));
|
|
goto error;
|
|
}
|
|
|
|
for (int i = 0; i < SHA256_DIGESTS_COUNT; i++) {
|
|
if (sha256_digests[i] == NULL) {
|
|
sha256_null_count++;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(sha256_digests[i], sha256_digest) == 0) {
|
|
sha256_found_flag = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* this gaussdb.version file exists, but sha256 digests are not consistent,
|
|
* we will give a fatal report to user.
|
|
*/
|
|
if (strcmp(filename, PRODUCT_VERSION_FILE) == 0) {
|
|
if (!sha256_found_flag && sha256_null_count != SHA256_DIGESTS_COUNT) {
|
|
close(fd);
|
|
ereport(
|
|
FATAL, (errmsg("\"%s\" configuration file is not official, please check it.", PRODUCT_VERSION_FILE)));
|
|
}
|
|
|
|
ereport(LOG, (errmsg("sha256 from \"%s\" is %s.", PRODUCT_VERSION_FILE, sha256_digest)));
|
|
}
|
|
|
|
/* deserialization from configuration file */
|
|
header = (version_header*)palloc(sizeof(version_header));
|
|
if (sizeof(version_header) > (size_t)file_size
|
|
|| memcpy_s(header, sizeof(version_header), file_content, sizeof(version_header)) != 0) {
|
|
goto error;
|
|
}
|
|
|
|
/* check for CRC32 equality */
|
|
if (sizeof(version_header) + header->base64_data_len <= (size_t)file_size) {
|
|
COMP_CRC32(crc32, file_content + sizeof(version_header), header->base64_data_len);
|
|
}
|
|
if (header->crc32_code != crc32) {
|
|
ereport(WARNING, (errmsg("The CRC32 check result of file is inconsistent.")));
|
|
goto error;
|
|
}
|
|
|
|
rc = base64_decode(file_content + sizeof(version_header), header->base64_data_len, buff, &outlen);
|
|
if (rc < 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (outlen != header->data_len) {
|
|
ereport(WARNING,
|
|
(errmsg("failed to check data length, outlen is %u, data_len is %u."
|
|
" please check the configuration file.",
|
|
outlen,
|
|
header->data_len)));
|
|
goto error;
|
|
}
|
|
|
|
data = (version_data*)palloc(header->data_len);
|
|
if (memcpy_s(data, header->data_len, buff, header->data_len) != 0) {
|
|
goto error;
|
|
}
|
|
|
|
version_info->header = header;
|
|
version_info->data = data;
|
|
pfree(file_content);
|
|
close(fd);
|
|
return 0;
|
|
|
|
error:
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
}
|
|
if (file_content != NULL) {
|
|
pfree(file_content);
|
|
}
|
|
if (data != NULL) {
|
|
pfree(data);
|
|
}
|
|
if (header != NULL) {
|
|
pfree(header);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @brief: Receive the SIG_RELOAD_LICENSE signal, reload the license control information from config file.
|
|
*
|
|
* @param [in] sig: The signal id.
|
|
*/
|
|
void signalReloadLicenseHandler(int sig)
|
|
{
|
|
/* Only GaussDB200 support the license resume operation. */
|
|
if (get_product_version() != PRODUCT_VERSION_GAUSSDB200) {
|
|
return;
|
|
}
|
|
|
|
ereport(LOG, (errmsg("Reloading the license control file.")));
|
|
|
|
if (loadConrtolData(&licenseControl) != 0) {
|
|
ereport(WARNING,
|
|
(errmsg("Failed to reload the license control file, so the license information will not be changed.")));
|
|
return;
|
|
}
|
|
|
|
ereport(LOG, (errmsg("Reload the license control file successfully.")));
|
|
|
|
ereport(LOG, (errmsg("Resuming the alarm for the new enabled features.")));
|
|
/**
|
|
* We will resume an alarm in the following situation:
|
|
* 1. The feature is enabled by the license control file
|
|
* flag is set.
|
|
*/
|
|
reportResumeAlarm();
|
|
}
|
|
|
|
/**
|
|
* @brief: Initialize the feature flag array, so that we can know if one feature is enabled by calling
|
|
* is_feature_disabled().
|
|
*
|
|
* @return: void
|
|
*/
|
|
void initialize_feature_flags()
|
|
{
|
|
MemoryContext oldcontext = MemoryContextSwitchTo(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB));
|
|
/* Initialize the alarm module. */
|
|
AlarmEnvInitialize();
|
|
|
|
/* Load the product version control file. */
|
|
if (loadConrtolData(&versionControl) != 0) {
|
|
ereport(WARNING,
|
|
(errmsg("Failed to load the product control file, so gaussdb cannot distinguish product version.")));
|
|
return;
|
|
}
|
|
|
|
/* Only GaussDB200 support the license operation. */
|
|
if (get_product_version() != PRODUCT_VERSION_GAUSSDB200) {
|
|
return;
|
|
}
|
|
|
|
/* Load the license contro file. */
|
|
if (loadConrtolData(&licenseControl) != 0) {
|
|
ereport(WARNING,
|
|
(errmsg("Failed to load the license control file, so gaussdb cannot distinguish license version.")));
|
|
}
|
|
|
|
/**
|
|
* We will resume an alarm in the following situation:
|
|
* 1. The feature is enabled by the license control file
|
|
* flag is set.
|
|
*/
|
|
reportResumeAlarm();
|
|
|
|
/* Set the intialized flag. */
|
|
isInitialized = true;
|
|
(void)MemoryContextSwitchTo(oldcontext);
|
|
}
|
|
// end of file
|